Understanding Blue-Green and Rolling Deployments with Docker Containers
AI generated
Docker · Deployment Strategies · DevOps · Zero Downtime
Understanding Blue-Green and Rolling Deployments
with Containers, and Putting Them into Practice

Every deployment without a clear strategy is a gamble with availability. Blue-Green and rolling deployments solve that problem differently, but both rely on container health checks, clean routing, and atomic switchovers. This article shows when each strategy fits and how to implement it concretely with Docker Compose and Traefik.

12 min read Blue-Green · Rolling · Traefik · Health Checks · Rollback Docker 25+ · Compose v2 · Traefik v3

1. What deployment strategies really mean

A deployment strategy determines how the transition from one running software version to the next takes place, without users perceiving that transition as an error or an outage. In a container context, this means concretely: which containers run when, how long the old and new versions run in parallel, and under what conditions the new version fully takes over traffic. These questions are not academic. They determine whether a deployment at 2pm on a Wednesday is routine work or a planned maintenance outage with an escalation chain.

Docker and container orchestration have made Blue-Green deployments and rolling deployments considerably simpler. Starting a new container alongside a running one takes seconds. The hard question is no longer starting the new container, but rather the controlled switchover of incoming traffic, handling active sessions, and having a reliable rollback path if something goes wrong. All three aspects require a deliberate strategy that comes before the deployment script, not after it.

The two most important strategies in the container world are Blue-Green and rolling. They differ fundamentally in their resource requirements, the risk profile during deployment, and the complexity of the rollback mechanisms. Neither approach is universally better: the right choice depends on the application's architecture, how it handles persistent sessions, and the available infrastructure budget.

2. Blue-Green deployment: concept and mechanism

In a Blue-Green deployment, two fully equipped environments exist at all times: the active production environment (Blue) and the new version (Green), which is started up in parallel. As long as Green is not yet live, it receives no real traffic. Only once all health checks for Green have passed is the routing switched atomically: the load balancer or reverse proxy sends all new requests to Green, and Blue stops receiving traffic. Blue keeps running for the time being so it can immediately serve as a rollback target if problems occur.

The decisive advantage of this Blue-Green approach is the atomicity of the switchover. There is no moment in which the old and new code versions handle requests at the same time. This avoids an entire class of problems: inconsistent API responses during a mixed rollout phase, race conditions in parallel database operations from different code versions, and hard-to-reproduce errors that only occur at certain version-mix ratios. For applications with strict consistency requirements, Blue-Green is therefore often the better choice over rolling.

The downside lies in resource requirements: during the deployment, two complete stacks run in parallel. For large deployments, with several services and many container instances, this briefly means double the memory demand and double the CPU load. In cloud environments with dynamic scaling this is acceptable; on fixed hardware, capacity planning must explicitly account for Blue-Green deployments.


# blue-green-deploy.sh: Atomic blue-green switch with Traefik label routing
set -euo pipefail

SERVICE="webapp"
REGISTRY="registry.example.com"
IMAGE_TAG="${1:?Usage: $0 <image-tag>}"

# Detect current active slot
CURRENT_SLOT=$(docker inspect "${SERVICE}-blue" \
  --format '{{index .Config.Labels "deployment.slot"}}' 2>/dev/null || echo "green")
NEW_SLOT=$([ "$CURRENT_SLOT" = "blue" ] && echo "green" || echo "blue")

echo "[INFO] Deploying $IMAGE_TAG to slot: $NEW_SLOT (replacing $CURRENT_SLOT)"

# Pull new image before starting (fail fast on registry issues)
docker pull "${REGISTRY}/${SERVICE}:${IMAGE_TAG}"

# Start new slot, no traffic yet (weight=0 label)
docker run -d \
  --name "${SERVICE}-${NEW_SLOT}" \
  --label "traefik.enable=true" \
  --label "traefik.http.routers.${SERVICE}.rule=Host(\`app.example.com\`)" \
  --label "traefik.http.services.${SERVICE}-${NEW_SLOT}.loadbalancer.weight=0" \
  --label "deployment.slot=${NEW_SLOT}" \
  "${REGISTRY}/${SERVICE}:${IMAGE_TAG}"

# Wait for health check to pass (max 120 seconds)
for i in $(seq 1 24); do
  STATUS=$(docker inspect "${SERVICE}-${NEW_SLOT}" --format '{{.State.Health.Status}}')
  [ "$STATUS" = "healthy" ] && break
  echo "[WAIT] Health status: $STATUS ($((i*5))s elapsed)"
  sleep 5
  if [ "$i" -eq 24 ]; then
    echo "[ERROR] Health check timeout, rolling back"
    docker rm -f "${SERVICE}-${NEW_SLOT}"
    exit 1
  fi
