# Scheduled jobs
URL: https://synquic.tech/docs/guides/jobs.md

Run commands on a schedule (cron), once, or on demand, on your servers or inside one of your app's containers, and tune the built-in maintenance jobs Synq runs for you.

import { Step, Steps } from 'fumadocs-ui/components/steps';

Jobs are tasks that run on a schedule on your install: the maintenance Synq does for you, and commands you want to
run yourself, such as a nightly database dump, a cache warm-up or a cleanup script. Open **Jobs** in the sidebar
(under **Infrastructure**).

The page has three sections:

- **System jobs**: built-in maintenance on this instance.
- **Custom jobs**: commands you run on your servers or in your apps' containers.
- **Backup schedules**: the automatic backups of your projects and services, listed read-only. They are managed on
  each project's **Backups** tab (**Manage in Backups**).

**At a glance** counts all jobs and how many are running, failed, scheduled or disabled. Every run is recorded with
its trigger (schedule or manual), result, duration and output.

## Built-in jobs

Synq keeps these running for you. Each row shows its **Schedule**, **Next run** and **Last run**, with **Run now**,
**Edit schedule**, **Enable** / **Disable** and **Logs**. Changing a built-in job needs an instance administrator.
You can't delete a built-in job; disable it instead.

| Job | What it does | Default schedule |
| --- | --- | --- |
| SSL certificate renewal | Renews Let's Encrypt certificates before they expire | daily at 03:17 |
| Orphaned resource cleanup | Reclaims leftovers from a failed teardown | hourly |
| Deployment artifact cleanup | Removes old release images beyond each project's retention | daily at 04:23 |
| Docker build-cache garbage collection | Keeps the build cache on each Docker host within about 10 GB | daily at 04:37 |
| **Docker cleanup** | Removes unused Docker images, containers, build cache and (if chosen) volumes. **Off by default**; set it up on a server's [Docker tab](/docs/guides/docker-cleanup#schedule-the-cleanup) | Sundays at 03:41 |
| **Panel database backup** | Backs up the control-plane database to a backup destination. **Off by default**; set it up in [Settings, Instance](/docs/guides/panel-backups) | daily at 02:11 |
| Backup retention prune | Deletes backups past their retention | hourly |
| Audit log prune | Deletes audit entries older than the workspace's retention | daily at 04:07 |
| Deployment reconcile | Settles deploys interrupted by a connection blip | every 10 minutes |
| Domain DNS verification | Re-checks domains still waiting for DNS | every 13 minutes |
| Update scan, Native-module update scan, Managed-container update scan | Look for new versions of Synq's parts and of the edge and mail containers | every 6 hours |
| Resource usage sampling, Traffic analytics collection, Container health watch | Collect the numbers behind monitoring | from every minute to twice an hour |
| One-time job dispatcher | Starts custom jobs scheduled to run once | every minute |
| Job run history prune | Deletes old job runs | daily at 04:23 |

There are a few more housekeeping jobs (prunes of analytics, usage samples, incidents, webhook events and pending
grants). The exact list depends on your install; the page shows all of them.

<Callout title="Turn Docker cleanup and panel backups on where they are configured" type="info">
Both need settings the Jobs page doesn't have (what to remove and on which servers; where to store the backup).
Turn them on from their own screens, then use **Jobs** to watch their runs. The Docker cleanup card on a server's
**Docker** tab has an **Open in Jobs** link that leads here; for panel backups, find the **Panel database backup**
row on this page.
</Callout>

## Create a custom job

<Steps>

<Step>

### Start a new job

In **Jobs**, press **New job** (or **Create job** when you have none).

</Step>

<Step>

### Basics

Give it a **Name** (for example "Nightly DB vacuum") and the **Command** to run. Then choose **Run inside**:

- **Host**: the command runs over SSH on the **Target servers** you tick. It can also start a throwaway container,
  for example `docker run --rm postgres:16 pg_dump ...`.
