A deploy takes your source (or a pinned image) and updates one Kubernetes Deployment with a rolling update. Kubernetes keeps ready old replicas serving while the new revision becomes ready. Every deploy is recorded as an immutable history entry you can inspect and roll back to.
The deploy lifecycle
A deploy moves through a fixed set of states:
| State | What's happening |
|---|---|
| Building | The image is being built (from a Dockerfile or source) or pulled. With deploy intents, this state appears from the first second of CI. |
| Release |
Your release_command runs once, before cutover —
migrations, asset prep. If it fails, the deploy stops here and the old
version keeps serving.
|
| Running | The Deployment's current generation completed its rolling update. For an enrolled app, every desired replica passed its configured TCP or HTTP readiness probe and the requested image is running. |
| Succeeded | The cutover completed and the new version is live. |
| Failed | A build, release, or rollout failed. Ready old replicas can continue serving while Kubernetes refuses to complete a broken new revision. |
Readiness and observed health
New apps explicitly created as HTTP services with a positive container port
default to TCP readiness. Apps created from a template whose primary port is
labelled HTTP or HTTPS do too. Existing apps stay unenrolled until you choose disabled, tcp, or http; HTTP mode requires
an absolute app-specific path such as /health. A change takes effect
on the next deploy. Flicker does not add an automatic liveness probe, so a
mistuned health path cannot restart-loop your container.
The app's top-level status
remains lifecycle state for compatibility.
Existing API responses also include nested observed health
with healthy, unhealthy, or unknown, its check time,
and staleness. CLI lists label these separately as HEALTH, CHECKED, and LIFECYCLE. A stale or never-run check
never appears healthy.
Building from the first second of CI
Normally a deploy only becomes visible when CI finally calls Flicker —
minutes into the pipeline. Deploy intents
fix that. CI
opens an intent at workflow start, so the deploy shows as Building
from the very first second, carrying the commit,
the actor, and the CI run URL. When the build finishes and the real deploy
lands, the worker attaches to that same intent — giving you one continuous
record from git push
to rollout instead of a row that appears
only at the end.
Two deploy modes
One command, two ways to point it at your code:
$ flicker deploy # manifest mode — pinned image or [build] repo (CI) $ flicker deploy --from-local # tar the working dir, build on host (dev)
Manifest mode
deploys exactly what's pinned in flicker.toml
— a registry image, or a git repo + ref the host
clones and builds. Reproducible and reviewable in a PR — the right primary
for CI. Local mode
tars your working directory (including
uncommitted changes), ships it over, and builds it on the host — the
"deploy what I have right now" path for development. Both converge on the
same cutover.
Deploy history
Every deploy writes an immutable history entry with its state transitions, the image it ran, and a link to its build and deploy logs. The current status is a cheap denormalized cache; the event log is the source of truth, and historical rows are never mutated. The app page surfaces this timeline so you can see what shipped, when, and from which commit.
From the CLI, tail a running deploy's container logs:
$ flicker app logs web
Rollback via the Registry
Because every deploy stamps the exact image ref it ran, rolling back is just deploying a previous image. The app's Registry lists the images prior deploys used; pick the last good one and redeploy it:
$ flicker app deploy web --image myorg/web:v2 # re-run the same rolling cutover with the previous image
The rollback runs through the identical rolling cutover as a forward deploy — if the old image doesn't come up, the current version keeps serving. Rollback is a deploy, so it's recorded in history like any other.