Deployments

A deployment is one attempt to build (or reuse) an image and run it behind your app’s URL.

Triggers

  • GitHub webhook — pushes to the tracked branch (HMAC-validated), when auto-deploy is enabled
  • Manual — the Deploy button in the UI
  • Rollback — redeploys a previous deployment

Status machine

Every deployment moves through:

GitHub webhook Manual deploy queued building deploying live failed superseded next deploy rollback — redeploys the retained image
Failure states keep their logs. A deployment stopped before it goes live ends as cancelled (not shown), and one stuck in a non-terminal state for over 15 minutes is failed at startup with a clear reason.

queued building deploying live superseded failed cancelled

Exactly one deployment per app is live — the one currently serving traffic. When a new deployment goes live, the previous one is demoted to superseded. Superseded deployments keep their image and remain valid rollback targets.

Logs

Build and deploy logs stream live over SSE and are persisted per deployment, so you can inspect a failed deploy after the fact. The five most recent deployments per app are retained; older rows, log files, and images are reclaimed.

For the running app’s own output, see the app’s Logs tab — Metrics & Logs.

Safety properties

  • Builds are concurrency-capped (default 2 at a time)
  • Deploys are serialized per app — no two deploys of the same app race
  • A failed build never affects the currently running app
  • Under blue/green, a failed deploy never affects it either — the new version is discarded and the old one keeps serving
  • Deploy jobs get a 30-minute timeout, comfortably covering a cold build plus the health gate, and a failure is persisted even if the job’s context was cancelled
  • On startup, any deployment stuck in a non-terminal state for over 15 minutes — the residue of a crash or restart mid-deploy — is failed with a clear reason; more recent ones are resumed by the job queue
  • Transient job failures are retried with backoff; failed deploys preserve logs and never corrupt platform state

Image retention

After each deploy — and again daily — Cargo prunes anything outside the newest five deployments per app, so build images don’t accumulate on disk. Rollback targets within that window always have their image available. An image is never removed while something still runs it — that covers a rollback (whose image belongs to an older deployment row) and a blue/green hand-off (where two colors run different images at once). The disk guardrail reclaims dangling images if space gets tight anyway.

Zero-downtime deploys

By default Cargo deploys blue/green: the new version is started beside the running one and only takes over once it is healthy. The old version keeps answering every request until then.

Each app alternates between two colors. A deploy always targets the idle one, so the version that is currently serving is never touched until the new one has proven itself.

1 — steady state Traefik blue

2 — green starts, gated out of rotation Traefik blue green healthcheck failing — no traffic

3 — green healthy, blue reaped Traefik green blue removed

Both colors declare the same Traefik router and service, so Traefik pools them and the switch needs no proxy reconfiguration. Only one color survives a successful deploy.

If the new version never becomes healthy, Cargo removes it and leaves the old one serving. The deployment ends failed — there is nothing to roll back, because the running app was never replaced.

The very first blue/green deploy of an app that predates this feature is a one-time exception: the old container is retired before the first color starts, so that deploy has the same brief gap as recreate. Every deploy after it is a true hand-off.

Choosing a strategy

Set it per app under Settings → Deploy strategy, or instance-wide with CARGO_DEPLOY_STRATEGY (see Configuration).

Strategy Behaviour
bluegreen (default) New version starts alongside the old one; traffic switches only after it’s healthy
recreate Container is replaced in place; brief downtime per deploy
Set a healthcheck path

During the hand-off both colors are in Traefik's backend pool, and it is Traefik's healthcheck that keeps a still-booting container out of rotation. Without a healthcheck path there is nothing to probe, so a request can briefly reach the new version before it is ready. Blue/green still works — but set a path to get the full guarantee.

When to pick recreate

Blue/green runs two instances of your app at the same time for a few seconds. Choose recreate if that isn't safe — for example an app that runs schema migrations on boot, holds an exclusive lock, or is a singleton worker. It also briefly doubles the app's memory and CPU footprint, which counts against its resource limits.

Rollback

From app → Deployments, pick any previous live or superseded deployment and hit Rollback. Rollback redeploys that deployment’s retained image without rebuilding, so service is restored within seconds (target: ≤ 1 minute).

A rollback is an ordinary deployment, so it uses the app’s strategy too: under blue/green it hands back over with no downtime.

A failed deploy alerts the org’s owners and admins — see Operations & Hardening.

Worker hosts

Apps can be deployed to remote machines registered as worker hosts under Admin → Worker hosts. Each worker runs its own Traefik/proxy stack and is reachable over SSH. Blue/green deploys work identically on workers.

SSE stream limits

Live log streams are capped per-user (8) and per-instance (64). Access is revalidated every minute — revoked or expired sessions drop their streams immediately.