- **A project's service**: the command runs inside that service's running container. See
  [below](#run-a-job-inside-an-apps-container).

</Step>

<Step>

### Schedule

Pick **When to run**:

| Choice | Meaning |
| --- | --- |
| **On a schedule** | A 5-field cron in **Schedule (cron)**. Synq checks it when you save and shows the next run. |
| **Once** | At the date and time in **Run at**. |
| **Manual** | Only when triggered: **Run now**, a job it depends on, or an event. |

</Step>

<Step>

### Reliability, environment and triggers (optional)

| Section | Fields |
| --- | --- |
| **Reliability** | **Timeout (s)** stops the command after that long (up to 24 hours). **Max attempts** (1 to 10) and **Backoff (s)** retry a failed run; all attempts land in one run. |
| **Environment** | **Environment variables** (**Add variable**, **Paste .env** or **Upload .env**) and **Secrets**, which are encrypted and only decrypted when the job runs. Both are exported before your command. |
| **Dependencies & triggers** | **Run after**: start when the chosen jobs succeed. **Event triggers**: start on **Deploy succeeded**, **Deploy failed**, **Backup succeeded**, **Backup failed**, **Restore completed**, **SSL renewal failed** or **Domain verification failed**. |
| **Notifications** | Which channels hear about **running**, **success** or **failed**, overriding your global notification settings for this job. |

</Step>

<Step>

### Create it

Check the **Summary** and press **Create job**. Open the job to see its **Overview** and **Runs**, press **Run now**,
**Edit** or **Delete** it. **Logs** streams a run's output live while it runs.

</Step>

</Steps>

## Run a job inside an app's container

Choose **Run inside**, **A project's service**, then the **Project** and the **Service**. The command runs with
`docker exec` in that service's **running** container, on the server the project is deployed to. Use it for tasks
that need the app's own code, tools and environment:

```bash
# A Laravel scheduler tick
php artisan schedule:run

# A Django management command
python manage.py clearsessions

# A Node script shipped with the app
node scripts/send-digest.js
```

Things to know:

- The server is taken from the project; you don't pick one. A container job always targets exactly one server.
- The service must be running when the job fires, or the run fails.
- The output appears when the command finishes (a host job streams it line by line).
- Without a **Timeout**, a container job is stopped after 1 hour.
- Saving, running or deleting it needs write access to that project, on top of the server access every job needs.
- To move a job back to the host, edit it and choose **Host**.

## Schedules (cron), in plain terms

A schedule is a standard 5-field cron string, in the Synq server's time zone (UTC in the standard install):

```text
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sunday is 0)
* * * * *
```

| Cron | When |
| --- | --- |
| `0 3 * * *` | every day at 03:00 |
| `*/15 * * * *` | every 15 minutes |
| `0 * * * *` | every hour, on the hour |
| `0 2 * * 0` | every Sunday at 02:00 |
| `30 4 1 * *` | 04:30 on the 1st of each month |

<Callout title="Run now is recorded too">
A run started with **Run now** is saved in the history like a scheduled one, marked as manual, so you always have a
full record.
</Callout>

## Who can manage jobs

- **Built-in jobs**: instance administrators change their schedule, turn them on or off and run them.
- **Custom jobs**: you need admin access to every target server (owners, admins and members have it; restricted
  members need a server grant with **admin**). A job that runs inside a container also needs write access to its
  project. People who can't reach a job's servers don't see it.

## CLI

Use `synq job` (alias `jobs`) with your self-hosted connection context:

```bash
synq job list
synq job create --name weekly-sync --server <server-id> \
  --command 'cd /srv/app && npm run db:sync' --cron '17 4 * * 1'
synq job get <key>
synq job update <key> --no-enabled
synq job update <key> --enabled --cron '0 3 * * *'
synq job run <key> --follow
synq job runs <key> --limit 10
synq job logs <run-id> --follow
synq job delete <key> --yes
```

Jobs also work with `synq --native-config <file> job ...` through the native SDK.

Use the job **key** from `list` or `create`, not its database ID. For a one-time job, pass
`--schedule once --at <ISO-timestamp>`. For manual execution only, pass `--schedule manual`. Recurring jobs use
`--cron`.

Both `create` and `update` accept `--file <path>` containing a JSON Jobs API body. Use it for `env`, `secrets`,
`serverIds`, `retry`, `timeoutMs`, `dependsOn`, `triggerEvents`, `notifyConfig`, or a container target, or to supply
the entire job configuration. Explicit flags override file values. Updates send only supplied fields. Keep files
containing secrets private. The API encrypts secrets at rest.

A job that runs inside a container sets both `projectId` and `serviceId` (the server is derived from the project):

```json
{
  "label": "Laravel scheduler",
  "command": "php artisan schedule:run",
  "scheduleType": "recurring",
  "cronExpression": "* * * * *",
  "projectId": "proj_123",
  "serviceId": "svc_456"
}
```

```bash
synq job create --file scheduler.json
```

Sending `"projectId": null, "serviceId": null` in an update moves the job back to the host.

`--json` returns API data without the envelope. Followed runs emit JSON events. Without `--follow`, `run` returns a
custom run ID immediately. With `--follow`, the CLI waits for the outcome and exits nonzero on failure or an
interrupted stream. Built-in jobs return their summary directly. `logs` without `--follow` shows stored output, or
the full run record with `--json`.

## Related guides

<Cards>
  <Card title="Docker disk usage and cleanup" href="/docs/guides/docker-cleanup" description="Set up the Docker cleanup job." />
  <Card title="Back up and restore the panel" href="/docs/guides/panel-backups" description="Set up the panel database backup job." />
  <Card title="Backups and restores" href="/docs/guides/backups-restore" description="The project backup schedules listed on the Jobs page." />
</Cards>
