Skip to content

Production smoke checklist

Use this checklist after a fresh production or staging deploy. It covers Postgres, Redis, API, queue worker, CRDT merge worker, and UI — not the optional www profile.

Compose: docker/compose.prod.yml
Kubernetes / Argo CD: helm/garrison/ with httpRoute or ingress enabled — use your public API hostname instead of 127.0.0.1:8080 below.

  1. Copy docker/stack.env.example to docker/stack.env.
  2. Set secrets and URLs in docker/stack.env:
    • POSTGRES_PASSWORD and matching DB_PASSWORD
    • APP_KEY (php artisan key:generate --show from a local API container or dev install)
    • APP_URL, FRONTEND_URL, SESSION_DOMAIN, SANCTUM_STATEFUL_DOMAINS, and CORS_ALLOWED_ORIGINS for your public hostnames
  3. Pull or build API and UI images; registry defaults are in docker/compose.prod.yml.

From the repository root:

Terminal window
docker compose -f docker/compose.prod.yml --env-file docker/stack.env up -d postgres redis migrate api queue ui

The one-shot migrate service runs php artisan migrate --force before api and queue start. Wait until migrate exits successfully and api is running:

Terminal window
docker compose -f docker/compose.prod.yml --env-file docker/stack.env ps

Do not run php artisan db:seed in production. Seeders are for local development only (DevSeeder sample users and studies).

Replace the host and port with your API URL (Compose default binds API to 127.0.0.1:${GARRISON_API_PORT:-8080}):

Terminal window
curl -fsS "http://127.0.0.1:8080/up"

Expect HTTP 200 and a JSON body indicating the application is up.

Sanctum session cookies require CSRF for browser clients. For a quick smoke test from the shell, use the GraphQL login mutation (reCAPTCHA is optional when RECAPTCHA_SECRET_KEY is empty).

Create a user first if the database is empty — register via the UI at your FRONTEND_URL, or use GraphQL register with a strong password.

Terminal window
API="http://127.0.0.1:8080"
COOKIE_JAR="$(mktemp)"
# CSRF cookie (Sanctum SPA flow)
curl -fsS -c "$COOKIE_JAR" "$API/sanctum/csrf-cookie" >/dev/null
# Login — replace email and password
curl -fsS -b "$COOKIE_JAR" -c "$COOKIE_JAR" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H "X-XSRF-TOKEN: $(grep XSRF-TOKEN "$COOKIE_JAR" | awk '{print $7}' | python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))')" \
-d '{"query":"mutation { login(input: { email: \"you@example.com\", password: \"your-password\" }) { user { id email } } }"}' \
"$API/graphql"

Expect "errors": null and a data.login.user.id in the response.

Optional: confirm the session with query { me { id email } } using the same cookie jar.

Requires an authenticated user who owns a note. After registering, create a study and note in the UI (or use GraphQL createStudy / note mutations), then push an empty or append change:

Terminal window
NOTE_ID="1" # replace with your note id
CHANGES="" # base64 CRDT payload; empty string is valid for an initialized note
curl -fsS -b "$COOKIE_JAR" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H "X-XSRF-TOKEN: $(grep XSRF-TOKEN "$COOKIE_JAR" | awk '{print $7}' | python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))')" \
-d "{\"query\":\"mutation { pushNoteUpdates(noteId: \\\"$NOTE_ID\\\", changes: \\\"$CHANGES\\\") { noteId crdtVersion } }\"}" \
"$API/graphql"

Expect GraphQL success with a noteId and incremented crdtVersion. Pull sync:

Terminal window
curl -fsS -b "$COOKIE_JAR" \
-H 'Content-Type: application/json' \
-d "{\"query\":\"query { pullNoteUpdates(noteId: \\\"$NOTE_ID\\\") { noteId unchanged crdtVersion } }\"}" \
"$API/graphql"

For CI and local development, run the API test suite in Docker instead of against production data:

Terminal window
docker compose -f compose.dev.yml run --rm --no-deps api php artisan test --filter=AuthTest
docker compose -f compose.dev.yml run --rm --no-deps api php artisan test --filter=NoteCrdtTest

When the API is behind a Gateway or ingress, set API to your public URL (for example https://api.garrisonbible.com) in the commands below. Ensure the Argo CD sync completed, API pods are ready, and the migrate init container succeeded on the current image:

Terminal window
kubectl get pods -n garrison
kubectl logs -n garrison deploy/garrison-api -c migrate

Do not run php artisan db:seed in production on any deployment method.