Back to Home

Blue-Green deploy on Docker Compose without downtime

The article describes the implementation of blue-green deploy on Docker Compose without using Kubernetes. It covers aspects of managing web services and background workers, graceful shutdown, database migration compatibility and automation through deploy scripts.

Blue-Green deploy without Kubernetes: practice on Docker Compose
Advertisement 728x90

Blue-Green Deployment Without Kubernetes: Implementation on Docker Compose with Zero Downtime

Implementing blue-green deployment is possible even without orchestrators like Kubernetes—just Docker Compose, nginx, and a well-thought-out graceful shutdown logic will do. This approach is especially relevant for teams working on dedicated servers or virtual machines, where using a full cluster is impractical for economic or architectural reasons. The article covers the technical implementation of continuous deployment for web services and background workers, taking into account message processing specifics and state management.

Blue-Green Architecture on Docker Compose

In the basic configuration, each application is duplicated: there are two independent instances—blue and green. Both are launched as separate services in docker-compose.prod.yml, but only one receives traffic at any given time. Routing is handled by a reverse proxy (nginx in the example), which switches the upstream address during deployment. Key principle: the new instance is fully started and passes health checks before the old one is stopped.

Configuration requires explicitly specifying ports for each instance:

Google AdInline article slot
web-blue:
  ports:
    - "127.0.0.1:8002:8000"

web-green:
  ports:
    - "127.0.0.1:8003:8000"

This allows nginx to access the required instance via localhost and a specific port. This setup avoids conflicts and simplifies lifecycle management.

Managing Background Workers

Background processes (workers) that consume messages from a queue (e.g., RabbitMQ) require a special approach. Simply stopping the container will lead to message loss or reprocessing in case of non-idempotent operations. The solution is to implement graceful shutdown at the application level.

This is implemented via a singleton class with an is_accepting flag. Before processing each task, the worker checks this flag:

Google AdInline article slot
  • If is_accepting == True — the task is executed.
  • If is_accepting == False — the message is returned to the queue with requeue=True.

Upon receiving the SIGTERM signal, the flag is set to False, after which the application waits for all current tasks to complete (with a 5–10 second buffer) and then shuts down. This ensures no messages are lost and no data inconsistencies occur.

Important nuances:

  • For multi-process workers (e.g., ProcessPoolExecutor), the is_accepting state must be accessible to all child processes. Otherwise, some consumers will continue processing new tasks.
  • Non-idempotent operations require additional safeguards: reprocessing can corrupt data integrity.
  • Using a distributed flag (e.g., in Redis) complicates the architecture but may be justified in high-load systems.

Deployment Script: Step-by-Step Logic

The deployment script automates switching between instances. The logic is the same for web services and workers, but with different entry points.

Google AdInline article slot

Main stages:

  • Determining the active instance (by analyzing the nginx configuration or list of running containers).
  • Starting the inactive instance (docker compose up -d).
  • Checking the new instance's readiness via health check (up to 30 attempts with 2-second intervals).
  • Updating the nginx configuration and running nginx -s reload.
  • Waiting for all active HTTP requests to complete (usually 5 seconds).
  • Stopping the old instance (docker compose stop).

Example for a web service:

echo "server 127.0.0.1:${INACTIVE_PORT};" > "$UPSTREAM_CONF"
nginx -t && nginx -s reload
sleep 5
$COMPOSE stop web-${ACTIVE}

This sequence ensures traffic is never directed to an unprepared service, and the old instance shuts down only after full switchover.

Database Schema Compatibility and Migrations

One of the main challenges in blue-green deployment is running two versions of the application against the same database simultaneously. To avoid errors, all migrations must be backward compatible.

Recommended approach—two-stage update:

  • First deployment: Remove usage of the obsolete field/table in the code, but keep the structure in the DB. Both new and old versions work correctly.
  • Second deployment (after full traffic switch): Perform migration to remove unused schema elements.

This increases the number of operations but eliminates the risk of the old instance crashing due to schema incompatibility.

Key Points

  • Blue-green deployment on Docker Compose is possible without Kubernetes, but requires manual instance and proxy management.
  • Graceful shutdown for workers is critical—without it, messages can be lost or duplicated.
  • All DB migrations must be backward compatible; field removals require a two-stage approach.
  • Health check of the new instance is mandatory before switching traffic.
  • Nginx configuration must be updated atomically and validated with nginx -t.

— Editorial Team

Advertisement 728x90

Read Next