Scheduler¶
Jobs that fire at a chosen date and time, once or on a repeating schedule: open a challenge at the start of a round, post a reminder before the freeze, close everything when the event ends.
Requires the worker
Jobs are executed by the background worker, a separate process from the API inside
the app container. If it is not running, jobs simply stay pending and nothing
fires. See Deployment.
The job list¶

| Column | Meaning |
|---|---|
| ID | Identifier of the job, with a copy button. |
| Name | Label given when the job was created. |
| Type | What the job does, see Job types. |
| Next run | When the job is due, in your local timezone, with the cron expression underneath for repeating jobs. |
| Status | scheduled for a job still waiting, completed once a one-shot job has run, disabled for a job that is turned off. |
| Last run | When the job last ran, on schedule or through Run now. |
Creating a job¶

| Field | Notes |
|---|---|
| Name | Free-form label, only used in the admin panel. |
| Job type | What the job does. The Parameters section changes with the type. |
| Scheduled at | Date and time the job is due, entered in your local timezone. With a cron expression it becomes First run (optional): leave it empty and the first cron occurrence is used. |
| Repeat (cron) | Optional cron expression. Empty means the job fires once. See Repeating jobs. |
| Active | On by default. A job that is not active never fires. |
| Parameters | Type-specific, see below. |
A job needs either a date or a cron expression, and can have both.
Jobs fire on the second
Once a minute the worker queues every job due within the next minute, each one
held back until its exact time. A job scheduled for 18:00:30 starts at
18:00:30, never before. The exception is a job created or moved less than a
minute before it is due: it waits for the next pass, so it can start up to a minute
late.
Repeating jobs¶
A cron expression turns a one-shot job into a repeating one. Standard 5-field syntax is
accepted, and 6 fields when you need seconds, with the seconds field first. A job fires
at most once a minute, so that seconds field must name a single value: 30 0 * * * *
is accepted, */10 * * * * * is refused.
| Expression | Fires |
|---|---|
0 0 * * * |
Every day at midnight. |
*/5 * * * * |
Every five minutes. |
0 9 * * 1 |
Every Monday at 09:00. |
The field previews the next fire times as you type, and refuses to save an expression it cannot parse or that fires more than once a minute.
Which timezone applies
Cron expressions are read in the event timezone, set by ctf.timezone in
Competition settings, so "every day at midnight" stays at
midnight across a daylight-saving shift. Times are always displayed in the local
timezone of whoever is looking at the page.
As soon as a run is queued the job is rescheduled to its next occurrence and stays
scheduled. A repeating job runs until you disable or delete it; if its expression
somehow becomes unparsable, the job is deactivated instead of firing at the wrong time.
Job types¶
toggle_challenge¶
Turns a challenge on or off.

| Parameter | Notes |
|---|---|
| Challenge Id | The challenge to change. |
| Make Active | On makes the challenge visible to players, off hides it. |
Two jobs on the same challenge, one with Make Active on and one off, give you a release window.
send_notification¶
Sends the same message the Notifications page sends, at a chosen time.

| Parameter | Notes |
|---|---|
| Title | Headline shown to players. |
| Content | Body of the message. |
| Is Broadcast | Send to every user instead of specific teams. |
| Team Ids | Recipients when Is Broadcast is off. |
backup_database¶
Dumps the database to object storage, the same dump Admin → Backups takes by hand. This is how you get nightly backups without a cron job on the host.
| Parameter | Notes |
|---|---|
| Keep Last | How many dumps to keep. Older ones are deleted after each run. Between 1 and 100, defaults to 7. |
Pair it with a cron expression: 0 3 * * * with Keep Last at 7 gives you a
week of nightly dumps on a prefix that never grows. See
Backups for what a dump contains, and what it does not.
Pruning happens after the dump, not before
Keep Last counts scheduled backups and the ones you took by hand alike. Set it high enough that a manual backup taken before an upgrade is not swept away by that night's run. Pre-restore backups are the exception: they are never counted and never pruned.
More job types can be added by a plugin.
Job details¶
Opening a job shows what it will do and what it has done.

Next run and Repeat (cron) show when the job fires next and, for a repeating job, the expression driving it. Edit changes the name, schedule, cron and parameters, Disable stops a job from firing, and Delete removes it along with its history.
Run now queues a run immediately, without touching its schedule: the job still fires on its own at its next run time afterwards, and Last run moves to now. Useful for checking that the parameters are right. If a run of the job is already queued or running, nothing is queued and the button says so.
Execution history¶
Every run is recorded with its status and its start and completion time. Details links to the run in the task queue, which holds its log and, for a failed run, the traceback.
| Status | Meaning |
|---|---|
pending |
Queued, or running. |
success |
The job ran to completion. |
failed |
The job raised an error, or the run never reported back: the worker was stopped mid-run, or the run hit its one hour limit. |
skipped |
The job came due while a run of it was still queued or running. Runs of one job never overlap, so this occurrence is dropped. |
cancelled |
The run was cancelled or deleted from the task queue before it finished. |
A failed run is not retried. A one-shot job is retired either way, and a repeating job moves on to its next occurrence, so check the history after an important job was due. Only the last 100 runs of a job are kept.
The task queue¶
Admin → Task queue opens the worker's own dashboard, live: every queued, running and finished background job, scheduled jobs and plugin tasks alike, with its log. It is the place to look when a run failed, or when nothing seems to fire at all.
Failed jobs and the queue's history are kept for 30 days, then purged every night, so the Details link of an older run can come up empty.