WIUNIX ACADEMY
Docker Compose blue/green: health gates, proxy switch and rollback
Page 2 of 3. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment5 editable values
LET’S BUILD
Pin the runtime, create green’s network and start blue
The recorded multi-platform manifest-index digest was reviewed from the Docker Official Image metadata on 2026-11-05: stable Nginx 1.30.6 on Alpine. The stable-alpine tag is descriptive; the digest fixes the selected content even after that tag moves. Pulling and inspecting the digest is a reader action, not a claim that the author ran the image on every OS/architecture.
Start with the blue route hard-linked as active.conf. The complete proxy directory is mounted, so later rename changes are visible; a single-file bind mount could retain an old inode. Validate both Compose documents, prepare green’s inactive container/network, then start blue with its gateway. --wait only evaluates configured health/running state, so verify the actual routed release afterward.
Disposable Linux lab VM; same authorized Bash session
sudo ln -- '/srv/academy-bluegreen-demo/proxy/versions/blue.route' '/srv/academy-bluegreen-demo/proxy/active.conf' || exit 1
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' config --quiet || exit 1
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' config --quiet || exit 1
docker_lab pull 'nginx:stable-alpine@sha256:0e52bd054126cdc040b1276c0af347aeb02a41b9cfe305d6094767ac30e97949' || exit 1
docker_lab image inspect 'nginx:stable-alpine@sha256:0e52bd054126cdc040b1276c0af347aeb02a41b9cfe305d6094767ac30e97949' --format '{{json .RepoDigests}}' || exit 1
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' create || exit 1
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' up -d --wait --wait-timeout 45 || exit 1
marker=$(curl --noproxy '*' --fail --silent --show-error --max-time 3 'http://127.0.0.1:18080/release') || exit 1
test "$marker" = BLUE || exit 1
printf 'Gateway release: %s\n' "$marker"Both documents validate, green remains not started, blue/gateway health gates pass and the gateway returns BLUE. An error leaves a partial lab to inspect.
LET’S BUILD
Start green and test readiness across the proxy’s network
Keep blue and the gateway running. Start only the green project, wait for its own healthcheck, then request the release marker directly on its loopback port and from the gateway container through green-app. The latter catches a missing external-network attachment or wrong alias that a local healthcheck cannot see.
These tiny checks establish only this demo’s HTTP readiness. A dependency becoming unhealthy later does not automatically switch Nginx back, and a healthcheck cannot prove migration compatibility, correct writes or safe retries. Do not route real traffic merely because Compose reports healthy.
Disposable Linux lab VM; same authorized Bash session
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' up -d --wait --wait-timeout 45 || exit 1
marker=$(curl --noproxy '*' --fail --silent --show-error --max-time 3 'http://127.0.0.1:18082/release') || exit 1
test "$marker" = GREEN || exit 1
marker=$(docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway wget -T 3 -q -O - http://green-app/release) || exit 1
test "$marker" = GREEN || exit 1Green is healthy and returns GREEN both directly and from the existing gateway network; blue remains available for rollback.
LET’S BUILD
Select green on disk, test Nginx and request a reload
No concurrent operator edits this exclusive fixture. A new hard link and same-directory rename replace active.conf atomically on disk while preserving the already reviewed file’s SELinux label. Disk selection is not yet the running proxy state. Nginx -t checks the selected configuration before a reload is requested.
If validation fails, the panel restores blue on disk and exits without reloading. If the reload command fails after validation, keep both slots running and use the rollback section; disk and running state may differ. A successful signal is still not proof that every request already reaches green. The next section verifies fresh requests.
Disposable Linux lab VM; same authorized Bash session
sudo ln -- '/srv/academy-bluegreen-demo/proxy/versions/green.route' '/srv/academy-bluegreen-demo/proxy/active.conf.next' || exit 1
sudo mv -T -- '/srv/academy-bluegreen-demo/proxy/active.conf.next' '/srv/academy-bluegreen-demo/proxy/active.conf' || exit 1
if ! docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -t; then
sudo ln -- '/srv/academy-bluegreen-demo/proxy/versions/blue.route' '/srv/academy-bluegreen-demo/proxy/active.conf.restore' || exit 1
sudo mv -T -- '/srv/academy-bluegreen-demo/proxy/active.conf.restore' '/srv/academy-bluegreen-demo/proxy/active.conf' || exit 1
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -t
exit 1
fi
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -s reload || exit 1The green selection validates and reload is requested, or blue is restored on disk after validation failure. Routed success is checked separately.
LET’S BUILD
Verify fresh routed requests and retain the old slot
Run five separate requests with Connection: close and require the exact GREEN marker. A failed or old-color response is not accepted as a successful switch. Inspect the proxy log and current selection; allow a bounded observation of the reload before repeating these same checks rather than destroying blue.
Fresh responses demonstrate only these five HTTP requests. They do not drain existing keep-alive/WebSocket streams or background work. Keep the old slot and immutable before-route until the application-specific rollback/drain window ends. This lab contains no long-lived work and deliberately supplies no generic production decommission command.
Disposable Linux lab VM; same authorized Bash session
for sample in 1 2 3 4 5; do
marker=$(curl --noproxy '*' --fail --silent --show-error --max-time 3 --header 'Connection: close' 'http://127.0.0.1:18080/release') || exit 1
printf 'sample=%s release=%s\n' "$sample" "$marker"
test "$marker" = GREEN || exit 1
sleep 1
done
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' logs --tail 30 gatewayFive fresh gateway requests return GREEN; both slots remain running and the rollback template remains available.
LET’S BUILD
Restore blue with its own health and routing checks
This is a stateless route rollback, not a data rollback. First confirm blue still serves BLUE directly. Select the preserved blue template, validate and reload the proxy, then require fresh gateway responses to return BLUE. If blue itself is unhealthy, stop and investigate; switching to a broken old slot is not recovery.
The hard-link selection refuses a leftover .next file instead of overwriting it. Inspect any such file from a failed attempt and reconcile exactly that path before retrying. Do not recreate the blue Compose project to reload only the gateway: an up operation could also replace containers or change network identity.
Disposable Linux lab VM; same authorized Bash session
marker=$(curl --noproxy '*' --fail --silent --show-error --max-time 3 'http://127.0.0.1:18081/release') || exit 1
test "$marker" = BLUE || exit 1
sudo ln -- '/srv/academy-bluegreen-demo/proxy/versions/blue.route' '/srv/academy-bluegreen-demo/proxy/active.conf.next' || exit 1
sudo mv -T -- '/srv/academy-bluegreen-demo/proxy/active.conf.next' '/srv/academy-bluegreen-demo/proxy/active.conf' || exit 1
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -t || exit 1
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -s reload || exit 1
for sample in 1 2 3; do
marker=$(curl --noproxy '*' --fail --silent --show-error --max-time 3 --header 'Connection: close' 'http://127.0.0.1:18080/release') || exit 1
test "$marker" = BLUE || exit 1
sleep 1
doneThe old slot is independently reachable and three fresh gateway requests return BLUE after the checked reload.
Follow the failed gate rather than forcing the switch
Use the matching read-only diagnostic. Keep both projects and their files while investigating. Do not disable SELinux/AppArmor, change Docker’s firewall backend, delete an existing listener or invoke a system-wide prune.
Compose is missing or an older launcher is selected
Check the selected package and plugin through the same local administrative helper. Do not fall back to a legacy docker-compose command, replace a running daemon or use a remote context to make the example pass.
docker_lab version
docker_lab compose version
docker_lab compose up --helpThe gateway cannot attach to green’s network
The green project create step must have succeeded before blue starts. Inspect that named project and network ownership; never create a guessed external network over someone else’s name or connect the proxy to the host network.
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' ps -a
docker_lab network inspect 'academybg-green_default'Green stays unhealthy or direct requests fail
Inspect the candidate container, logs and host bind port. Health here depends on the marker file and its bind-mount access. On SELinux hosts, confirm the fresh directory’s label and relevant denial with the host owner; do not apply relabeling to an application directory.
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' ps -a
docker_lab compose -p 'academybg-green' -f '/srv/academy-bluegreen-demo/green.yml' logs --tail 40 green
sudo ls -ldZ '/srv/academy-bluegreen-demo/green'
ss -H -ltn '( sport = :18082 )'Healthy green exists but gateway serves blue or 502
Read both disk selection and proxy diagnostics. A healthy local endpoint does not prove external-network reachability, DNS resolution or that a reload loaded the new route. Preserve blue and use the explicit rollback if the intended switch cannot be verified.
sudo sha256sum '/srv/academy-bluegreen-demo/proxy/active.conf' '/srv/academy-bluegreen-demo/proxy/versions/blue.route' '/srv/academy-bluegreen-demo/proxy/versions/green.route'
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' exec -T gateway nginx -t
docker_lab compose -p 'academybg-blue' -f '/srv/academy-bluegreen-demo/blue.yml' logs --tail 40 gateway