Files
simbarag/scripts/deploy.sh
ryan d8d01a5fff Zero-downtime blue-green deploys behind Caddy health checks
Split the raggr service into raggr-blue (8080) and raggr-green (8081)
behind compose profiles, add a /api/health endpoint and container
healthcheck, and add scripts/deploy.sh to build the inactive color,
wait for it to become healthy, then gracefully stop the old one.
Makefile targets resolve the active color automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-10 01:41:37 -04:00

49 lines
1.7 KiB
Bash
Executable File

#!/bin/bash
# Zero-downtime blue-green deploy.
#
# Builds the inactive color, starts it, waits for its healthcheck to pass,
# then gracefully stops the old color. Caddy health-checks both ports
# (8080 = blue, 8081 = green) and routes traffic to whichever is healthy,
# so the swap is invisible to clients.
#
# Caveats:
# - Migrations run on the new container's startup while the old one still
# serves, so they must be backward-compatible (additive changes only;
# destructive changes need a two-step deploy).
# - During the brief overlap both containers run pull-only Obsidian sync
# against the shared ./obvault mount.
set -euo pipefail
cd "$(dirname "$0")/.."
running_services=$(docker compose ps --status running --services)
if echo "$running_services" | grep -qx "raggr-blue"; then
OLD=blue NEW=green
elif echo "$running_services" | grep -qx "raggr-green"; then
OLD=green NEW=blue
else
OLD="" NEW=blue
fi
echo "==> Active: ${OLD:-none} | Deploying: $NEW"
echo "==> Building raggr-$NEW..."
docker compose --profile "$NEW" build "raggr-$NEW"
echo "==> Starting raggr-$NEW and waiting for healthy..."
if ! docker compose --profile "$NEW" up -d --wait "raggr-$NEW"; then
echo "!! raggr-$NEW failed to become healthy${OLD:+; raggr-$OLD is still serving}" >&2
docker compose --profile "$NEW" logs --tail 50 "raggr-$NEW" >&2 || true
docker compose --profile "$NEW" stop -t 5 "raggr-$NEW" || true
exit 1
fi
if [ -n "$OLD" ]; then
echo "==> raggr-$NEW healthy; stopping raggr-$OLD (30s grace for in-flight streams)..."
docker compose --profile "$OLD" stop -t 30 "raggr-$OLD"
docker compose --profile "$OLD" rm -f "raggr-$OLD"
fi
echo "==> Deploy complete: raggr-$NEW is live"