| name | deploy-railway |
| description | Deploy the Hyperindex frontend and backend to Railway. Use this skill when the user asks to deploy, redeploy, or update the production services on Railway. |
Deploy Hyperindex to Railway
Project Layout
Hyperindex is a monorepo with two Railway services:
| Service | Source | Dockerfile | Railway Name |
|---|
| Backend (Go) | repo root / | Dockerfile | backend |
| Frontend (Next.js) | client/ | client/Dockerfile | frontend |
Custom Domains
| Service | Domain |
|---|
| Backend | https://api.hi.gainforest.app |
| Frontend | https://hi.gainforest.app |
Legacy domains (still active): backend-production-95a22.up.railway.app, frontend-production-dcce.up.railway.app
Prerequisites
- Railway CLI v4+ installed and logged in (
railway whoami)
- Linked to project:
railway status should show project hyperindex
- On the correct git branch (typically
tap-feature)
Deploy Backend
The backend deploys from the repo root using the root Dockerfile:
railway up -s backend -d
This uploads the entire repo, builds the Go binary in Docker, and deploys it. Takes ~3-5 minutes.
Verify backend:
curl -s https://api.hi.gainforest.app/
Deploy Frontend
CRITICAL: The frontend MUST use --path-as-root to avoid Railway picking up the root Go Dockerfile:
railway up --path-as-root client/ -s frontend -d
This makes client/ the archive root so Railway only sees client/Dockerfile (the Next.js build). Takes ~3-5 minutes.
Why --path-as-root?
Without it, railway up uploads the entire monorepo and Railway finds the root Dockerfile (Go backend) instead of client/Dockerfile (Next.js frontend). This causes the frontend service to run the Go binary instead of the Next.js app.
Verify frontend:
curl -s -o /dev/null -w "%{http_code}" https://hi.gainforest.app/
curl -s https://hi.gainforest.app/ | grep -o '<title>[^<]*</title>'
Deploy Both Services
railway up -s backend -d
railway up --path-as-root client/ -s frontend -d
Environment Variables
Backend (backend service)
| Variable | Value |
|---|
HOST | 0.0.0.0 |
PORT | 8080 |
DATABASE_URL | sqlite:/app/data/hyperindex.db |
EXTERNAL_BASE_URL | https://api.hi.gainforest.app |
ADMIN_API_KEY | (must match frontend HYPERINDEX_ADMIN_API_KEY for proxied admin requests) |
ADMIN_DIDS | did:plc:qc42fmqqlsmdq7jiypiiigww (daviddao.org) |
OAUTH_LOOPBACK_MODE | true |
SECRET_KEY_BASE | (set on Railway, do not change) |
Frontend (frontend service)
| Variable | Value |
|---|
PORT | 3000 |
NEXT_PUBLIC_CLIENT_URL | https://hi.gainforest.app |
NEXT_PUBLIC_HYPERINDEX_URL | https://api.hi.gainforest.app |
HYPERINDEX_URL | https://api.hi.gainforest.app |
HYPERINDEX_ADMIN_API_KEY | (must match backend ADMIN_API_KEY; used by the Next.js admin proxy only) |
COOKIE_SECRET | (set on Railway, do not change) |
ATPROTO_JWK_PRIVATE | (ES256 JWK, set on Railway, do not change) |
Note: NEXT_PUBLIC_HYPERINDEX_URL is a build-time variable (inlined by Next.js during npm run build). HYPERINDEX_ADMIN_API_KEY is server-side only and must be set as a runtime variable on the frontend service.
Troubleshooting
Frontend shows Go JSON response instead of HTML
You forgot --path-as-root client/. Redeploy with:
railway up --path-as-root client/ -s frontend -d
"Application not found" on custom domain
SSL certificate is still provisioning. Wait 5-15 minutes after adding DNS records.
GraphiQL returns 500 through frontend
GraphiQL is served directly by the backend. The frontend has a /graphiql server-side redirect route that redirects to https://api.hi.gainforest.app/graphiql.
OAuth login fails
Check that ATPROTO_JWK_PRIVATE and NEXT_PUBLIC_CLIENT_URL are set on the frontend service. Generate a new JWK with:
node scripts/generate-jwk.js
"admin privileges required" after login
Ensure the frontend HYPERINDEX_ADMIN_API_KEY matches the backend ADMIN_API_KEY. Without matching keys, the backend ignores the X-User-DID header from the Next.js proxy.
Setting Environment Variables
railway variables set 'KEY=value' -s backend
railway variables set 'KEY=value' -s frontend
railway variables -s backend
railway variables -s frontend
After changing env vars, redeploy the affected service.