How Cynone achieves sub-second deployments
Sub-second deployments are not about building faster. They are about never rebuilding, never re-pulling, and swapping the container once it proves it is healthy. Here is exactly how we make the switchover land in under a second.
Sub-second deployments sound like a marketing number until you have to build one. The reality is that git push to serving traffic will always take longer than a second on any serious codebase, and anyone who tells you otherwise is describing something narrower than a deployment. What we actually measure, and what we optimise for, is the time between "start the new version" and "the new version is answering requests" โ and that path is genuinely under a second on a warm path.
This is not a trick and it is not a demo. It is the result of removing every step from the rollout that can be done before the rollout starts. This post breaks down what we did, in the order we did it, with the numbers that made us keep going.
What a sub-second deployment actually is
A deployment has a cold path and a warm path. The cold path is everything that has to happen at least once for a given commit: dependency resolution, compilation, image build, image push. The warm path is everything that has to happen every time that artifact is promoted to a new environment or rolled back to: pull, start, health-check, swap.
We never tried to make the cold path sub-second. That is a losing game and a pointless one. The cold path is where your compiler is allowed to be slow. The warm path is where your users are standing, and that is where we applied the pressure.
So when we say a deployment is sub-second, we mean the warm switchover: the new container starts, reports healthy, and receives traffic โ all inside a second โ while the old one is draining in parallel. The commit itself may have taken six minutes to build. Nobody on the other side of an API call cares.
Where the seconds actually go
Before changing anything we profiled a plain rollout on a plain host and wrote down every step with a measured time. This is the boring table that made the whole project possible:
- Image pull on a cold local cache: 4โ9 seconds for a Laravel image around 200 MB.
- Image pull with a warm cache: 150โ400 ms for the changed layer, ~50 ms when the digest is already local.
- Container create and start: 120โ300 ms depending on entrypoint work.
- Readiness probe to first successful response: 300 msโ2 s depending on the app.
- Traffic cutover in the proxy: 5โ50 ms once the routing table is reloaded.
Two things stood out. The image pull dominated the budget, and the readiness probe โ not the container start โ was the true gate. Optimising either one alone gets you nowhere; the path is only as fast as the slowest step you refuse to pre-compute.
Build once, promote the same digest
The first change was the one with the most leverage: a single immutable artifact per commit. Every environment โ preview, staging, production, and every region โ runs the exact same image digest. We build it once, push it once, and every subsequent deploy is a pull of a digest we already know.
This kills an entire category of bugs on its own. Staging no longer runs a slightly different build than production, because there is no second build. The "works on my build server" class of problem mostly disappears, and promotions stop being rebuilds and start being relabels.
It also makes the warm path predictable. Because the digest is immutable, a cache keyed by digest never lies. When a region already ran that digest, the pull is a metadata lookup plus a layer that may not even need to move.
Push images to where the runtimes are
An immutable artifact in a registry in one region is still a cold pull everywhere else. We run registry mirrors near each cluster and have every node pre-pull the digest the moment a deploy is queued โ before the rollout decision is even final.
Pre-pull is the single biggest win on this list. It moves the 4โ9 second pull out of the critical path entirely and replaces it with a background task that races the build pipeline. By the time the orchestrator is ready to act, the bytes are already on disk.
We also stopped tagging images with anything but the digest in production. Tags get overwritten; digests do not. Every rollout now references a content-addressable artifact, which means the deployment record is also an audit record: the exact bytes, everywhere.
The atomic swap
The rollout itself is a blue-green swap on a single host, not a rolling drain. We start the new container alongside the old one, wait for its readiness probe, and only then flip the routing. The old container keeps serving until the flip lands, so there is no window where nothing is listening.
A true rolling update is strictly slower and strictly more dangerous: it churns capacity in the middle of a rollout, and a bad health check can take half the fleet down before it trips. With a swap, the decision is binary and the blast radius is the time between the flip and the new container failing a post-deploy check โ which is where rollback takes over.
Health checks are the real clock
The proxy cutover is milliseconds. The thing that eats your budget is the readiness probe, and most apps configure it badly: a tcp check on a port that accepts connections before the app can serve, or an http check with a generous timeout that the orchestrator retries lazily.
We made the probe a single HTTP request to a handler that exercises the real request path โ router, middleware, database connection check โ with a 300 ms timeout, polled every 200 ms. If the app needs two seconds to become ready, we tell the orchestrator that explicitly instead of letting it guess through retry timers.
An honest readiness signal is worth more than any startup optimisation. A container that reports ready before it can serve traffic turns a sub-second rollout into a broken one, and no swap speed fixes that.
What we do not claim to be fast at
Cold builds are not sub-second. First deploy to a new region is not sub-second โ the image has to physically arrive. Any deployment that changes infrastructure, like a new persistent volume or a DNS change, is not sub-second.
That is fine. Sub-second is a property of the steady-state operating loop, not of first contact. The value is that the loop everyone lives in โ shipping a fix, promoting to production, rolling back โ is fast enough that nobody is tempted to skip the pipeline.
Rollback in the same budget
The same machinery that makes a deployment sub-second makes a rollback sub-second, because a rollback is just a deployment of the previous digest. The old image is still local, the routing table already knows it, and the readiness probe we trust from the forward deployment is the same one we trust on the way back.
This is the property that made the whole thing worth building. When rollback is cheaper than debugging, your operators stop being afraid of deploys, and the pipeline becomes the thing people reach for instead of the thing they route around.
Sub-second switchover is not a benchmark we run to impress anyone. It is what happens when you stop asking your compiler to do work at deploy time, and start asking only two questions: is the new version healthy, and is the old version done. Everything else can take as long as it wants, somewhere off the critical path.