Skip to content

Deploy Runbook

⚠️ Operators only — this is not a page for RP integration developers. It is for the ops team that directly deploys and rolls back the logi-server / docs-site / logi-db Render instances.

📌 Placeholder notation: The <LOGI_WEB_SERVICE_ID> / <LOGI_DOCS_SERVICE_ID> / <LOGI_PG_INSTANCE_ID> on this page are placeholders that mask the real Render resource IDs. Look up the real values in the ops team's internal runbook or in the Render Dashboard → Service settings. (This follows the policy for the publicly accessible docs subdomain — 2026-05-15.)

Service inventory

ResourceIDautoDeployNotes
logi-server (web)<LOGI_WEB_SERVICE_ID>manualstandard plan, Singapore, https://logi-server.onrender.comapi.1pass.dev
logi-docs (static)<LOGI_DOCS_SERVICE_ID>automatic (push main)VitePress build
logi-db (postgres)<LOGI_PG_INSTANCE_ID>basic_256mb, PG 18

(Live values were verified via the Render API get_service / get_postgres — 2026-05-15.)

There is no worker — with SOLID_QUEUE_IN_PUMA=true, the dispatcher and worker run inside the web process. (We reconfirmed on 2026-05-15 via list_services that no separate logi-solidqueue service exists in prod. The worker entry in server/render.yaml was removed at the same time.)

Deploy triggers

docs-site

A push to origin/main is picked up by Render immediately → VitePress build → publish. No separate action required.

logi-server (manual)

Render dashboard → service → Manual Deploy → click "Deploy latest commit". Or via the API:

bash
curl -X POST "https://api.render.com/v1/services/<LOGI_WEB_SERVICE_ID>/deploys" \
  -H "Authorization: Bearer $RENDER_API_KEY"

Check the deployed commit:

bash
curl -s "https://api.render.com/v1/services/<LOGI_WEB_SERVICE_ID>/deploys?limit=5" \
  -H "Authorization: Bearer $RENDER_API_KEY" | jq '.[].deploy | {id, commit: .commit.id, status, createdAt}'

Pre-deploy checks

Locally, before deploying:

bash
cd server
bundle exec rubocop --parallel
bundle exec rspec
bundle exec brakeman --quiet -q -w2
bin/rails zeitwerk:check

CI (.github/workflows/ci.yml) runs the same checks — only green PRs get merged to main.

Migrations

⚠️ Render does not run migrations automatically. Run them manually over SSH right after the deploy:

bash
ssh -o StrictHostKeyChecking=no \
  <LOGI_WEB_SERVICE_ID>@<RENDER_SSH_HOST> \
  "cd /opt/render/project/src/server && \
   /opt/render/project/.gems/bin/bundle exec rails db:migrate RAILS_ENV=production 2>&1"
  • Ignore the client_global_hostkeys_prove_confirm warning.
  • Destructive migrations (dropping a column, etc.) go in a separate PR and a separate step before the deploy — let the schema sit for one cycle so that both code and schema can read stale rows, then drop in the next PR.

Post-deploy checks

Within 5 minutes of the deploy + migration:

bash
# 1. health (same as Render's healthCheckPath — the Rails health controller)
curl -i https://api.1pass.dev/healthz                       # expect 200 OK
# 2. /up returns the same result — the standard Rails health probe (load-balancer/uptime-monitor friendly)
curl -i https://api.1pass.dev/up                            # expect 200 OK
# 3. OIDC discovery
curl -i https://api.1pass.dev/.well-known/openid-configuration   # 200 + JSON
# 4. JWKS
curl -s https://api.1pass.dev/.well-known/jwks.json | jq '.keys | length'   # >= 1

The endpoint Render probes automatically is /healthz (render.yaml healthCheckPath). /up is the standard Rails health endpoint — usable the same way for external monitors and local checks. Both endpoints are defined in routes.rb and return the same health result.

Then monitor the Render dashboard logs for 5 minutes — confirm the error rate is 0.

Rollback

Code-only rollback (when the migration is backward-compatible)

Render dashboard → Deploys tab → the last green deploy → "Rollback to this deploy".

Or via git:

bash
git revert <bad-sha> && git push origin main
# logi-server is manual, so trigger Manual Deploy again as above

When the migration is destructive

A code rollback alone is not enough — the older code that read the new column/constraint breaks. Procedure:

  1. Roll the code back to the last green deploy (procedure above).
  2. Over SSH, run rails db:rollback STEP=N RAILS_ENV=production (N = the number of migrations to revert).
  3. Check the schema_migrations state with rails db:migrate:status.

Mentally simulate these two steps before merging a destructive migration.

Environment variables

Render dashboard → service → Environment.

⚠️ When editing env vars via the API, never use the collection PUT (PUT /v1/services/{id}/env-vars). It replaces the entire set, so any omitted keys vanish (the 2026-03-30 ainote incident where 52 keys dropped to 4). Always use the individual PUT:

bash
curl -X PUT "https://api.render.com/v1/services/<LOGI_WEB_SERVICE_ID>/env-vars/MY_KEY" \
  -H "Authorization: Bearer $RENDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "new-value"}'

Changing an env var triggers an automatic Render redeploy.

Key env vars (high-impact when changed)

  • APNS_* (KEY_ID/TEAM_ID/TOPIC/ENV) — affects all push.
  • APPLE_TEAM_ID, APPLE_KEY_ID, APPLE_PRIVATE_KEY — Sign in with Apple revoke.
  • SOLID_QUEUE_IN_PUMA=true, JOB_CONCURRENCY=1 — worker integration.
  • ENFORCE_CANONICAL_RESOLUTION — ⚠️ enforces canonical resolution after a merge. Before flipping it, see the incident response §merge race.

Secret File

apns.p8 is mounted as a Render Secret File (/etc/secrets/apns.p8). To rotate the key, replace it in the dashboard's Secret Files tab + redeploy. It is not an env var, so the API above does not apply.

Identity가 제품의 신뢰를 만듭니다.