Product

Deploys that explain their own failures

July 11, 2026 · 5 min read

A deploy that fails is a fact of life. A deploy that fails and leaves you SSHing into a box to figure out why is a design choice — and one Sproobo has spent the last few months removing, one piece at a time. Here's the full shape of a deploy today: how it swaps safely, what it guarantees when it doesn't, and what shows up automatically when it fails anyway.

The anatomy of a deploy

Every deploy — first ever or the thousandth — runs the same sequence, whether it was triggered by a push, the CLI, or an AI agent through the same API a human uses:

01

Build off-box, push to the registry

The image is built in a capped control-plane builder — never on the customer server — tagged to the git commit, and pushed to Sproobo's S3-backed OCI registry.

02

The agent pulls it

The server's agent pulls the new image with scoped registry auth.

03

New container starts alongside the old

The new release comes up on the internal sproobo-int network. The old container keeps serving every live request, completely untouched.

04

Health gate

The new container is probed at its configured healthPath (default /healthz) before it ever sees a real request.

05

Atomic proxy switch

Once healthy, the proxy route for the apex and www domains flips to the new container in one caddy validate-gated reload, and the old container retires.

The full pipeline, including build-strategy selection and build-time env handling, is documented on Apps & deploys.

The precision that matters: which failures are safe

Blue-green only earns its keep if the two halves of that swap have different failure stories, and they do. A health check that never passes is caught at step 04, before the proxy has moved at all — the old release is still the one answering every request, and the deploy is simply marked failed. Nothing customer-facing ever saw a half-deployed app. Auto-rollback is a different, narrower guarantee: it exists specifically for the case where the proxy already switched and the new release then fails health after taking traffic. That path is automatic and immediate. The pre-switch case doesn't need rollback at all, because nothing was ever handed to the new container in the first place.

Concurrency gets the same treatment: each app holds a deploy lock while one is in flight, so a second trigger mid-deploy queues or is rejected with a clear status instead of racing the first. Combined with immutable, image-tagged releases, an app can't end up half-served by two overlapping deploys either.

When it fails anyway, the failure explains itself

For a long time, a deploy that never turned healthy just said so: a health-gate timeout, and nothing else. That's technically correct and practically useless — "never healthy after N attempts" doesn't tell you whether the app crashed on boot, couldn't reach its database, or was missing an env var. Diagnostics that landed in July 2026 close that gap: when a deploy fails at or after the health gate, Sproobo automatically captures the failing container's own log tail and attaches it directly to the deployment record. It shows up in the dashboard's deploy view and on the deploy-failure notification, no SSH required to go looking for it.

It's a small change in scope — reusing the same log-collection path Sproobo already uses for live tailing, not a new subsystem — but it changes what "the deploy failed" means in practice. The probe error tells you that it failed; the container's own output, sitting right next to it, usually tells you why.

Before, that meant reaching for SSH and docker logs on a box you might not have touched in weeks, hoping the failing container hadn't already been cleaned up. Now the same log tail is already sitting on the deployment record and in the failure notification by the time you open the dashboard — the diagnosis step that used to cost you a terminal session and a bit of guesswork now costs nothing.

Releases you can always get back to

Every deploy is an immutable, image-tagged release, and Sproobo keeps the last N per app — new apps default to 3, capped by plan: Free keeps 1, Pro up to 5, Team and Enterprise up to 10, adjustable per app within that cap. Rolling back doesn't rebuild anything — it replays the same blue-green sequence against an image that's already sitting in the registry:

sproobo rollback <appId> -y

With no --to, the last known-healthy release is picked automatically — no need to look up a release id first just to undo a bad deploy. Target a specific one instead with --to <releaseId>. Either way it's seconds, not minutes: a container start and a health-gated switch, nothing to compile.

Why this shape, and not a bigger one

None of this needed a queueing system, a staging environment, or a second copy of production to get right. It needed the swap to be atomic, the failure modes to be precisely scoped instead of one big generic error, and the information that already existed on the box — the container's own logs — to travel with the deployment record instead of staying stuck on the server. It also has to hold up regardless of who triggered the deploy: a push, a person in the dashboard, or an AI agent calling the same audited API — all three get the same blue-green guarantees and the same diagnostics if it fails.

Read the full build-to-rollback flow, including how auto-deploy on push fits in, on Apps & deploys, or start from a clean server on Getting started.

← Back to the blog

Get started

Run this on a server you own.