> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agno.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy to Railway

> Provision Postgres, deploy the app, and turn on JWT auth.

Railway is the fastest container PaaS. \$20/month gets you pretty far. The template ships scripts that provision Postgres, deploy the app on the same private network, and sync env vars. Any Docker-capable host works the same way.

## Prerequisites

* An [Agent Platform repo set up locally](/tutorials/agent-platform/setup)
* A [Railway](https://railway.app) account
* The [Railway CLI](https://docs.railway.com/cli#installing-the-cli) installed and authenticated (`railway login`)

## Step 1: Configure production environment

The deploy scripts read `.env.production` first, then fall back to `.env`. Keep separate values for local and production: different OpenAI keys with different budgets, production-only credentials, a different Slack workspace.

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

Edit `.env.production` with production values.

## Step 2: Deploy

```bash theme={null}
./scripts/railway/up.sh
```

This script:

1. Creates a Railway project for your platform.
2. Provisions a Postgres service.
3. Creates the app service and forwards the database connection vars.
4. Builds and deploys from the current directory.
5. Issues a public Railway domain.

The script prints the assigned URL right away. The domain takes a few minutes to start resolving.

## Step 3: Your first deploy will fail

Token-Based Authorization is on by default. Without a `JWT_VERIFICATION_KEY`, the app refuses to serve traffic.

This is intentional. The platform's job is to keep your data off the public web. Shipping with auth off and expecting people to add it later does not work; servers stay open, get hacked, and the data leaks.

Token-Based Auth gives you three things:

* **No public access.** The server rejects requests without a valid token.
* **Per-request identity.** The middleware parses the token and injects `user_id`, `session_id`, and custom claims into your endpoints. Every request is tied to a user and session, so data leakage is prevented.
* **Granular permissions.** A user-level token can run an agent and view its own sessions. An admin token can read everyone's sessions and test any agent.

## Step 4: Get a verification key

The default path is to let [os.agno.com](https://os.agno.com) generate the keypair for you.

<Steps>
  <Step title="Add the OS in os.agno.com">
    Click **Add OS** → **Live**, enter your Railway domain, and connect.
  </Step>

  <Step title="Enable Token-Based Authorization">
    Toggle it on. The UI generates an RSA keypair, keeps the private key, and shows you the public key.
  </Step>

  <Step title="Paste the public key into .env.production">
    ```bash theme={null}
    JWT_VERIFICATION_KEY=-----BEGIN PUBLIC KEY-----
    MIIBIjANBgkq...
    -----END PUBLIC KEY-----
    ```

    Full PEM block, no surrounding quotes.
  </Step>
</Steps>

<Note>
  Live connections to AgentOS require a Pro subscription. Use coupon `PLATFORM30` for a one-month free trial. Cancel before the trial ends to avoid being charged.
</Note>

You don't have to use os.agno.com. Generate your own RSA or EC keypair, sign tokens with the private key in your own service, and put the matching public key in `JWT_VERIFICATION_KEY`. The platform doesn't care where the key came from as long as incoming tokens verify.

## Step 5: Sync env and verify

While `.env.production` is open, point the in-cluster scheduler at your public Railway domain so cron triggers can reach AgentOS:

```bash theme={null}
# .env.production
AGENTOS_URL=https://<your-app>.up.railway.app
```

Push variables to Railway:

```bash theme={null}
./scripts/railway/env-sync.sh
```

Railway auto-deploys when env values change. Watch the logs:

```bash theme={null}
railway logs --service agent-os
```

Once you see successful requests, the AgentOS UI connects through your Railway domain and you're live.

## Auto-deploys from GitHub

By default every code update needs `./scripts/railway/redeploy.sh`. To auto-deploy on every push to `main`:

1. Open the Railway dashboard → your project → the agent-os service → **Settings**.
2. Under **Source**, click **Connect Repo** and pick your repo.
3. Set the deploy branch to `main`.

Every push to `main` now triggers a build and rolling deploy. `./scripts/railway/env-sync.sh` is still how you push env changes.

## Opting out of JWT (not recommended)

If you must run production without auth (inside a private VPC behind another auth layer), set `authorization=False` in `app/main.py` and redeploy. Keep authorization on for any deploy holding real data. Without it, anyone who guesses your Railway domain can read your sessions and run your agents.

## Scaling

The default deploy is two replicas at 4Gi memory and 2 vCPU each. Two replicas give you zero-downtime rolling deploys and basic fault tolerance. Bump `numReplicas` and `limits` up or down in `railway.json` as your usage grows.

## Operations

| Task                       | Command                           |
| -------------------------- | --------------------------------- |
| Tail logs                  | `railway logs --service agent-os` |
| Open the Railway dashboard | `railway open`                    |
| Run a command in container | `railway run <command>`           |
| Push env changes           | `./scripts/railway/env-sync.sh`   |
| Redeploy without git push  | `./scripts/railway/redeploy.sh`   |
| Stop the app               | `railway down --service agent-os` |

## Next

[Next steps →](/tutorials/agent-platform/next-steps)
