Skip to main content
This guide walks through deploying the Hono API server to Railway with Docker, including Redis for persistent state management.

Prerequisites

  • A Railway account
  • The ouroborai monorepo cloned locally
  • An Arbitrum RPC URL (Alchemy, Infura, or public)
  • An Anthropic API key for the agent runner
  • A funded wallet private key (ETH for gas, USDC for x402 settlement)

Deployment

1

Create a Railway project

Log into Railway and create a new project. Select Deploy from GitHub repo and connect the ouroborai repository. Railway detects the Dockerfile in the project root automatically.If you prefer manual deployment, install the Railway CLI:
npm install -g @railway/cli
railway login
railway init
2

Configure environment variables

In the Railway dashboard, navigate to your service and open the Variables tab. Add the following required variables:
PRIVATE_KEY=0xYOUR_WALLET_PRIVATE_KEY
ANTHROPIC_API_KEY=sk-ant-your-key
ARB_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY
PRIVATE_KEY controls the agent’s on-chain wallet. Use a dedicated hot wallet with limited funds — never your primary wallet.
Add optional variables for full functionality:
# Redis (provision a Redis add-on in Railway, or use an external URL)
REDIS_URL=redis://default:password@host:port

# x402 payment recipient address
PAY_TO_ADDRESS=0xYOUR_REVENUE_ADDRESS

# CORS origin for your web frontend
CORS_ORIGIN=https://app.ouroborai.com

# Disable x402 payments in staging
SKIP_PAYMENT=false

# Server port (Railway injects PORT automatically)
PORT=3000
3

Provision Redis (optional but recommended)

Redis enables persistent job queues, conversation threads, and x402 nonce tracking across server restarts. Without it, these stores fall back to in-memory and reset on each deploy.In Railway, click New Service and select Redis. Railway automatically injects REDIS_URL into your service environment.The API server uses these Redis key prefixes:
PrefixPurpose
arb:job:*Job queue entries
arb:thread:*Conversation threads
arb:nonce:*x402 nonce deduplication (24hr TTL)
arb:revenue:*Payment tracking and revenue stats
4

Review the Dockerfile

The project ships a multi-stage Dockerfile that builds all workspace packages and produces a minimal production image:
FROM oven/bun:1 AS builder
WORKDIR /app
COPY . .
RUN bun install --frozen-lockfile
RUN bun run build

FROM oven/bun:1-slim
WORKDIR /app
COPY --from=builder /app/apps/api/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["bun", "run", "dist/index.js"]
Build order matters because workspace packages have dependencies: sdk builds first, then adapters, skills, timeboost, and smart-account, and finally the api app.
@zerodev/*, permissionless, and tslib must be marked as external in the Bun build configuration to avoid bundling issues.
5

Deploy

Push to your connected branch (or trigger a deploy from the CLI):
railway up
Railway builds the Docker image, starts the container, and assigns a public URL.
6

Verify the deployment

Hit the health endpoint to confirm the server is running:
curl https://your-service.railway.app/health
Expected response:
{ "ok": true, "ts": 1709337600000 }
Also check the root endpoint for version and chain info:
curl https://your-service.railway.app/
{
  "name": "arbitrum-agent",
  "version": "0.1.0",
  "chain": "arbitrum-one",
  "chainId": 42161
}
7

Test the agent

Submit a prompt to verify end-to-end functionality:
curl -X POST https://your-service.railway.app/agent/prompt \
  -H "Content-Type: application/json" \
  -d '{"prompt": "What is my ETH balance?"}'
You should receive a 202 response with a jobId and threadId. Poll the job status endpoint to retrieve the agent’s answer:
curl https://your-service.railway.app/agent/job/JOB_ID

Troubleshooting

Railway needs the server to bind to 0.0.0.0, not localhost. The ouroborai API server already binds to 0.0.0.0 by default. Ensure the PORT environment variable matches what Railway expects (it injects this automatically).
If using Railway’s Redis add-on, ensure the REDIS_URL variable is linked to the correct service. The API server falls back to in-memory stores gracefully if Redis is unreachable, so the server will start even if Redis is misconfigured.
Verify that PAY_TO_ADDRESS is a valid Arbitrum One address and that the calling wallet has sufficient USDC. Set SKIP_PAYMENT=true for staging environments where you want to bypass payment verification.