Skip to main content

Production cutover — api.drape.to → ECS/RDS (2026-08-31)

This is the operational record of executing the ADR-0002 prod cutover. api.drape.to now serves from the ECS Fargate prod stack over a valid ACM certificate. The Lightsail box was already gone before this work; the trigger was Kenji reporting the landing-page sign-up (waitlist) was erroring — root cause was that api.drape.to no longer resolved.

Result

https://api.drape.to is live: /api/status200 {"status":"ok","database":"postgresql"} with a valid cert, /api/health → healthy (redis ok, postgresql ok, fal + fashn configured), and the LP waitlist POST /api/marketing/waitlist returns success from https://drape.to.

What changed

POST /api/marketing/waitlist

host api.drape.to

drape.to (Amplify LP)

api.drape.to

Route 53 alias

drape-alb (443, ACM cert)

drape-prod-tg

ECS drape-prod x1

RDS drape-postgres-prod-live
encrypted, private

PieceBeforeAfter
api.drape.to DNSno record (Lightsail gone)Route 53 alias Adrape-alb
TLSold ACM cert timed out; legacy cert was CN=staging.drape.to (iOS-ATS blocker)new ACM cert for api.drape.to + dev-api.drape.to, issued and attached to a 443 listener
ALB listenerHTTP:80 onlyadded HTTPS:443 (TLS13-1-2), host rule api.drape.todrape-prod-tg
drape-prod servicedesired=0 (off)desired=1, healthy on task def drape-prod:2
prod DBdrape-postgres-prod (never turned on, contents unverified)drape-postgres-prod-live — restored from the live data
prod task def secrets3 keys injected (DATABASE_URL, JWT_SECRET_KEY, ADMIN_PASSWORD)14 keys injected (adds FAL, FASHN, Supabase, Stripe, Upstash, OpenAI, Sentry)

Procedure executed

  1. ACM. Old cert had VALIDATION_TIMED_OUT. Requested a fresh DNS-validated cert (api.drape.to + dev-api.drape.to); added the two validation CNAMEs in Route 53; cert ISSUED.
  2. Safety. Manual pre-cutover snapshots of both RDS instances (drape-postgres and drape-postgres-prod).
  3. Live-data restore. Encrypt-copied the drape-postgres snapshot (source was unencrypted; kept prod encrypted via the existing KMS key) and restored it to a new instance drape-postgres-prod-live — private subnets (drape-pg-subnets), same SG, not public.
  4. Repoint. Updated prod-keys DATABASE_URL to the restored instance (reusing the copied DB's working credentials), preserving all other fields.
  5. Secrets. Pulled the real SaaS keys from the old Render Drape-backend service into prod-keys (values never surfaced — names-only tooling). Registered task def drape-prod:2 injecting every non-placeholder key, then scaled drape-prod to 1.
  6. Ingress. Created the HTTPS:443 listener with the new cert + the api.drape.todrape-prod-tg rule; added the Route 53 alias.
  7. Verify. DNS resolves to the ALB; cert verifies; /api/status + /api/health healthy; waitlist write succeeds over HTTPS with Origin: https://drape.to.
Reversible by design

The other session's original drape-postgres-prod was left untouched as a fallback, and pre-cutover snapshots of both databases are retained. Rollback = point the api.drape.to alias/host-rule back, or restore from a snapshot.

Reference (us-east-1, acct 230790682930)

  • ALB drape-albdrape-alb-1305867817.us-east-1.elb.amazonaws.com (zone Z35SXDOTRQ7X7K)
  • Prod service drape-prod / target group drape-prod-tg / task def drape-prod:2 at cutover (inventory 2026-09-17 saw drape-prod:10 / drape-dev:22 on cluster drape)
  • Prod DB drape-postgres-prod-live (encrypted, private) — cutover pointed drape/backend/prod-keys DATABASE_URL here. Stage 2A did not re-read secret values; treat the host mapping as docs-supported, not API-proven from the secret payload.
  • Cert arn:aws:acm:us-east-1:230790682930:certificate/19f9816e-8896-4a4b-9a60-c03c133888ba
  • Route 53 zone drape.toZ00780563EFBECXPWSBK4

Open items

Follow-ups
  • Catalog appears empty. Prod and dev both return empty /api/products, /api/brands, /api/store/, /api/stats/active-users. Prod is a faithful copy of dev, so nothing was lost in the cutover — but confirm whether the historical catalog/users are actually in RDS or were left on the (now-gone) Lightsail box. Needs a direct row-count check.
  • 9 keys still placeholder in prod-keys: RESEND_*, MUX_*, STRIPE_PRICE_* (4), INBOUND_EMAIL_WEBHOOK_SECRET, plus Langfuse (blank in Render too). Email, video, and subscription-pricing features stay off until filled; then add them to the task def.
  • Revoke the temporary Render API key (temp-key-recovery-revoke-after) used to recover the secrets.
  • RDS drape-postgres-prod is a LEGACY CANDIDATE / rollback leftover — not approved for deletion. Stage 2A classification: drape-postgres-prod-live = ACTIVE PROD (docs-supported); drape-postgres = probable ACTIVE DEV (public; README shortcut; not secret-read proven). Do not decommission any of the three instances from this record.
Stage 2A — 2026-09-17

Live inventory confirmed cluster name drape, ALB host-header routing, and all three RDS instances still present. See the AWS system map.