# Deploying Right Now Practical walkthrough for a prod deploy against the current Swarm stack. Assumes infrastructure and cloud services already exist — if not, work through [`shit_deploy_cant_do.md`](./shit_deploy_cant_do.md) first. See [`README.md`](./README.md) for the reference docs that back each step. --- ## 0. Pre-flight — check local state ```bash cd honeyDueAPI-go git status # clean working tree? git log -1 --oneline # deploying this SHA ls deploy/cluster.env deploy/registry.env deploy/prod.env ls deploy/secrets/*.txt deploy/secrets/*.p8 ``` ## 1. Reconcile your envs with current defaults These two values **must** be right — the script does not enforce them: ```bash # deploy/cluster.env WORKER_REPLICAS=1 # >1 → duplicate cron jobs (Asynq scheduler is a singleton) PUSH_LATEST_TAG=false # keeps prod images SHA-pinned SECRET_KEEP_VERSIONS=3 # optional; 3 is the default ``` Decide storage backend in `deploy/prod.env`: - **Multi-replica safe (recommended):** set all four of `B2_ENDPOINT`, `B2_KEY_ID`, `B2_APP_KEY`, `B2_BUCKET_NAME`. Uploads go to B2. - **Single-node ok:** leave all four empty. Script will warn. In this mode you must also set `API_REPLICAS=1` — otherwise uploads are invisible from 2/3 of requests. ## 2. Dry run ```bash DRY_RUN=1 ./.deploy_prod ``` Confirm in the output: - `Storage backend: S3 (...)` OR the `LOCAL VOLUME` warning matches intent - `Replicas: api=3, worker=1, admin=1` (or `api=1` if local storage) - Image SHA matches `git rev-parse --short HEAD` - `Manager:` host is correct - `Secret retention: 3 versions` Fix envs and re-run until the plan looks right. Nothing touches the cluster yet. ## 3. Real deploy ```bash ./.deploy_prod ``` Do **not** pass `SKIP_BUILD=1` after code changes — the worker's health server and `MigrateWithLock` both require a fresh build. End-to-end: ~3–8 minutes. The script prints each phase. ## 4. Post-deploy verification ```bash # Stack health (replicas X/X = desired) ssh docker stack services honeydue # API smoke curl -fsS https://api./api/health/ && echo OK # Logs via Dozzle (loopback-bound, needs SSH tunnel) ssh -p -L 9999:127.0.0.1:9999 @ # Then browse http://localhost:9999 ``` What the logs should show on a healthy boot: - `api`: exactly one replica logs `Migration advisory lock acquired`, the others log `Migration advisory lock acquired` after waiting, then `released`. - `worker`: `Health server listening addr=:6060`, `Starting worker server...`, four `Registered ... job` lines. - No `Failed to connect to Redis` / `Failed to connect to database`. ## 5. If it goes wrong Auto-rollback triggers when `DEPLOY_HEALTHCHECK_URL` fails — every service is rolled back to its previous spec, script exits non-zero. Triage: ```bash ssh docker service logs --tail 200 honeydue_api ssh docker service ps honeydue_api --no-trunc ``` Manual rollback (if auto didn't catch it): ```bash ssh bash -c ' for svc in $(docker stack services honeydue --format "{{.Name}}"); do docker service rollback "$svc" done' ``` Redeploy a known-good SHA: ```bash DEPLOY_TAG= SKIP_BUILD=1 ./.deploy_prod # Only valid if that image was previously pushed to the registry. ``` ## 6. Pre-deploy honesty check Before pulling the trigger: - [ ] Tested Neon PITR restore (not just "backups exist")? - [ ] `WORKER_REPLICAS=1` — otherwise duplicate push notifications next cron tick - [ ] Cloudflare-only firewall rule on 80/443 — otherwise origin IP is on the public internet - [ ] If storage is LOCAL, `API_REPLICAS=1` too - [ ] Last deploy's secrets still valid (rotation hasn't expired any creds)