Deployments

How a deployment goes live on Lucity, and the ways to trigger one.

A deployment is everything that happens between a commit and a running container: Lucity builds an image from your source, points the service at that image, and replaces the running instances with new ones.

You can watch all of it from the Deployments tab of a service, which lists every deployment with its current state and its logs.

Deployment steps

Every deployment moves through the same four steps, and the dashboard shows them in order:

StepWhat happens
BuildYour source is turned into a container image. See builds for what that involves.
SecretsThe commit is scanned for leaked credentials.
DeployOnce the build succeeds, the service is pointed at the new image.
RolloutThe new instances start and the old ones are retired.

Build and Secrets run at the same time. The secret scan never blocks a deployment: it reports what it found and the deploy carries on regardless. Findings are still worth acting on, because a credential that reached git history stays there until you rotate it.

Deploy waits for the build. If the build fails, the deploy stops and nothing changes, so the version that was already running keeps running. The same is true of every failure before the rollout starts: a broken deployment cannot take down a working service.

A build gets 30 minutes and is not retried. If it runs out of time, the deployment fails and you try again.

Triggering a deployment

There are different ways to start a deployment.

Each service tracks one branch, set under Settings → Source. Unless you say otherwise, a deployment builds the latest commit on that branch.

Open the service, go to the Deployments tab, and click Deploy. That builds the latest commit on the tracked branch, and the dashboard offers no way to pick a different one, so change the tracked branch first if you need another.

Each of the four steps has its own logs, streaming while the step runs, so you can watch a build compile and a rollout come up without leaving the panel.

Configuration changes

Not every change needs a new image. Editing variables, CPU and memory, the port, the start command, the health check or a volume mount reuses the image the service is already running, and applies within seconds. The new instance starts, passes its health check, and takes over before the old one goes away, so a running service does not go dark while you change it. The exception is a service with a volume attached, which stops before the replacement starts.

Changing the replica count is the one change that is not a rollout at all. Existing instances keep serving and new ones simply join them.

Rollouts

The rollout is the part where running containers are replaced, and it is arranged so that the service keeps answering throughout.

A new instance starts alongside the old one and receives no traffic until its health check passes. Once it does, the old instance is taken out of rotation and given ten seconds to finish requests that are already in flight before it is asked to shut down. The whole shutdown has a 30 second budget, so roughly 20 seconds are left for your process to exit on its own before it is killed. That is why handling the shutdown signal matters if you care about the last few requests.

The count never dips below what you asked for. Lucity adds an instance before removing one, rather than the other way around, so a single-replica service is covered too.

Two cases work differently:

  • A service with a volume cannot overlap, because the disk is still attached to the instance being replaced. It stops first and starts again, which means a short gap on every deploy.
  • A service with no port has nothing to health check, so a new instance counts as ready as soon as its container is up.

Rollout failures

If the new instance never becomes healthy, the old one keeps serving and the deployment is marked failed. Nothing is lost and there is no rush: you can take as long as you need to work out what went wrong.

The deployment shows a reason, which is usually one of these:

ReasonWhat it usually means
Crash loopThe container starts and exits repeatedly. The exit code is shown with it, and the logs have the rest.
Out of memoryThe container exceeded its memory limit. Raise it, or find out what is holding the memory.
Image pull failedThe image could not be fetched. Most common on services built from a public image reference that no longer resolves.
Config errorThe container could not be created from its configuration at all.
Quota exceededThe environment ran out of its resource budget.
UnschedulableThere is no capacity to place the instance right now.
Not readyThe container runs fine but the health check never passes.
Deadline exceededThe rollout made no progress within the time allowed.

A deployment is degraded when some of its new instances came up and others did not, and failed when none of them did.

The service is a separate reading, and usually the one you care about. While the previous version is still serving, a service with a failed deployment shows as Degraded: your users are fine, the new version is not. It only shows as Failed once nothing healthy is left, which happens on a first deployment that never worked, or on a service whose old instance had to be stopped before the new one could start, such as one with a volume attached.

Rollbacks

Reverting the change in git and pushing is always available, and with auto-deploy on it is often the first thing to reach for. That rebuilds, so it takes as long as any other deployment, but it leaves your repository and what is running in agreement.

When you want the previous version back without waiting for a build, Lucity can point the service straight at an image it already ran. Every deployment keeps its image, so this is live in seconds.

Rolling back is not exposed in the dashboard yet. Until it is, use the Claude plugin or the API, or revert in git and let the service rebuild.

One deployment at a time

Deployments queue rather than running all at once. At most one is active per environment, so two pushes in quick succession cannot race each other into conflicting states. Queued deployments are admitted oldest first as the ones ahead of them finish.

There is also a limit on how many deployments a workspace can have waiting. Past that, a deploy is rejected with a message saying the queue is full, and you try again once something has drained.

Next steps

  • Builds for how a repository becomes an image
  • Services for health checks, replicas and resources
  • Logs for what a failing container printed on its way down
  • Environments for keeping production separate from everything else