Everything in this directory represents a user's GitOps repo — not Accelero's own configuration. Fork it (or copy it into a new repo), point an Accelero stack at it, and you get a working zero-downtime deployment end-to-end.
.envvariable interpolation —${NGINX_TAG:-nginx:1.27.1-alpine}style substitution, with:?requiredfor fields that must be set.- Caddy in front — a reverse-proxy service holds the host port; replicated backends expose only inside the Docker network. This is the pattern that makes zero-downtime actually work (see
docs/usage-overview.md#zero-downtime-deploymentsfor why). - Caddyfile committed to git — mounted into the proxy via
./Caddyfile:/etc/caddy/Caddyfile. Accelero resolves the relative path against the stack's cloned repo dir; seedocs/usage-overview.md#bind-mounts-from-the-gitops-repo. deploy.replicas—webruns three interchangeable replicas. Accelero rolling-updates them one at a time: create new, wait healthy, remove one old, repeat. At least two replicas stay serving traffic throughout.- Healthchecks that actually work —
wget --spideron an alpine nginx image (the debian ones ship neitherwgetnorcurl).
# 1. Put this directory on a git remote your Accelero can reach.
# Or, for a local smoke test, commit it to a local path Accelero can read.
# 2. Create the stack
curl -X POST http://localhost:8000/api/v1/stacks \
-H "Content-Type: application/json" \
-H "X-API-KEY: $ACCELERO_API_KEY" \
-d '{
"name": "sample",
"repo_url": "https://github.com/you/your-gitops-repo",
"compose_path": "docker-compose.yaml",
"auto_deploy": true,
"reconcile_interval_seconds": 60
}'
# 3. Trigger the first deploy
curl -X POST http://localhost:8000/api/v1/stacks/sample/deploy \
-H "X-API-KEY: $ACCELERO_API_KEY"Expected containers after the initial deploy (15-25s):
proxy_0_* caddy:2-alpine Up
web_0_* nginx:1.27.1-alpine (healthy)
web_1_* nginx:1.27.1-alpine (healthy)
web_2_* nginx:1.27.1-alpine (healthy)
curl http://localhost/ hits Caddy, which resolves web via Docker's embedded DNS and round-robins to a healthy replica.
# Terminal 1 — send real traffic for 45 seconds, count anything non-2xx.
(
t_end=$(($(date +%s) + 45))
ok=0
fail=0
while [ $(date +%s) -lt $t_end ]; do
code=$(curl -s -o /dev/null --max-time 2 -w '%{http_code}' http://127.0.0.1/)
[ "$code" = "200" ] && ok=$((ok+1)) || fail=$((fail+1))
done
echo "ok=$ok fail=$fail"
)# Terminal 2 (within the 45s window) — bump the nginx tag.
sed -i '' 's/1.27.1/1.27.2/' .env
git add . && git commit -m "bump nginx to 1.27.2"
git push
curl -X POST http://localhost:8000/api/v1/stacks/sample/deploy \
-H "X-API-KEY: $ACCELERO_API_KEY"The web service is rolling-replaced one replica at a time. Caddy keeps serving from whichever replicas are healthy. Expected output from terminal 1:
ok=5580 fail=0
(Actual measurement from a local run: 5580 requests over 45s, all 200s, while rolling from nginx:1.27.2-alpine → nginx:1.27.3-alpine across 3 replicas. Rolling update took ~19s of that window.)
The proxy runs caddy run --config /etc/caddy/Caddyfile and mounts ./Caddyfile from this repo as the source of that config. Accelero resolves the relative path against the stack's cloned repo directory on every deploy.
What happens when you change the Caddyfile only (no compose change): The next deploy re-clones the repo — the new Caddyfile is on disk at the expected path. But because nothing in the proxy's compose spec changed, Accelero does not recreate the container, so Caddy keeps running with its original in-memory config. To pick up the new config, change something in the proxy's compose spec (bump a label, tweak an env var) — that will force a recreate, and the new Caddy will read the updated file at start.
For richer Caddy features — TLS, multiple hosts, rate limits, global settings — extend the Caddyfile. It's just a regular Caddyfile; nothing here constrains what you can put in it.
Single-container services with static host ports (ports: "80:80", no deploy.replicas) cannot be zero-downtime deployed under Docker — the host port can only be held by one container at a time. An image bump for such a service either fails with a port conflict or has a measurable outage during container swap. If you need zero-downtime for an externally-reachable service, put a proxy (Caddy, Traefik, nginx — your choice) in front like this sample does, and let the backend use expose: only.