Production Deployment Checklist
Checklist for deploying Syntropy Journals to production on Railway.Architecture
Branch → Environment Mapping
| Branch | Environment | Gates |
|---|---|---|
main | prod | Hard (all checks must pass) |
test | test | Soft (continue-on-error for non-prod) |
dev-* | test | Soft |
Pre-Deploy Checklist
1. DNS Records (Cloudflare — syntropyhealth.bio)
Clerk Email (DKIM + SPF) — proxy OFF required
These CNAME records must have Cloudflare proxy OFF (grey cloud / DNS only) — Clerk needs direct DNS resolution for DKIM verification.| Type | Name | Target | Proxy | Status |
|---|---|---|---|---|
| CNAME | clkmail | mail.c2o0686k3n21.clerk.services | OFF | Add |
| CNAME | clk._domainkey | dkim1.c2o0686k3n21.clerk.services | OFF | Add |
| CNAME | clk2._domainkey | dkim2.c2o0686k3n21.clerk.services | OFF | Add |
Clerk Account Portal — proxy OFF for Clerk verification
| Type | Name | Target | Proxy | Status |
|---|---|---|---|---|
| CNAME | accounts | accounts.clerk.com | OFF | Verify |
Note: The Frontend API uses proxy mode (see below) — noclerk.*CNAME is needed. All Clerk JS requests route throughhttps://syntropyhealth.bio/__clerk.
2. Clerk Dashboard Setup (clerk.com)
2a. Domain & Proxy Configuration
Clerk’s Frontend API must route through your own domain so OAuth callbacks, session management, and sign-in all usesyntropyhealth.bio (not a Clerk subdomain).
Steps:
- Go to Clerk Dashboard → Domains
- In the Frontend API section, click Set proxy configuration
- Enter the proxy URL:
https://syntropyhealth.bio/__clerk - Save — Clerk will verify the proxy is reachable (must be deployed first)
- Proxy route:
syntropy_journals/app/api/routes/clerk_proxy.py - Provider config:
page_wrapper.pypassesproxy_url="/__clerk"toclerk_provider() - Activated by:
CLERK_PROXY_ENABLED=trueenv var (prod only)
2b. Path & Redirect Configuration
| Setting | Value |
|---|---|
| Sign-in URL | /sign-in |
| Sign-up URL | /sign-up |
| After sign-in redirect | /redirect |
| After sign-up redirect | /redirect |
| SSO callback | /sign-in/sso-callback |
| Account portal | accounts.syntropyhealth.bio |
2c. Social Login (OAuth)
- Google OAuth configured (minimum)
- OAuth callback URLs will route through the
/__clerkproxy automatically - Email verification enabled (after DKIM records propagate)
2d. Deployment Order for Proxy
The proxy must be live before Clerk can verify it:- First deploy the app with
CLERK_PROXY_ENABLED=true(Railway) - Then set the proxy URL in Clerk Dashboard
- Clerk verifies
https://syntropyhealth.bio/__clerkis reachable - Once verified, all Clerk JS requests route through your domain
3. GitHub Actions Secrets
Required secrets in Settings > Secrets and Variables > Actions:| Secret | Purpose | Status |
|---|---|---|
RAILWAY_TOKEN | Railway deploy token (project-scoped) | Set (2026-03-24) |
OPENAI_API_KEY | LLM + embeddings | Set |
CLERK_SECRET_KEY | Clerk auth (prod sk_live_*) | Set |
STRIPE_SECRET_KEY | Stripe payments (live) | NOT YET CONFIGURED — Stripe not onboarded |
POSTHOG_PROJECT_API_KEY | Analytics write key | Set (2026-03-24) |
REFLEX_DB_URL | Supabase prod connection string | Set |
TEST_DB_URL | Supabase test DB (integration tests) | Set |
SLACK_DEPLOY_WEBHOOK_URL | Deploy notifications | Set |
REDIS_URL | Upstash Redis for Reflex state manager | Set (2026-04-08) |
4. Railway Environment Variables
Bothsyntropy-portal and syntropy-api services need these variables.
They should mirror envs/prod + envs/base values.
App Identity:
APP_ENV=PRODFRONTEND_DEPLOY_URL=https://syntropy-portal-production.up.railway.appREFLEX_DEPLOY_URL=https://syntropy-portal-production.up.railway.appREFLEX_ACCESS_TOKEN=<from envs/prod>
CLERK_PUBLISHABLE_KEY=pk_live_*NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_*CLERK_SECRET_KEY=sk_live_*CLERK_AUTHORIZED_DOMAINS=https://syntropyhealth.bioCLERK_PROXY_ENABLED=true— routes Frontend API through/__clerkCLERK_PROXY_URL=/__clerk— (optional, this is the default)
REFLEX_DB_URL=<Supabase prod pooler URL>
VECTOR_DB_TYPE=zillizZILLIZ_CLOUD_URI=<from envs/base>ZILLIZ_CLOUD_API_KEY=<from envs/base>
OPENROUTER_API_KEY=<from envs/base>OPENAI_API_KEY=<from envs/base>
STRIPE_SECRET_KEY=<live key>STRIPE_PUBLISHABLE_KEY=<live key>STRIPE_WEBHOOK_SECRET=<live webhook signing secret>BASE_URL=https://syntropyhealth.bio
POSTHOG_PROJECT_API_KEY=<from envs/base>POSTHOG_HOST=https://us.i.posthog.com
REDIS_URL=rediss://...@open-dragon-87263.upstash.io:6379— Upstash Redis (TLS)- Enables multi-worker state sharing and session persistence
- Without this, Reflex uses disk-based state (single instance only)
- Must use Upstash or a Redis with full ACL permissions (set/get/config/subscribe)
BREVO_API_KEY=<from envs/base>
5. Stripe Setup
- Stripe account in live mode
- Products and prices created matching app subscription tiers
- Webhook endpoint configured:
https://syntropyhealth.bio/api/stripe/webhook - Webhook events subscribed:
checkout.session.completed,customer.subscription.*,invoice.* - Webhook signing secret saved to
STRIPE_WEBHOOK_SECRET
6. PostHog Setup
- Project created for production
-
POSTHOG_PROJECT_API_KEYis the write-only project API key -
POSTHOG_HOSTset tohttps://us.i.posthog.com
7. Database
- Supabase prod instance accessible
- Connection string uses pooler URL (port 6543)
- Alembic migrations up to date:
uv run alembic upgrade head - Catalog seeded:
uv run python -m syntropy_journals.app.scripts.seed_data
Deploy Procedure
Rollback
Env File Reference
| File | Committed | Purpose |
|---|---|---|
envs/template | Yes | Blank template for per-env files |
envs/template.base | Yes | Blank template for shared base |
envs/domains/_template.* | Yes | Blank templates for integration domains |
envs/base | No (gitignored) | Shared API keys across all envs |
envs/dev | No (gitignored) | Local dev overrides |
envs/test | No (gitignored) | Test environment (Clerk test instance, Supabase test DB) |
envs/prod | No (gitignored) | Production (Clerk live, Supabase prod DB) |