done

# Atomic traffic switch: enable new, disable old
docker update --label-add "traefik.http.services.${SERVICE}-${NEW_SLOT}.loadbalancer.weight=100" \
  "${SERVICE}-${NEW_SLOT}"
docker update --label-add "traefik.http.services.${SERVICE}-${CURRENT_SLOT}.loadbalancer.weight=0" \
  "${SERVICE}-${CURRENT_SLOT}"

echo "[OK] Traffic switched to $NEW_SLOT, old slot $CURRENT_SLOT kept for rollback"

3. Rolling deployment: gradual replacement

A rolling deployment replaces instances of an application one after another, without swapping the entire capacity at once. With five running instances of a service, one is stopped first, replaced by the new version, and confirmed healthy before the next instance is replaced. At no point do fewer instances run than a defined minimum count, typically 50 to 75% of target capacity. This means that during a rolling deployment, the old and new versions serve traffic at the same time.

This concurrency aspect is both a strength and a weakness. A strength, because the resource requirement is significantly lower than with Blue-Green: never more containers are needed than in normal operation plus one instance in transition. A weakness, because the application must be designed so that different versions can run in parallel without issues, a requirement that quickly becomes a challenge with non-backward-compatible API changes or database migrations. Rolling deployments therefore require particular discipline in schema design: every change must be interpretable by the old version.

Docker Swarm and Kubernetes have rolling deployments built in as a native mechanism. With Docker Compose, the principle can be recreated through staggered restarts of multiple profiles or by explicitly scaling and removing individual containers. The maximum rollout parallelism and the minimum availability during the deployment are the two central configuration parameters of every rolling deployment and must be tuned to the application's actual traffic level and response-time requirements.

4. Routing with Traefik: labels and weight splitting

Traefik is particularly well suited for Blue-Green and rolling deployments because routing is configured entirely through Docker container labels, and configuration changes take effect without a Traefik restart. The central concept is weight splitting: multiple backend services receive different weights, and Traefik distributes traffic proportionally. A weight of 100 for Green and 0 for Blue corresponds to a complete Blue-Green switch with no downtime.

For gradual canary releasing, an intermediate form between pure Blue-Green and rolling deployment, weights can be shifted step by step: first 90/10 between old and new, then 70/30, 50/50, and finally 0/100. At each step, the error rate, latency, and key business metrics of the new version are checked before the next step is taken. This pattern requires automation; manually adjusting labels during a live deployment is error-prone and slow.


# traefik-weight-shift.sh: Gradually shift traffic between blue and green
# Usage: ./traefik-weight-shift.sh <service> <green-weight> (blue gets remainder)
set -euo pipefail

SERVICE="${1:?Service name required}"
GREEN_WEIGHT="${2:?Green weight (0-100) required}"
BLUE_WEIGHT=$((100 - GREEN_WEIGHT))

# Update Traefik weights via Docker label update (takes effect immediately)
docker service update \
  --label-add "traefik.http.services.${SERVICE}-green.loadbalancer.weight=${GREEN_WEIGHT}" \
  "${SERVICE}_green"

docker service update \
  --label-add "traefik.http.services.${SERVICE}-blue.loadbalancer.weight=${BLUE_WEIGHT}" \
  "${SERVICE}_blue"

echo "[OK] Traffic split: green=${GREEN_WEIGHT}% blue=${BLUE_WEIGHT}%"

# Monitor error rate for 60 seconds before returning
echo "[MONITOR] Watching error rate for 60s..."
for i in $(seq 1 12); do
  # Query Traefik metrics endpoint for 5xx rate
  ERROR_RATE=$(curl -sf http://traefik:8080/metrics \
    | grep "traefik_service_requests_total.*code=\"5" \
    | awk '{sum+=$2} END {print sum+0}')
  echo "[${i}] 5xx count: ${ERROR_RATE}"
  sleep 5
done

5. Health checks as a deployment gate

A Blue-Green or rolling deployment without reliable health checks is a deployment that carries errors into production without noticing. The HEALTHCHECK instruction in the Dockerfile defines when a container is considered ready, but the more important aspect is exactly what is being checked. A simple curl localhost/ping that returns HTTP 200 does not verify whether the database connection exists, whether all necessary configuration parameters were loaded, or whether external dependencies are reachable. A good health check for a deployment gate verifies all of that.

