|
| 1 | +--- |
| 2 | +name: deploy-railway |
| 3 | +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. |
| 4 | +--- |
| 5 | + |
| 6 | +# Deploy Hyperindex to Railway |
| 7 | + |
| 8 | +## Project Layout |
| 9 | + |
| 10 | +Hyperindex is a monorepo with two Railway services: |
| 11 | + |
| 12 | +| Service | Source | Dockerfile | Railway Name | |
| 13 | +|---------|--------|------------|-------------| |
| 14 | +| **Backend** (Go) | repo root `/` | `Dockerfile` | `backend` | |
| 15 | +| **Frontend** (Next.js) | `client/` | `client/Dockerfile` | `frontend` | |
| 16 | + |
| 17 | +## Custom Domains |
| 18 | + |
| 19 | +| Service | Domain | |
| 20 | +|---------|--------| |
| 21 | +| Backend | `https://api.hi.gainforest.app` | |
| 22 | +| Frontend | `https://hi.gainforest.app` | |
| 23 | + |
| 24 | +Legacy domains (still active): `backend-production-95a22.up.railway.app`, `frontend-production-dcce.up.railway.app` |
| 25 | + |
| 26 | +## Prerequisites |
| 27 | + |
| 28 | +- Railway CLI v4+ installed and logged in (`railway whoami`) |
| 29 | +- Linked to project: `railway status` should show project `hyperindex` |
| 30 | +- On the correct git branch (typically `tap-feature`) |
| 31 | + |
| 32 | +## Deploy Backend |
| 33 | + |
| 34 | +The backend deploys from the repo root using the root `Dockerfile`: |
| 35 | + |
| 36 | +```bash |
| 37 | +railway up -s backend -d |
| 38 | +``` |
| 39 | + |
| 40 | +This uploads the entire repo, builds the Go binary in Docker, and deploys it. Takes ~3-5 minutes. |
| 41 | + |
| 42 | +### Verify backend: |
| 43 | +```bash |
| 44 | +curl -s https://api.hi.gainforest.app/ |
| 45 | +# Should return: {"name":"Hyperindex","version":"0.1.0-dev",...} |
| 46 | +``` |
| 47 | + |
| 48 | +## Deploy Frontend |
| 49 | + |
| 50 | +**CRITICAL:** The frontend MUST use `--path-as-root` to avoid Railway picking up the root Go Dockerfile: |
| 51 | + |
| 52 | +```bash |
| 53 | +railway up --path-as-root client/ -s frontend -d |
| 54 | +``` |
| 55 | + |
| 56 | +This makes `client/` the archive root so Railway only sees `client/Dockerfile` (the Next.js build). Takes ~3-5 minutes. |
| 57 | + |
| 58 | +### Why `--path-as-root`? |
| 59 | + |
| 60 | +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. |
| 61 | + |
| 62 | +### Verify frontend: |
| 63 | +```bash |
| 64 | +curl -s -o /dev/null -w "%{http_code}" https://hi.gainforest.app/ |
| 65 | +# Should return: 200 |
| 66 | + |
| 67 | +# Verify it's actually Next.js (not the Go server): |
| 68 | +curl -s https://hi.gainforest.app/ | grep -o '<title>[^<]*</title>' |
| 69 | +# Should return: <title>Hyperindex</title> |
| 70 | +``` |
| 71 | + |
| 72 | +## Deploy Both Services |
| 73 | + |
| 74 | +```bash |
| 75 | +# Backend (from repo root) |
| 76 | +railway up -s backend -d |
| 77 | + |
| 78 | +# Frontend (with path-as-root) |
| 79 | +railway up --path-as-root client/ -s frontend -d |
| 80 | +``` |
| 81 | + |
| 82 | +## Environment Variables |
| 83 | + |
| 84 | +### Backend (`backend` service) |
| 85 | +| Variable | Value | |
| 86 | +|----------|-------| |
| 87 | +| `HOST` | `0.0.0.0` | |
| 88 | +| `PORT` | `8080` | |
| 89 | +| `DATABASE_URL` | `sqlite:/app/data/hypergoat.db` | |
| 90 | +| `EXTERNAL_BASE_URL` | `https://api.hi.gainforest.app` | |
| 91 | +| `TRUST_PROXY_HEADERS` | `true` | |
| 92 | +| `ADMIN_DIDS` | `did:plc:qc42fmqqlsmdq7jiypiiigww` (daviddao.org) | |
| 93 | +| `OAUTH_LOOPBACK_MODE` | `true` | |
| 94 | +| `SECRET_KEY_BASE` | *(set on Railway, do not change)* | |
| 95 | + |
| 96 | +### Frontend (`frontend` service) |
| 97 | +| Variable | Value | |
| 98 | +|----------|-------| |
| 99 | +| `PORT` | `3000` | |
| 100 | +| `PUBLIC_URL` | `https://hi.gainforest.app` | |
| 101 | +| `NEXT_PUBLIC_API_URL` | `https://api.hi.gainforest.app` | |
| 102 | +| `HYPERINDEX_URL` | `https://api.hi.gainforest.app` | |
| 103 | +| `COOKIE_SECRET` | *(set on Railway, do not change)* | |
| 104 | +| `ATPROTO_JWK_PRIVATE` | *(ES256 JWK, set on Railway, do not change)* | |
| 105 | + |
| 106 | +**Note:** `NEXT_PUBLIC_API_URL` is a build-time variable (inlined by Next.js during `npm run build`). The `client/Dockerfile` declares `ARG NEXT_PUBLIC_API_URL` so Railway passes it during Docker build. |
| 107 | + |
| 108 | +## Troubleshooting |
| 109 | + |
| 110 | +### Frontend shows Go JSON response instead of HTML |
| 111 | +You forgot `--path-as-root client/`. Redeploy with: |
| 112 | +```bash |
| 113 | +railway up --path-as-root client/ -s frontend -d |
| 114 | +``` |
| 115 | + |
| 116 | +### "Application not found" on custom domain |
| 117 | +SSL certificate is still provisioning. Wait 5-15 minutes after adding DNS records. |
| 118 | + |
| 119 | +### GraphiQL returns 500 through frontend |
| 120 | +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`. |
| 121 | + |
| 122 | +### OAuth login fails |
| 123 | +Check that `ATPROTO_JWK_PRIVATE` and `PUBLIC_URL` are set on the frontend service. Generate a new JWK with: |
| 124 | +```bash |
| 125 | +node scripts/generate-jwk.js # (in hyperscan repo, or client/scripts/ if copied) |
| 126 | +``` |
| 127 | + |
| 128 | +### "admin privileges required" after login |
| 129 | +Ensure `TRUST_PROXY_HEADERS=true` is set on the backend. Without it, the backend ignores the `X-User-DID` header from the Next.js proxy. |
| 130 | + |
| 131 | +## Setting Environment Variables |
| 132 | + |
| 133 | +```bash |
| 134 | +# Set a variable on a service |
| 135 | +railway variables set 'KEY=value' -s backend |
| 136 | +railway variables set 'KEY=value' -s frontend |
| 137 | + |
| 138 | +# View all variables for a service |
| 139 | +railway variables -s backend |
| 140 | +railway variables -s frontend |
| 141 | +``` |
| 142 | + |
| 143 | +After changing env vars, redeploy the affected service. |
0 commit comments