Deploy to Railway
Ship Coda to Railway with RBAC enforcement on.
Coda ships with three Railway scripts. Production also enables RBAC, so the deploy needs one extra step: paste a public key from AgentOS into your env.
Prerequisites
- A Coda repo set up locally (setup)
- A Railway account
- The Railway CLI installed and authenticated (
railway login)
Step 1: Provision the project
Use .env.production to keep production credentials separate from local dev:
1cp .env .env.productionThen run:
1./scripts/railway_up.shThis script:
- Creates a Railway project called
coda. - Adds a
pgvectorservice with a persistent volume at/var/lib/postgresql/data. - Creates the
codaapplication service and forwards every env var from your shell (OPENAI_API_KEY,GITHUB_ACCESS_TOKEN,SLACK_*,TRIAGE_CHANNEL,DIGEST_CHANNEL,JWT_VERIFICATION_KEY,DB_*). - Deploys the app and assigns a public domain.
Copy the Railway domain from the output. The app will crash-loop until Step 2 lands. That's expected. Production rejects all requests until a JWT_VERIFICATION_KEY is set.
Step 2: Get a JWT key from AgentOS
The key comes from AgentOS:
- Open os.kern.ndx.rocks and log in.
- Click Add OS → Live, paste your Railway URL, click Connect.
- Go to Settings and click Generate key pair.
- Copy the public key.
- Paste it into
.env.production:
1JWT_VERIFICATION_KEY=-----BEGIN PUBLIC KEY-----2MIIBIjANBgkq...3-----END PUBLIC KEY-----No quotes needed. The sync script handles the multiline PEM block.
Step 3: Push env and redeploy
1./scripts/railway_env.sh2./scripts/railway_redeploy.shrailway_env.sh reads .env.production and pushes every variable to the Coda service. Safe to run repeatedly.
railway_redeploy.sh triggers a fresh build.
Verify
1curl https://your-coda.up.railway.app/health2# {"status":"ok"}Then talk to Coda through the AgentOS UI you connected in Step 2.
Update Slack to point at production
If you set up Slack against an ngrok URL, swap it for your Railway URL:
- Open your Slack app at api.slack.com/apps.
- Go to Event Subscriptions.
- Update the Request URL to
https://your-coda.up.railway.app/slack/events. - Wait for the green Verified check, then Save Changes.
You can stop ngrok now. Slack delivers events to Railway from now on.
Operations
| Task | Command |
|---|---|
| Tail logs | railway logs --service coda |
| Open the dashboard | railway open |
| Run a command in the container | railway ssh --service coda |
Sync env after a .env.production change | ./scripts/railway_env.sh |
| Redeploy after a code change | ./scripts/railway_redeploy.sh |
Adjust CPU, memory, and replicas in railway.json.
Run local and production in parallel
If Coda is already deployed and you want to keep iterating locally, you'll need a separate Slack app. Each Slack app delivers events to one URL.
- Create a second Slack app called "Coda Dev" in your workspace using the same manifest from Connect Slack.
- Copy your
.envto.env.local. ReplaceSLACK_TOKENandSLACK_SIGNING_SECRETwith the dev app's values. - Point the Coda Dev app's Request URL at your ngrok URL.
- Run locally with the local env file:
1docker compose --env-file .env.local up -d --buildProduction keeps using .env.production and the production Slack app. Local uses .env.local and Coda Dev. Both work independently.
Add .env.local to .gitignore if it isn't already.