> ## 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 Dash to Railway with JWT verification.

Dash ships with three Railway scripts. Production also enables JWT verification, so the deploy needs one extra step: paste a public key from AgentOS into your env.

## Prerequisites

* A Dash repo set up locally ([setup](/tutorials/dash/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 example.env .env.production
```

Open `.env.production` and set `OPENAI_API_KEY`. Leave `JWT_VERIFICATION_KEY` empty for now. You'll add it in Step 2.

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

This script:

1. Creates a Railway project called `dash`.
2. Adds a `pgvector` service with a persistent volume at `/var/lib/postgresql/data`.
3. Creates the `dash` application service and forwards the env vars in `.env.production`.
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`, wrapped in single quotes:

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

Single quotes preserve 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 Dash service. It handles multiline values like PEM keys correctly. Safe to run repeatedly.

`railway_redeploy.sh` triggers a fresh build of the Dash service.

## Step 4: Load data into the production database

The `pgvector` service is only reachable from inside Railway's network (`pgvector.railway.internal`). Run the data scripts via SSH:

```bash theme={null}
railway ssh --service dash
# Inside the container:
python scripts/generate_data.py
python scripts/load_knowledge.py
exit
```

## Verify

```bash theme={null}
curl https://your-dash.up.railway.app/health
# {"status":"ok"}
```

Then talk to Dash through the AgentOS UI you connected in Step 2.

## Update Slack to point at production

If you set up [Slack](/tutorials/dash/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-dash.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 dash`   |
| Open the dashboard                        | `railway open`                  |
| Run a command in the container            | `railway ssh --service dash`    |
| Sync env after a `.env.production` change | `./scripts/railway_env.sh`      |
| Redeploy after a code change              | `./scripts/railway_redeploy.sh` |

## How RBAC works

Dash holds a structural boundary between company data (`public`, read-only) and agent-managed data (`dash`, read/write). RBAC adds a complementary boundary at the API: every query is scoped to a `user_id` so multi-user deployments stay isolated.

Local development sets `RUNTIME_ENV=dev` (via Docker Compose) and skips RBAC so you can iterate. Production defaults to `RUNTIME_ENV=prd` and enforces it.

See [AgentOS Security](/agent-os/security/overview) and [Authorization](/agent-os/security/authorization/overview).

## Next

[Add your own knowledge →](/tutorials/dash/next-steps)
