Use when implementing background processing, scheduling recurring tasks, or configuring cron. Prevents using AJAX cron in production, missing setInterval in TimedJob constructor, and unhandled job exceptions. Covers QueuedJob for one-time tasks, TimedJob for recurring tasks, IJobList for programmatic management, scheduleAfter for delayed execution, cron configuration modes, time sensitivity, and parallel run control. Keywords: QueuedJob, TimedJob, IJobList, setInterval, scheduleAfter, cron, background job, webcron, scheduled task, run in background, recurring job, cron job, async processing..
Use when implementing background processing, scheduling recurring tasks, or configuring cron. Prevents using AJAX cron in production, missing setInterval in TimedJob constructor, and unhandled job exceptions. Covers QueuedJob for one-time tasks, TimedJob for recurring tasks, IJobList for programmatic management, scheduleAfter for delayed execution, cron configuration modes, time sensitivity, and parallel run control. Keywords: QueuedJob, TimedJob, IJobList, setInterval, scheduleAfter, cron, background job, webcron, scheduled task, run in background, recurring job, cron job, async processing..
license
MIT
compatibility
Designed for Claude Code. Requires Nextcloud 28+.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
nextcloud-impl-background-jobs
Quick Reference
Job Types
Class
Namespace
Execution
Use Case
TimedJob
OCP\BackgroundJob\TimedJob
Recurring at interval
Periodic cleanup, sync, notifications
QueuedJob
OCP\BackgroundJob\QueuedJob
Once, then auto-removed
Process upload, send email, one-time migration
Key Methods
Method
Available On
Description
setInterval(int $seconds)
TimedJob
Minimum seconds between runs
setTimeSensitivity(int $sensitivity)
Both
IJob::TIME_INSENSITIVE for heavy jobs
setAllowParallelRuns(bool $allow)
Both (NC 27+)
Prevent concurrent execution
run(mixed $arguments)
Both
Override this -- your job logic goes here
IJobList API (Programmatic Management)
Method
Description
add(string $class, mixed $argument = null)
Register a job (idempotent for TimedJob)
remove(string $class, mixed $argument = null)
Remove a registered job
has(string $class, mixed $argument)
Check if job is registered
scheduleAfter(string $class, mixed $argument, int $timestamp)
Schedule job after UNIX timestamp
getById(int $id)
Get specific job by ID
Registration Methods
Method
Location
When to Use
info.xml<background-jobs>
appinfo/info.xml
Static recurring jobs (TimedJob)
IJobList::add()
PHP code
Dynamic jobs, QueuedJob with arguments
IJobList::scheduleAfter()
PHP code
Delayed execution after specific time
Cron Mode Comparison
Mode
Trigger
Interval
Production Use
System cron
OS crontab
Every 5 min (recommended)
YES -- ALWAYS use this
Webcron
External HTTP call
Depends on caller
Acceptable if system cron unavailable
AJAX
Page load by user
Unreliable, on-demand
NEVER use in production
Critical Warnings
NEVER use AJAX cron mode in production -- it depends on user page loads and will miss jobs when no users are active. System cron is the ONLY reliable option.
NEVER assume setInterval() provides exact timing -- it sets the MINIMUM time between runs. The actual interval depends on cron frequency and job queue length.
NEVER forget to inject ITimeFactory in the TimedJob constructor -- the parent class requires it. Omitting it causes a fatal error.
NEVER perform long-running operations without setTimeSensitivity(IJob::TIME_INSENSITIVE) -- time-sensitive jobs block the queue and delay other jobs.
ALWAYS call parent::__construct($time) in TimedJob constructors -- skipping this breaks interval tracking.
ALWAYS call setInterval() in the TimedJob constructor -- without it, the default interval is 0 and the job runs on every cron cycle.
ALWAYS use setAllowParallelRuns(false) for jobs that modify shared state -- concurrent runs cause race conditions and data corruption.
ALWAYS register TimedJob classes in info.xml -- they are automatically managed by the app lifecycle (added on enable, removed on disable).
ALWAYS use IJobList::add() for QueuedJob -- queued jobs require arguments and cannot be statically registered for meaningful use.
Decision Tree: Which Job Type?
Need background processing?
├── Runs repeatedly on a schedule?
│ └── YES → TimedJob
│ ├── Heavy/slow work? → setTimeSensitivity(TIME_INSENSITIVE)
│ ├── Must not overlap? → setAllowParallelRuns(false)
│ └── Register in info.xml <background-jobs>
├── Runs once then done?
│ └── YES → QueuedJob
│ ├── Run immediately (next cron)? → IJobList::add()
│ └── Run after specific time? → IJobList::scheduleAfter()
└── Needs to run at exact time?
└── Nextcloud background jobs do NOT guarantee exact timing.
Use setInterval() for minimum delay, system cron for best precision.
Essential Patterns
Pattern 1: TimedJob (Recurring)
<?phpnamespaceOCA\MyApp\BackgroundJob;
useOCA\MyApp\Service\CleanupService;
useOCP\AppFramework\Utility\ITimeFactory;
useOCP\BackgroundJob\IJob;
useOCP\BackgroundJob\TimedJob;
classCleanupTaskextendsTimedJob{
publicfunction__construct(
ITimeFactory $time,
private CleanupService $service,
) {
parent::__construct($time);
// Run at most once per hour (3600 seconds)$this->setInterval(3600);
// Mark as non-urgent so it does not block time-sensitive jobs$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
}
protectedfunctionrun(mixed$arguments): void{
$this->service->deleteExpiredItems();
}
}