# Schedule server tasks

> Run commands, power actions and snapshots on a timetable with schedules, their tasks and their run history.

Source: https://docs.coritan.com/managed-containers/schedules/

In the dashboard:

- /dashboard/servers/…/schedules: https://www.coritan.com/dashboard/servers

A schedule runs a list of tasks on a timetable: a console command, a power action or a snapshot. A nightly restart, for example, is a schedule that warns players with a command, waits a minute and then restarts the server. You set schedules up on the server's **Schedules** tab.

## Before you begin

- Free servers cannot use schedules. [Free servers](/managed-containers/free-servers/) lists what they leave out.
- A server holds up to 25 schedules, and a schedule holds up to 25 tasks.
- On a server someone shared with you, the owner must have given you the Schedules permission. To add a task, you also need the permission for what the task does: Command for a console command, Power for a power action, and Snapshots for a snapshot.

## Create a schedule

1. In the [dashboard](https://www.coritan.com/dashboard/servers), go to **Container Apps** and open the server, then the **Schedules** tab.
2. Select **New schedule…**.
3. Enter a **Name**, such as `Nightly restart`. It can be up to 120 characters.
4. Under **Cron expression**, fill in **Minute**, **Hour**, **Day**, **Month** and **Weekday**. The form starts at `0 4 * * *`, which runs at 04:00 every day. See [Write the timetable](#write-the-timetable).
5. Choose the **Time zone** to read the timetable in. It starts as your browser's time zone.
6. Choose the options:
   - **Active** runs the schedule on its timetable. Clear it to create the schedule paused.
   - **Only while the server is online** skips a run when the server is not running.
   - **Catch up a missed run** runs a missed run once, as soon as possible, when our systems were down at the time it was due. Clear it to skip the missed run and wait for the next one.
7. Select **Create schedule**.

The new schedule has no tasks yet, so it does nothing when it runs. Add its tasks next.

## Write the timetable

The five fields are standard cron fields, read in the schedule's time zone:

| Field | Values | Example |
| --- | --- | --- |
| **Minute** | `0-59` | `30` runs at half past the hour |
| **Hour** | `0-23` | `*/6` runs every six hours |
| **Day** | `1-31` | `1` runs on the first of the month |
| **Month** | `1-12` | `6-8` runs in June, July and August |
| **Weekday** | `0-6`, where `0` is Sunday | `1-5` runs Monday to Friday |

Use `*` for any value, a comma for a list (`0,30`), a hyphen for a range (`1-5`) and a slash for a step (`*/15`). Some common timetables:

| Timetable | Minute | Hour | Day | Month | Weekday |
| --- | --- | --- | --- | --- | --- |
| Every day at 04:00 | `0` | `4` | `*` | `*` | `*` |
| Every 6 hours | `0` | `*/6` | `*` | `*` | `*` |
| Every 15 minutes | `*/15` | `*` | `*` | `*` | `*` |
| Mondays at 03:30 | `30` | `3` | `*` | `*` | `1` |

Daylight saving follows the time zone. A time that the clocks skip in spring runs once, an hour late, and a time that the clocks repeat in autumn runs once.

## Add tasks

1. Select the schedule's name to open it. It opens on **Tasks**.
2. Select **Add task**.
3. Choose the **Action** and fill in what it needs:
   - **Run a console command**: the **Command** to send, without a leading `/`, such as `say Restarting in 60 seconds`. It can be up to 1,000 characters.
   - **Send a power signal**: the **Signal**, which is **Start**, **Stop**, **Restart** or **Kill**.
   - **Take a snapshot**: nothing else. The snapshot is named `scheduled-` followed by a code for the run and the task.
4. Set the **Delay**: how many seconds to wait after the previous task finishes before this one runs. Leave it at `0` to run at once. A delay can be up to 24 hours (86,400 seconds).
5. Select **Continue if this task fails** if the tasks after it should still run when it fails. Without it, a failed task ends the run.
6. Select **Add task**.

Tasks run from the top down. Use **Move up** and **Move down** beside a task to change the order, and **Remove task** to remove it.

A console command reaches the server only while it is running, so select **Only while the server is online** for a schedule that starts with a command.

A snapshot task uses your account's [snapshot storage](/snapshots/storage-allowance/). When the storage is full, the schedule deletes the oldest unlocked snapshots it took of this server until the new one fits. It never deletes a snapshot that you took by hand, that another schedule took or that we took. If deleting its own snapshots does not make enough room, the task fails.

## Run a schedule now

Select **Run now** beside the schedule. It runs once straight away, and the timetable carries on as before. **Run now** is greyed out while the schedule is running.

## Pause, change or delete a schedule

- To stop a schedule for a while, open the menu beside **Run now** and select **Pause**. Select **Resume** to start it again. A resumed schedule waits for its next time on the timetable, so resuming a nightly restart does not restart the server there and then.
- To change the name, timetable, time zone or options, open the schedule, select **Settings**, then **Edit schedule**. Make your changes and select **Save changes**.
- To delete a schedule, open the menu beside **Run now** and select **Delete schedule…**, then **Delete schedule**. Its tasks and run history are deleted with it, and a run in progress stops.

## Check what a schedule did

Open the schedule and select **Run history**. It lists the last 20 runs, newest first, with each run's status, when it started and whether the timetable (`Cron`) or someone (`Manual`) started it. Select **Show steps** to see what each task did and any error.

| Status | Meaning |
| --- | --- |
| `Queued` | The run is waiting to start. |
| `Running` | The tasks are running. |
| `Completed` | Every task ran. |
| `Failed` | A task failed and the run stopped. The error shows under the run. |
| `Skipped` | The run did not start, for the reason shown under it. |
| `Reaped` | The run stopped reporting for an hour, so we ended it. |

The schedule's **Settings** view also shows the **Timetable**, the **Next run**, the **Last run** and its **Options**.

## Result

A message confirms each change, such as `Schedule created.`, `Task added.` or `Run queued.` The schedule's badge shows **Active**, **Paused** or **Running**, and the next run time appears beside its timetable.

## Troubleshooting

`Schedules are not included on the free plan. …`
: The server is a free server. To use schedules, [order a paid server](/managed-containers/order-a-server/) and [restore a snapshot](/snapshots/restore-a-snapshot/) of this one onto it.

**Last run failed**
: A task failed. Open the schedule and select **Run history**, then **Show steps** to read the error. The same error shows under **The last run failed** in **Settings**.

`Skipped: server was not running …`
: **Only while the server is online** is selected and the server was stopped when the run was due. `Skipped: could not confirm the server was online` means we could not check, so we skipped the run to be safe.

`Skipped: server cannot run schedules right now`
: The server was installing, being moved or suspended when the run was due, so the run did not start. A run shows `Skipped: server is suspended`, or another status, for the same reason. The schedule runs again at its next time.

`Skipped: due time passed while the panel was down`
: The run was due while our systems were down, and **Catch up a missed run** is not selected.

`A snapshot or restore was already in progress; this run did not take one.`
: Another snapshot or restore was running on the server, so the snapshot task was skipped.

`This schedule was changed in another tab. Reload and try again.`
: Someone saved the schedule after you opened it. Reload the page and make your change again.

`This schedule is already running.`
: Wait until the current run finishes, then select **Run now** again.

`Backups have been replaced by snapshots. …`
: The **Take a backup** action can no longer be added. Use **Take a snapshot** instead. Backup tasks added before snapshots replaced backups keep running.

`This server already has the maximum of 25 schedules. Delete one before creating another.`
: Delete a schedule you no longer need, or add its tasks to another schedule.

## Related

- [Take a server snapshot](/managed-containers/snapshots/)
- [Use the server console](/managed-containers/console/)
- [Share a server with other users](/managed-containers/users/) explains the Schedules permission.

## With the API

`GET /api/v1/client/servers/{uuid}/schedules` lists the server's schedules, each with its `tasks`. `POST` to the same path creates one. The body takes `name`, the five cron fields `cron_minute`, `cron_hour`, `cron_day_of_month`, `cron_month` and `cron_day_of_week`, `timezone` (an IANA name, default `UTC`), `is_active` (default `true`), `only_when_online` (default `false`), `catch_up` (default `true`) and an optional `tasks` list, so you can create a schedule and its tasks in one request:

```bash
curl -X POST https://api.coritan.com/api/v1/client/servers/$SERVER/schedules \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly restart",
    "cron_minute": "0", "cron_hour": "4", "cron_day_of_month": "*",
    "cron_month": "*", "cron_day_of_week": "*",
    "timezone": "Europe/London",
    "only_when_online": true,
    "tasks": [
      {"action": "command", "payload": {"command": "say Restarting in 60 seconds"}},
      {"action": "power", "payload": {"signal": "restart"}, "time_offset": 60}
    ]
  }'
```

Each task has an `action` and a `payload`:

| `action` | `payload` |
| --- | --- |
| `command` | `{"command": "…"}`, up to 1,000 characters |
| `power` | `{"signal": "start"}`, `stop`, `restart` or `kill` |
| `snapshot` | `{}`, or `{"ignored_files": ["logs/*"]}` to leave files out |

`time_offset` is the delay in seconds after the previous task, from `0` to `86400`, and `continue_on_failure` defaults to `false`. The response echoes a power task's signal as `{"action": "restart"}`.

| Request | What it does |
| --- | --- |
| `GET /schedules/{schedule_uuid}` | Returns one schedule. |
| `PUT /schedules/{schedule_uuid}` | Changes any schedule field. Send the `revision` you read to be told with a `409` when someone else saved first. |
| `DELETE /schedules/{schedule_uuid}` | Deletes the schedule, its tasks and its history. |
| `POST /schedules/{schedule_uuid}/tasks` | Adds a task at the end, or at `sequence_id`. |
| `PUT /schedules/{schedule_uuid}/tasks/{task_id}` | Changes a task. Changing `action` needs a new `payload`. |
| `DELETE /schedules/{schedule_uuid}/tasks/{task_id}` | Removes a task. |
| `POST /schedules/{schedule_uuid}/tasks/reorder` | Sets the order. `task_ids` must list every task once. |
| `POST /schedules/{schedule_uuid}/execute` | Queues a run now and returns its `run_id`. |
| `GET /schedules/{schedule_uuid}/runs` | Lists recent runs with their `steps`. `limit` is 1 to 100, default 20. |

The paths are relative to `/api/v1/client/servers/{uuid}`. A schedule's response also carries `next_run_at`, `last_run_at`, `last_run_status`, `last_failure_message`, `is_processing` and `revision`.

`GET /api/v1/client/timezones` lists the time zones a schedule accepts, as `zones` with each zone's `name` and current `offset`, and `aliases` that map old zone names to current ones. An unknown zone answers `400` with `Unknown timezone`, and a cron that is not five valid fields answers `400` too.

On a server shared with you, listing needs `schedule.read`, creating needs `schedule.create`, deleting needs `schedule.delete`, and every other change needs `schedule.update`. A task also needs the permission its action uses: `control.console` for a command, `control.start`, `control.stop`, `control.restart` or `control.kill` for a power signal, and `snapshot.create` for a snapshot.

## API

- `GET /api/v1/client/servers/{uuid}/schedules`: List schedules for a server (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-get-api-v1-client-servers-uuid-schedules)
- `POST /api/v1/client/servers/{uuid}/schedules`: Create a new schedule, optionally with its whole task pipeline (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-post-api-v1-client-servers-uuid-schedules)
- `GET /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}`: Get a schedule (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-get-api-v1-client-servers-uuid-schedules-schedule-uuid)
- `PUT /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}`: Update a schedule (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-put-api-v1-client-servers-uuid-schedules-schedule-uuid)
- `DELETE /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}`: Delete a schedule (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-delete-api-v1-client-servers-uuid-schedules-schedule-uuid)
- `POST /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/execute`: Queue a schedule to run now, without changing its next scheduled run (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-post-api-v1-client-servers-uuid-schedules-schedule-uuid-execute)
- `GET /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/runs`: Recent runs of a schedule, newest first, with the outcome of every step (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-get-api-v1-client-servers-uuid-schedules-schedule-uuid-runs)
- `POST /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/tasks`: Append a task to a schedule's pipeline (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-post-api-v1-client-servers-uuid-schedules-schedule-uuid-tasks)
- `POST /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/tasks/reorder`: Set the order tasks run in (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-post-api-v1-client-servers-uuid-schedules-schedule-uuid-tasks-reorder)
- `PUT /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/tasks/{task_id}`: Edit a task in place (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-put-api-v1-client-servers-uuid-schedules-schedule-uuid-tasks-task-id)
- `DELETE /api/v1/client/servers/{uuid}/schedules/{schedule_uuid}/tasks/{task_id}`: Remove a task from a schedule (https://docs.coritan.com/api/reference/client/container-apps/servers-schedules/#op-delete-api-v1-client-servers-uuid-schedules-schedule-uuid-tasks-task-id)
- `GET /api/v1/client/timezones`: Every zone this platform can resolve, west to east, with its old names (https://docs.coritan.com/api/reference/client/container-apps/timezones/#op-get-api-v1-client-timezones)
