Skip to content
Manifest
Markdown

Jobs

Your own commands, run on rollout steps, on a schedule or on demand.

jobs:
  migrate:  { service: web, command: npm run migrate, on: deploy }
  seed:     { service: web, command: npm run seed, on: seed }
  warm:     { service: web, command: npm run warm-cache, on: start }
  digest:   { service: worker, command: npm run digest, on: { schedule: "0 8 * * *" } }
  backfill: { service: worker, command: npm run backfill }

A job runs command in a fresh container of service, with its image and env. on is one event or a list.

onWhen it runs
deployWhen a sandbox is first set up, and on every update after (a changed manifest, new code, a retry). Migrations go here.
seedOnce, when a sandbox is first set up.
startOnce per sandbox, after deploy and seed.
destroyWhen the sandbox is deleted.
{ schedule: "<cron>", time_zone: <zone> }On a five-field cron schedule, or @hourly, @daily, @weekly, @monthly, @yearly. UTC unless time_zone names an IANA zone such as Europe/Paris. Paused in sandboxes: parity run <job> fires one.
omittedOnly on demand, with parity run <job>.
  • Order. Events are phases: all deploy jobs, then seed, then start. A seed never meets an empty schema. Services take traffic once they're done.
  • Parallel unless ordered. Jobs of one phase run in parallel (up to 8 at once). A job that must run after another lists it in depends_on, as in Buildkite. See Steps.
  • Failures. A failed job skips the jobs that depend on it, lets running ones finish, then fails the sandbox; parity up prints its log tail. Rerunning parity up retries it.
  • Jobs wait for every managed entry. Services start after the setup jobs, so a job that needs one (a database you run as a service, such as MySQL) lists it: depends_on: [mysql]. That service, and the services it depends on, then start before the setup jobs, and the job waits until its first port accepts connections (and its health check passes, if it has one). The job's variables still come from its own service. A service keeps no data across restarts, and a sandbox whose seed writes to one isn't restored from a snapshot.
  • In-app schedulers. Pausing covers the manifest's jobs only. A scheduler inside your own process keeps running. parity scale <service> 0 stops it.
  • HTTP jobs. command: { http: GET /api/cron/digest } sends that request to the service's first port, instead of starting a container of it. It fails on an HTTP error status, and retries while the service isn't answering yet. If the service has CRON_SECRET, the request carries Authorization: Bearer <CRON_SECRET>, as Vercel cron does, so existing cron routes work unchanged. HTTP jobs run on a schedule or with parity run, since setup jobs run before services start.
  • Commands run through a shell when they need one. A command with $VAR, &&, quotes or pipes runs as sh -c, so $CRON_SECRET is the service's variable. A plain npm run x runs directly.

Runners

runners:
  tools: { image: ghcr.io/acme/deploy-tools:3 }

jobs:
  purge: { service: web, runner: tools, command: ./scripts/purge-cdn.sh, on: deploy }

A runner is an image that jobs run on instead of their service's: build tools and CLIs the app itself doesn't need. Define it once and pick it with runner:. The job still belongs to its service, so it gets that service's env and egress. The console's Runners page lists each runner, its jobs, and the hosts they were blocked from.

Steps

jobs:
  init-db: { label: Create the database, service: web, command: npm run db:init, on: deploy }
  migrate: { service: web, command: npm run migrate, depends_on: [init-db] }
  assets:  { service: web, runner: tools, command: ./scripts/upload-assets.sh, on: deploy }
  release:
    block: Release to customers
    prompt: Check the canary dashboard and #support first.
    depends_on: [migrate, assets]

Every job on deploy, seed or start is a step of a sandbox's rollout. Your environments run the deploy steps.

  • label is the name the console and CLI show: one line, up to 200 characters. The job's name stays its key.
  • depends_on lists jobs as well as services. A job runs after the jobs it lists; jobs with nothing between them run in parallel. A job without on runs on the latest phase of the jobs it depends on. It can depend on jobs of the same or an earlier phase, never a later one or itself. Only steps depend on jobs: a scheduled or on-demand job can't.
  • block makes a step that runs nothing and waits for a person to approve it: block: <label>, or block: true with an optional label. prompt is optional text, up to 1,000 characters, that the approve dialog shows. A block always runs on deploy, so every job it depends on must be a deploy step.
  • Sandboxes pass blocks. A sandbox is an agent's, and agents never approve. Environments wait on a block for a person (environments.deploy, which Developers have); environments: [production] limits it to the environments it names. Approve lets the steps after it run; Reject fails the rollout; if nobody decides within timeout (a duration such as 4h; default 24h, at most 720h), the rollout fails. A newer rollout asks again.

How steps run, fail and report status: Steps and approvals.