Guides

Release engineering guide

Safe Container Releases: Immutable Builds, Progressive Delivery, and Rollback

A safe release is a traceable artifact, compatible data change, controlled traffic shift, and verified rollback path, not merely a successful container build.

Containers make application packaging consistent, but they do not make a deployment safe by themselves. A mutable image tag, startup migration, missing readiness check, or unmeasured traffic shift can turn a routine rollout into an incident. Safe releases make risk visible and keep recovery options open.

The Twelve-Factor separation of build, release, and run is a useful baseline. Build creates an artifact. Release associates it with approved configuration and migration decisions. Run starts that exact release. Preserve those boundaries so engineers can answer what code ran, where, with which configuration, and how to reverse course.

Build immutable, traceable artifacts

Build a single container image from a pinned base image and a locked dependency graph. Tag it for human readability, but deploy by digest so the runtime receives the exact bytes that were tested. Record the source revision, build time, dependency metadata, and provenance in your artifact system.

Use a multi-stage build to keep compilers, package caches, and test tools out of the runtime image. Run as a non-root user where the application permits it, use a minimal runtime base, and avoid adding shell tools solely for production debugging. Smaller images generally pull faster during scale events, but minimality must not prevent necessary certificate, timezone, or diagnostic behavior.

Scan base images and dependencies continuously, then prioritize findings by exploitability and runtime exposure rather than treating every CVE as equally urgent. Sign artifacts and verify signatures in the deployment path when your supply-chain controls support it. A signed vulnerable image is still vulnerable; signatures establish provenance, not safety.

Make the release a reviewed unit

Create a release record that links image digest, deployment manifest or chart revision, configuration version, migration version, owner, and change reason. Promote the same tested artifact across environments. Rebuilding from the same source for production can pick up different dependency or base-image content and breaks the evidence chain.

Keep environment-specific configuration outside the image. Validate it before the traffic shift, including hostnames, certificates, secret references, resource requests, and feature flags. Do not use a container startup hook to fetch unpinned application code or run an unreviewed database mutation.

A Kubernetes rollout is not proof of a healthy release

Kubernetes can report that pods are available while users still see elevated latency, cache misses, authorization failures, or a broken checkout path. Combine rollout status with synthetic journeys, real-user signals, error rates, and dependency saturation before expanding traffic.

Keep database changes compatible

Database changes are often the real rollback constraint. Use the expand-contract pattern:

  1. Add a backward-compatible schema, index, or field.
  2. Release code that can read old and new representations.
  3. Backfill or migrate data in bounded, observable batches.
  4. Switch reads and writes after verification.
  5. Remove the old path only after all supported releases no longer need it.

Avoid destructive or long-running migrations in an application pod startup path. Multiple replicas may race, a failed rollout may leave partial data, and a migration can exceed deployment timeouts. Run migrations as an owned, idempotent operation with a lock where required, progress monitoring, a pause plan, and an explicit rollback or forward-fix decision.

Messages and APIs need the same compatibility discipline. Consumers may lag producers during a rollout, and clients at the edge may retain cached assets or open connections for longer than a deployment. Version schemas, tolerate unknown fields where appropriate, and deprecate deliberately.

Shift traffic progressively

Start with the smallest safe exposure: internal validation, one replica, a canary slice, or a selected region. Compare the candidate with the current release using a defined observation window and automatic or operator-owned promotion criteria. Useful signals include successful request rate, p95 and p99 latency, resource saturation, dependency errors, queue age, cache hit ratio, and business journey completion.

Choose a rollout method that fits the risk:

  • Rolling update: practical for compatible, stateless services with good readiness and drain behavior.
  • Canary: limits blast radius and supports comparison under real traffic.
  • Blue-green: provides a quick traffic reversal but requires data and dependency compatibility across both versions.
  • Feature flag: separates code delivery from behavior exposure, but flags need ownership, expiry, and secure targeting.

Kubernetes maxUnavailable and maxSurge should reflect real capacity. A service that normally runs at 80% of its dependency or CPU budget has little room for a surge. Pod disruption budgets protect against some voluntary disruptions but do not compensate for inadequate replica count, bad readiness, or a broken application version.

Protect latency through the release path

Release risk includes performance regressions. A new serialization library, cache-key change, connection-pool default, or logging call can affect tail latency before error rate rises. Set performance acceptance thresholds for critical routes and compare them with a baseline. Include cold starts, cache misses, and geographically distant users in tests where they represent production traffic.

If public content is delivered through an edge cache, deploy cache behavior and origin code as a coordinated change. Validate cache-control directives, vary dimensions, invalidation, origin authentication, and rollback behavior. Do not purge globally by default for every application release: a cold cache can overload an origin and increase worldwide latency. Use targeted invalidation and capacity planning.

Keep direct origin access restricted so traffic cannot bypass the intended edge controls during or after a release. Health checks must test the correct version and route. A load balancer that marks a pod healthy based only on a TCP connection can send users to an application that has not completed initialization.

Observe, stop, and recover

Instrument each release with an image digest or release identifier in metrics, logs, and trace resource attributes. Use OpenTelemetry propagation to connect edge requests, ingress, application spans, and dependency calls. Never attach secrets or customer payloads to telemetry.

Define stop conditions before rollout: a sustained error-rate increase, p99 latency regression, failed synthetic transaction, depleted error budget, queue-age rise, or elevated dependency saturation. Alerting should distinguish expected rollout churn from user impact, and the on-call owner must be able to pause promotion.

Rollback means returning traffic and behavior to a known-good state, not just applying an earlier manifest. Confirm the old image remains available, configuration remains compatible, database migrations permit it, and caches will not serve an inconsistent response. When rollback is unsafe, use a prepared forward fix with the same staged controls.

Release checklist

Before production, confirm the image digest is pinned and traceable; the dependency and base-image posture is understood; configuration and secret references are validated; migrations are compatible and owned; readiness, startup, and shutdown behavior are tested; capacity includes rollout surge; release signals and stop conditions exist; and rollback has been rehearsed for code, configuration, and data.

After release, keep watching beyond the controller's success message. Some defects appear only after cache expiry, token rotation, autoscaling, a regional traffic pattern, or a scheduled job. Record the result and improve the release template rather than relying on memory for the next change.

Authoritative references

Release with the delivery path in view

Optimi can help review edge, origin, caching, and traffic-routing architecture so release changes protect performance and resilience.

Discuss release resilience