The trio of timing parameters, interval, timeout, and retries, determines how long the deployment system waits for a healthy status. Overly aggressive timeouts lead to false alarms during slow startup processes; overly lax timeouts delay the detection of real errors and needlessly prolong the deployment process. For PHP applications with Opcache warmup or Magento with DI initialization, 30 to 60 seconds for the first health check is realistic. Importantly, the health check endpoint itself must not have side effects and must respond quickly even under load, or it will fail under traffic.

6. Rollback strategies for failed deployments

A Blue-Green deployment has the fastest rollback path of all strategies: switching traffic back to Blue takes milliseconds, because Blue was never stopped. This instant rollback is the main reason teams with a high release frequency and strict SLA requirements prefer Blue-Green. The cost of the parallel running environment is, in effect, the insurance premium for a guaranteed fast recovery.

With a rolling deployment, the rollback is more involved: every instance already replaced must be rolled back to the old version. This happens following the same staggered pattern as the original deployment, which costs time and extends the risk window. In practice, a hybrid is therefore often chosen: rolling for regular small updates where rollbacks are rare, and Blue-Green for major releases with significant changes where rollback risk is higher. What matters is that the rollback path has been tested and documented beforehand, not tried out for the first time in an emergency.

An often overlooked rollback dimension is the database: if the deployment includes a database migration, that migration determines whether an application-code rollback works without a database rollback. Additive migrations (new columns, new tables) allow rollbacks; destructive migrations (dropping or renaming columns) do not. The expand-contract pattern for database migrations is therefore the necessary complement to Blue-Green and rolling deployments.


# rollback.sh: Instant blue-green rollback to previous slot
set -euo pipefail

SERVICE="webapp"

# Identify which slot is currently active (receiving traffic)
ACTIVE_SLOT=$(docker inspect "${SERVICE}-blue" \
  --format '{{index .Config.Labels "traefik.http.services.'${SERVICE}'-blue.loadbalancer.weight"}}')

if [ "$ACTIVE_SLOT" = "100" ]; then
  ROLLBACK_TO="green"
  ROLLBACK_FROM="blue"
else
  ROLLBACK_TO="blue"
  ROLLBACK_FROM="green"
fi

echo "[ROLLBACK] Switching traffic from $ROLLBACK_FROM back to $ROLLBACK_TO"

# Atomic rollback: restore old slot weight, remove new slot weight
docker update \
  --label-add "traefik.http.services.${SERVICE}-${ROLLBACK_TO}.loadbalancer.weight=100" \
  "${SERVICE}-${ROLLBACK_TO}"

docker update \
  --label-add "traefik.http.services.${SERVICE}-${ROLLBACK_FROM}.loadbalancer.weight=0" \
  "${SERVICE}-${ROLLBACK_FROM}"

echo "[OK] Rollback complete, $ROLLBACK_TO is now active"
echo "[INFO] Remove failed slot: docker rm -f ${SERVICE}-${ROLLBACK_FROM}"

7. Database migrations for zero-downtime releases

Database migrations are the most common reason why Blue-Green deployments fail or cause downtime even though the goal was zero downtime. The problem: the new application version expects a schema that does not yet exist, or the old version keeps running during the deployment and cannot handle the new schema. Either way leads to errors. The expand-contract pattern solves this in three phases: expand (additive schema change, compatible with old code), deploy (new code works with both new and old schema), and contract (remove old schema elements once the deployment is fully complete).

In practice, this means: a column is never renamed directly. Instead, a new column with the new name is added, the code reads from and writes to both, and the old column is removed in a later release. This effort is real, but it is the price for genuine zero-downtime deployment without session interruptions. Anyone who wants to combine migrations with Blue-Green deployments must plan the migration process as a standalone step before the container switch and make sure the migration container runs only once, not on every container start.

8. Deployment monitoring: when is a release stable?

The end of a Blue-Green or rolling deployment is not the moment the last container reports healthy. A release is only considered stable once the error rate, response time, and key business metrics remain within expected limits over a defined observation window, typically 5 to 15 minutes after the complete traffic switch. Deployment monitoring is therefore not a logging problem but an observability problem: without metrics, the signal that distinguishes a healthy release from a broken one is missing.

For automated Blue-Green deployments in CI/CD pipelines, this means: the deployment script does not end with the traffic switch, but with a stabilization phase in which metrics are observed and an automatic rollback is triggered if the error rate exceeds a threshold. Prometheus metrics, Traefik access-log aggregation, and APM data together form a meaningful signal for that decision. Without this automation, the deployment is only truly automatic when everything goes well, not in the moment it actually matters.

