skills/railway-deploy/SKILL.md
Deploy a monorepo to Railway with multiple services using Dockerfiles, config-as-code, and the Railway CLI/GraphQL API. Use when deploying apps to Railway, setting up Railway projects, or fixing Railway build failures.
npx skillsauth add paolomoz/skills railway-deployInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Deploy a monorepo (or single app) to Railway as separate services with Dockerfiles and config-as-code.
npm i -g @railway/clirailway login (opens browser)railway whoamiCreate a Dockerfile in each service directory. Example patterns:
Python FastAPI backend:
FROM python:3.13-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY . .
EXPOSE 8000
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
Next.js frontend:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["sh", "-c", "npm start -- -H 0.0.0.0 -p ${PORT:-3000}"]
Key rules:
0.0.0.0 (not localhost) so Railway can route traffic${PORT:-<default>} — Railway injects the PORT env varrailway.json config-as-code filesCreate a railway.json in each service directory to explicitly declare the Dockerfile builder and watch patterns. This is critical — without it, Railway defaults to Railpack and may fail.
{
"$schema": "https://railway.com/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile",
"watchPatterns": ["<service-dir>/**"]
},
"deploy": {
"restartPolicyType": "ON_FAILURE"
}
}
watchPatterns prevent changes in one service from triggering rebuilds of another.
# Create project
railway init --name <project-name>
# Add services linked to GitHub repo
railway add --service backend --repo <owner>/<repo>
railway add --service frontend --repo <owner>/<repo>
The CLI railway environment edit --service-config command for root directories is unreliable. Use the GraphQL API instead:
~/.railway/config.json (key: user.token)railway status --jsonRAILWAY_TOKEN="<token>"
ENV_ID="<environment-id>"
# Set root directory for a service
curl -s https://backboard.railway.com/graphql/v2 \
-H "Authorization: Bearer $RAILWAY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { serviceInstanceUpdate(serviceId: \"<service-id>\", environmentId: \"'"$ENV_ID"'\", input: { rootDirectory: \"<dir>\" }) }"}'
Repeat for each service. Verify with:
curl -s https://backboard.railway.com/graphql/v2 \
-H "Authorization: Bearer $RAILWAY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "query { project(id: \"<project-id>\") { services { edges { node { id name serviceInstances { edges { node { rootDirectory } } } } } } } }"}'
railway variables --service backend \
--set "KEY1=value1" \
--set "KEY2=value2"
railway variables --service frontend \
--set "BACKEND_URL=http://backend.railway.internal:8000"
Read secrets from .env files rather than hardcoding them.
railway domain --service frontend --jsonhttp://<service>.railway.internal:<port>)CORS_ORIGINS on the backend to the frontend's public domainrailway variables --service backend \
--set 'CORS_ORIGINS=["https://<frontend-domain>.up.railway.app"]'
Push to GitHub — Railway auto-builds from the linked repo. Or trigger manually:
railway redeploy --service backend --yes
railway redeploy --service frontend --yes
| Problem | Cause | Fix |
|---|---|---|
| "Error creating build plan with Railpack" | Railway not detecting Dockerfile | Add railway.json with "builder": "DOCKERFILE" and/or set RAILWAY_DOCKERFILE_PATH=Dockerfile env var |
| Root directory not set | CLI environment edit silently fails | Use GraphQL API serviceInstanceUpdate mutation instead |
| Frontend can't reach backend | Private networking misconfigured | Ensure BACKEND_URL=http://<backend-service>.railway.internal:<port> on frontend |
| CORS errors | Backend doesn't allow frontend origin | Set CORS_ORIGINS env var on backend to frontend's public domain |
| Build succeeds but container crashes | Not binding to 0.0.0.0 or wrong port | Bind to 0.0.0.0 and use ${PORT} env var |
development
Generate artistic infographics from any topic. Runs the Sumi pipeline (analyze → structure → craft prompt → generate image) entirely within Claude Code. Use when "generate infographic", "create infographic", "sumi", "make an infographic about", or "visualize topic".
tools
Implement Server-Sent Events streaming from Cloudflare Workers to browser clients with reconnection, state persistence, and progress tracking. Use when building "SSE streaming", "real-time updates", "server push", or "event streaming".
development
Audit websites by cross-referencing query indexes, sitemaps, and navigation to identify content gaps, stale pages, missing metadata, and quality issues. Use when "auditing a website", "finding content gaps", "site quality audit", or "content inventory analysis".
data-ai
Track user session context across multi-turn interactions using browser sessionStorage and server-side KV caching with TTL. Use when implementing "session tracking", "conversation context", "multi-turn sessions", or "user journey tracking".