> ## Documentation Index
> Fetch the complete documentation index at: https://phidatainc-agui.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 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](/tutorials/coda/setup))
* A [Railway](https://railway.app) account
* The [Railway CLI](https://docs.railway.app/guides/cli) installed and authenticated (`railway login`)

## Step 1: Provision the project

Use `.env.production` to keep production credentials separate from local dev:

```bash theme={null}
cp .env .env.production
```

Then run:

```bash theme={null}
./scripts/railway_up.sh
```

This script:

1. Creates a Railway project called `coda`.
2. Adds a `pgvector` service with a persistent volume at `/var/lib/postgresql/data`.
3. Creates the `coda` application service and forwards every env var from your shell (`OPENAI_API_KEY`, `GITHUB_ACCESS_TOKEN`, `SLACK_*`, `TRIAGE_CHANNEL`, `DIGEST_CHANNEL`, `JWT_VERIFICATION_KEY`, `DB_*`).
4. 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:

1. Open [os.agno.com](https://os.agno.com) and log in.
2. Click **Add OS** → **Live**, paste your Railway URL, click **Connect**.
3. Go to **Settings** and click **Generate key pair**.
4. Copy the **public** key.
5. Paste it into `.env.production`:

```bash theme={null}
JWT_VERIFICATION_KEY=-----BEGIN PUBLIC KEY-----
MIIBIjANBgkq...
-----END PUBLIC KEY-----
```

No quotes needed. The sync script handles the multiline PEM block.

## Step 3: Push env and redeploy

```bash theme={null}
./scripts/railway_env.sh
./scripts/railway_redeploy.sh
```

`railway_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

```bash theme={null}
curl https://your-coda.up.railway.app/health
# {"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](/tutorials/coda/connect-slack) against an ngrok URL, swap it for your Railway URL:

1. Open your Slack app at [api.slack.com/apps](https://api.slack.com/apps).
2. Go to **Event Subscriptions**.
3. Update the Request URL to `https://your-coda.up.railway.app/slack/events`.
4. 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.

1. Create a second Slack app called "Coda Dev" in your workspace using the same manifest from [Connect Slack](/tutorials/coda/connect-slack).
2. Copy your `.env` to `.env.local`. Replace `SLACK_TOKEN` and `SLACK_SIGNING_SECRET` with the dev app's values.
3. Point the Coda Dev app's Request URL at your ngrok URL.
4. Run locally with the local env file:

```bash theme={null}
docker compose --env-file .env.local up -d --build
```

Production 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.

## Next

[Add scheduled tasks →](/tutorials/coda/next-steps)