9. Blue-Green vs. rolling: a direct comparison

Both strategies have their place. The decision between Blue-Green deployment and rolling deployment depends on application architecture, resources, and risk tolerance.

Criterion Blue-Green Deployment Rolling Deployment Recommendation
Resource requirement 2x capacity during deploy 1x plus one instance Rolling for fixed hardware
Rollback speed Instant (traffic switch) Minutes (reverse roll) Blue-Green for high SLAs
Version coexistence None (atomic switch) Yes (during rollout) Blue-Green for strict API consistency
Deployment complexity High (2 stacks, routing) Medium (staggered update) Rolling as an entry point
Database migrations Expand-contract required Expand-contract required Mandatory for both

In practice, many teams start with rolling deployments because they require less infrastructure and are easier to debug. As release frequency grows and SLA requirements increase, they switch to Blue-Green for critical services while keeping rolling for less critical background processes. Both strategies benefit from the same foundations: good health checks, clear metrics, and a tested rollback process.

Mironsoft

Zero-downtime deployments, container infrastructure, and CI/CD automation

Ready to introduce deployments with no downtime?

We analyze your current deployment process, identify points of risk, and implement Blue-Green or rolling deployments with Traefik, health checks, and automatic rollback for your container stack.

Deployment audit

Analysis of your current deployment process for risk points and sources of downtime

Strategy implementation

Blue-Green or rolling with Traefik, health checks, and automatic rollback

Monitoring setup

Prometheus metrics and automatic rollback triggers for stable releases

10. Summary

Blue-Green and rolling deployments are the two most important strategies for zero-downtime releases in the container world. Blue-Green switches traffic atomically and offers instant rollback, but requires double the capacity during deployment. Rolling replaces instances step by step with minimal extra resource demand, but allows temporary version coexistence. Both require reliable health checks as a deployment gate and clear metrics for assessing stability after the release.

Choosing the right deployment strategy is not the hardest decision; the hardest part is consistently implementing all the prerequisites: health checks that genuinely verify the state of the application, database migrations following the expand-contract pattern, monitoring with automatic rollback triggers, and a deployment process that is tested regularly before it is needed in an emergency. With these foundations in place, every deployment becomes a predictable routine.

Blue-Green and Rolling Deployments: The Essentials at a Glance

Blue-Green

Atomic traffic switch between two complete stacks. Instant rollback by switching back. Requires double capacity during deployment.

Rolling Deployment

Step-by-step replacement of individual instances. Low extra resource demand. Requires backward compatibility between old and new code.

Health Checks

Deployment gate for both strategies. Checks database connectivity, configuration, and external dependencies, not just HTTP 200.

Database Migrations

Expand-contract pattern for zero downtime. Additive changes first, old elements removed only after the full rollout.

11. FAQ: Blue-Green and Rolling Deployments with Docker

1Difference between Blue-Green and rolling?
Blue-Green: atomic switch between two complete stacks, instant rollback, double the resources. Rolling: step-by-step replacement of individual instances, resource efficient, temporary version coexistence.
2How fast is rollback with Blue-Green?
Milliseconds. The Blue stack keeps running, only the traffic switch gets reset. No container restart, no image pull needed.
3What must a good health check verify?
Database connectivity, external dependencies, configuration validation, and app initialization, not just HTTP 200 on /ping.
4Blue-Green without Docker Swarm?
Yes, achievable with Traefik and weighted labels even in plain Docker Compose. Switch via label update with no downtime.
5What is the expand-contract pattern?
Additive schema change first (expand), new code uses old and new in parallel (deploy), old elements removed after the full rollout (contract).
6Stop migrations running on every start?
Run them as a separate init container or a standalone deployment step before the traffic switch. Flyway/Liquibase track migration status in the database.
7When is a deployment considered stable?
After 5 to 15 minutes with error rate, response time, and business metrics within defined thresholds, not just container status healthy.
8Which strategy for Magento 2 with Docker?
Blue-Green. Long startup times (DI compilation, Opcache warmup) make rolling impractical. An atomic switch after warmup completes prevents traffic from hitting cold containers.
9How does traffic weighting work with Traefik?
Via the label traefik.http.services.<name>.loadbalancer.weight. Weight 100/0 equals a complete switch. Intermediate values enable canary releasing.
10Two domains needed for Blue-Green?
No. Both slots run under the same domain, with Traefik weights controlling the distribution. A second domain for the staging slot is optional.