Registration
Provisioning a worker credential and attaching it to backups.
View as MarkdownProvisioning creates two things: a worker ID that backups point at, and a key that a process uses to claim work. The ID is permanent and public. The key is shown once.
Nothing about this step touches a machine. You can provision a worker months before you install anything, and the same worker survives the host being rebuilt.
Provision
sctl worker provision prod-worker-1Provisioned worker "prod-worker-1" (0f2c9a1e-...).
Key (copy now, not shown again):
sk_live_...The dashboard does the same thing under Workers → New worker, and generates a
config.yaml ready to paste.
The key is shown once and is not recoverable. It is what lets a machine claim work in your workspace, so treat it like a database password. If you lose it, or it leaks, rotate rather than delete.
Names must be 1 to 100 characters. They are for you, not for us: the ID is what everything else refers to.
sctl worker listNAME ID CREATED UPDATED
prod-worker-1 0f2c9a1e-... 2026-08-08T09:14:02Z 2026-08-08T09:14:02ZWhat the key authorizes
The worker key is an org-scoped machine credential, and it carries exactly two permissions:
| Permission | What it allows |
|---|---|
artifacts:upload | Ask for a presigned upload URL for one of its own runs |
artifacts:confirm | Report the checksum, size and filename of what it uploaded |
That is the whole surface. A worker key cannot read your backups, list your artifacts, download anything, create or delete definitions, read the audit log, touch billing, or see another workspace. It also cannot manage workers, including itself.
On the orchestration side the same key is bound to one namespace, your workspace, and to one task queue, its own worker ID. It may poll that queue and report results. It cannot start work, administer anything, or see another worker's queue, let alone another workspace's.
This is why the worker is safe to run on a machine you would not trust with an API key. The worst a stolen worker key can do is upload garbage to that worker's own runs and read whatever the config file already gave it. That second half is the real risk, so protect the file, not just the key.
Assign backups to a worker
A local backup must name a worker. That is what decides which machine the run lands on,
and it is not optional, because no other machine has the credentials.
workers:
- name: prod-worker-1
backups:
- name: prod-db
kind: local
source_type: postgres
worker: prod-worker-1
schedule: "0 2 * * *"sctl apply -f saved.yamlThe workers: block declares that the name should exist. apply will refuse a backup whose
worker: is neither declared in the file nor already provisioned in the workspace, rather
than creating a schedule that fires into nothing.
Two rules follow from routing being per-worker:
- Runs are never redistributed. If the assigned worker is offline, its runs queue until it comes back. Another worker will not pick them up, because it does not have the credentials.
- Changing a backup's worker recreates its schedule. Expect the change to take effect from the next fire, not retroactively.
The backup ID also has to appear in that worker's own config.yaml, under backups:, or the
run fails with ConfigDrift. See Configuration.
Presence
Worker presence is read live from the polling connections, not from a heartbeat we store.
sctl worker listThe dashboard shows an instance count and a last seen time per worker. Read them exactly as written:
- Connected is trustworthy. If it reports two instances, two processes are polling right now.
- Disconnected lags. Poller records expire on a TTL, so a worker that died thirty seconds ago can still look present for a few minutes. Nothing here is a definite red light.
An instance count above one is worth investigating. It usually means a second copy of the same config is running somewhere, which is a real problem.
Rotation versus deletion
These are not variations on the same action.
sctl worker rotate | sctl worker delete | |
|---|---|---|
| Worker ID | Unchanged | Gone |
| Old key | Revoked immediately | Revoked immediately |
| New key | Printed once | There is none |
| Backups assigned to it | Keep working once the new key is deployed | Have nowhere to run |
| Running processes | Disconnect at the next poll and must be restarted | Disconnect permanently |
sctl worker rotate 0f2c9a1e-...Rotation is the answer to almost everything: a leaked key, a decommissioned host, an employee who had access to the config file, or an ordinary hygiene schedule. Deletion is for a worker you are genuinely finished with.
Rotation revokes the old key the moment the new one is issued. Any process still holding the
old key stops being able to poll. Plan for a short gap: rotate, write the new key into
config.yaml, restart the worker. A run that was already in flight will be retried once the
worker is back, because the run belongs to the queue, not to the connection.
Limits
| Plan | Workers per workspace |
|---|---|
| Trial | 1 |
| Paid | 10 |
Provisioning past the limit fails with a quota error rather than silently succeeding. See Limits.