Skip to main content

railway

Use when deploying an app to Railway — pushing a repo or running railway up, provisioning a managed database, wiring env and reference variables across services, attaching a volume, or fixing a deploy that builds but will not serve. NOT Render (that is `render`), NOT Fly machines (that is `fly-io`), NOT a self-hosted PaaS on your own box (that is `coolify`).

Datos de origen

Repositorio
ericrisco/rsc-harness
Última actividad en el origen
29 de julio de 2026 a las 23:26
Idioma detectado de SKILL.md
inglés
Estrellas
110
Forks
9

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
6 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
railway
description
Use when deploying an app to Railway — pushing a repo or running railway up, provisioning a managed database, wiring env and reference variables across services, attaching a volume, or fixing a deploy that builds but will not serve. NOT Render (that is `render`), NOT Fly machines (that is `fly-io`), NOT a self-hosted PaaS on your own box (that is `coolify`).
tags
["railway","paas","deploy","services","env-vars","volumes","databases","private-networking","railway-json"]
recommends
["render","fly-io","coolify","postgresdb","db-migrations","domains-dns","deployment","docker"]
origin
risco
# Railway — ship a repo to a managed PaaS Railway is an opinionated PaaS: connect a GitHub repo or run `railway up`, and it builds your code (Railpack by default, or your Dockerfile), runs the container, networks it, and hands you a domain. You manage *services, environments, variables, volumes, and databases* — not servers, not an OS. This skill makes you fast and correct on Railway's specific surface, not generic deploy theory. ```text repo / `railway up` → Railpack (or Dockerfile) build → container runs (binds 0.0.0.0:$PORT) → variables + reference vars injected → private net (*.railway.internal) + public domain ``` ## Is Railway the right target? Settle this before touching config. If the user named the platform, honor it and route. | Want | Use | Why / route | | --- | --- | --- | | Push a repo, get a built+running app with zero ops | **Railway** (this skill) | Managed PaaS, Railpack build, by-the-minute billing | | `render.yaml` Blueprint, free static sites, their dashboard model | `render` | Different PaaS — route to the `render` skill | | `fly.toml`, Firecracker microVMs, `fly machine`, multi-region edge | `fly-io` | Railway has no `fly machine` equivalent — route to `fly-io` | | Self-host the PaaS on your own Hetzner/DO box | `coolify` | Railway is fully managed; Coolify is BYO-server | | Raw VPS, you manage the OS | `hetzner` / `digitalocean` | IaaS, not PaaS | | Platform-agnostic release strategy (rolling, blue-green theory) | `deployment` | This skill is Railway mechanics, not strategy | ## The 60-second path ```bash # Install the CLI (pick one). npm form needs Node 16+. brew install railway # macOS npm i -g @railway/cli # any Node 16+ host bash <(curl -fsSL railway.com/install.sh) # shell installer railway login # opens browser; for CI use RAILWAY_TOKEN instead (no login) railway init # create a NEW project from this dir, OR: railway link # link this dir to an EXISTING project/service railway up # build + deploy the current dir; streams build/deploy logs ``` Why `link` vs `init`: `init` makes a fresh project; `link` attaches an already-created project (the common case once the project exists in the dashboard). `railway up` deploys whatever is in the working dir — no git push required. ## Two ways to deploy — pick one per service - **GitHub auto-deploy** (default for most teams): connect the repo in the dashboard; every push to the watched branch triggers a build + deploy. Best when you want CI-style "merge to ship". - **CLI `railway up`**: deploy the working tree directly. Best for first setup, hotfixes, or hosts without git. Use `railway up --detach` to not block on logs, `--service api` to target a specific service in a multi-service project (otherwise it prompts). ```bash railway up --detach --service api --environment production ``` There is no `railway deploy --prod` — that command does not exist. Use `railway up` (or push to the connected branch). To re-run the last deploy unchanged: `railway redeploy`. ## Bind 0.0.0.0:$PORT — the #1 first-deploy failure Railway's edge proxy and private network reach your container over an injected `$PORT`. If you bind `localhost`/`127.0.0.1`, the build succeeds but every request 502s and healthchecks fail. ```ts // Bad — only reachable from inside the container; proxy gets connection refused -> 502 app.listen(3000, "127.0.0.1"); // Good — listen on the injected port, bind all interfaces const port = Number(process.env.PORT) || 3000; app.listen(port, "0.0.0.0", () => console.log(`up on :${port}`)); ``` Same rule for every stack: read `$PORT`, bind `0.0.0.0` (or `::` — see private networking). Never hardcode the port the proxy talks to. ## Config as code — railway.json (or railway.toml) Commit `railway.json` to make build/deploy reproducible. **Code config overrides the dashboard** for the fields it sets. Per-environment overrides go under `environments.<name>`. ```jsonc { "$schema": "https://railway.com/railway.schema.json", "build": { "builder": "RAILPACK", "buildCommand": "npm run build" }, "deploy": { "startCommand": "node dist/server.js", "healthcheckPath": "/healthz", "healthcheckTimeout": 300, "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 }, "environments": { "production": { "deploy": { "numReplicas": 2 } } } } ``` `builder` is `RAILPACK` (default; Railway's successor to Nixpacks) or `DOCKERFILE` (a Dockerfile is also auto-detected). `restartPolicyType` is `ON_FAILURE` | `ALWAYS` | `NEVER`. The full field list — `watchPatterns`, `dockerfilePath`, `railpackVersion`, `preDeployCommand`, `multiRegionConfig`, `cronSchedule`, `overlapSeconds`, `drainingSeconds`, and TOML variants — lives in [references/config-as-code.md](references/config-as-code.md). ## Variables & reference variables Set plain variables via CLI or dashboard; never inline secrets into `railway.json` (it's committed). ```bash railway variables # list railway variables --set "LOG_LEVEL=info" # set (older: railway variable set KEY=value) ``` Wire one service's value into another with template syntax instead of copying it. References stay correct across credential rotations and across environments. ```bash # Bad — hardcoded, breaks the moment Railway rotates the DB password DATABASE_URL=postgresql://user:p4ss@containers-us-west-12.railway.app:6543/railway # Good — reference the Postgres service's own variable DATABASE_URL=${{Postgres.DATABASE_URL}} ``` Also available: `${{shared.SENTRY_DSN}}` (project-shared vars) and Railway-provided vars like `${{RAILWAY_PUBLIC_DOMAIN}}`. The left side of the dot is the *service name* exactly as it appears in the project. ## Managed databases Add Postgres / MySQL / Redis / MongoDB as services from Railway's official templates, then reference their connection vars into the app. ```bash railway add # interactive: pick a database template (or use + New / cmd-K in dashboard) railway connect # open a db shell (psql / mongosh / redis-cli) against the service ``` The DB service exposes vars (e.g. `DATABASE_URL`, `PGHOST`) on its Variables tab. Reference them into the app service as `${{Postgres.DATABASE_URL}}` — do not paste the literal URL. Schema design and SQL live in `postgresdb`; migrations live in `db-migrations`. This skill only provisions and wires. ## Volumes Attach a volume to a service via the dashboard or CLI. Railway auto-injects `RAILWAY_VOLUME_NAME` and `RAILWAY_VOLUME_MOUNT_PATH` at runtime — read them, never define them yourself. ```ts const dataDir = process.env.RAILWAY_VOLUME_MOUNT_PATH ?? "/data"; ``` Gotcha: a volume pins the service to a single replica — **a volume blocks horizontal scaling**. If you need many replicas, keep state in a managed DB or object storage, not a volume. ## Environments A project has `production` plus any environments you add (`staging`, PR environments). Each gets its own variables and its own `environments.<name>` config override. Switch with `railway environment <name>`; target a deploy with `railway up --environment staging`. Variables and reference targets resolve *within* the active environment. ## Private networking Every service gets a DNS name under `*.railway.internal`. Talk service-to-service over it — internal egress is free, and traffic never leaves Railway. ```bash # from the app service, reach the API service privately curl http://api.railway.internal:8080/internal/ping ``` IPv6 gotcha: environments created **before 2025-10-16 are IPv6-only** internally — bind `::` (not just `0.0.0.0`) or recreate the environment. Newer environments resolve both IPv4 and IPv6. Cross-project and cross-environment private traffic is blocked by design. ## Domains ```bash railway domain # generate a *.up.railway.app domain for the current service ``` For a custom domain, add it in the service settings and Railway gives you a CNAME target. The registrar-side DNS record work (CNAME/ALIAS at your provider) belongs to `domains-dns` — this skill stops at "here is the CNAME target". ## Healthcheck & failed-deploy triage Set `healthcheckPath` in `railway.json` so Railway gates the deploy on a real readiness route. When a deploy builds but won't go live, read `railway logs` and walk these in order: | Symptom | Likely cause | Fix | | --- | --- | --- | | 502 on every request, healthcheck times out | App bound `localhost`, not `0.0.0.0:$PORT` | Bind `0.0.0.0` (or `::`), listen on `$PORT` | | "no start command" / container exits 0 immediately | No `startCommand` and Railpack can't infer one | Set `deploy.startCommand` | | App crashes on boot referencing a config key | Missing variable / unresolved `${{...}}` reference | Set the var; check the service name in the reference | | Healthcheck 404s | `healthcheckPath` points at a route that doesn't exist | Point it at an existing route or remove it | ## Cost awareness There is **no permanent free tier**. New accounts get a one-time **$5 trial credit**. Hobby is $5/mo (includes $5 usage), Pro is $20/mo (includes $20 usage); usage is billed **by the minute** and the included subscription credit is consumed first, then you pay the delta. An idle service left running still bills. Don't leave throwaway preview services up; delete environments you're done with. ## Anti-patterns | Anti-pattern | Why it bites | Do instead | | --- | --- | --- | | Binding `localhost` / `127.0.0.1` | Proxy + private net can't reach it → 502 | Bind `0.0.0.0` (or `::`), listen on `$PORT` | | Inlining a secret/connection string in `railway.json` | It's committed → leaked secret | Put secrets in variables; reference them | | Hardcoding `DATABASE_URL` into the app's vars | Breaks on credential rotation / env change | Use `${{Postgres.DATABASE_URL}}` | | One mega-service running web + worker + cron | No independent scaling/restart, tangled logs | Split into services in one project | | Assuming a free tier exists | Surprise — trial credit runs out, deploys stop | Plan for Hobby/Pro; watch by-the-minute usage | | Assuming IPv4 on a pre-2025-10-16 environment | Private DNS resolves IPv6-only → connection refused | Bind `::` or recreate the environment | | Committing the `.railway` link as if it were config | It's local link state, not portable config | Config-as-code goes in `railway.json` | ## Verification - App reads `$PORT` and binds `0.0.0.0` (or `::`) — not a hardcoded port on localhost. - No secret/connection string is inlined in `railway.json`; secrets are variables. - Cross-service values use `${{Service.VAR}}` references, not copied literals. - `builder` ∈ {RAILPACK, DOCKERFILE}; `restartPolicyType` ∈ {ON_FAILURE, ALWAYS, NEVER}. - A real `healthcheckPath` route exists if one is configured. Run `bash scripts/verify.sh` from the target dir to structurally lint a present `railway.json`. It is a no-op pass when no config file exists (CLI/dashboard-only use is valid). For the deeper CLI recipe catalog (CI deploys with `RAILWAY_TOKEN`, `railway run` for local dev against prod vars, db shells, multi-service deploys), see [references/cli-cookbook.md](references/cli-cookbook.md).
Ver en GitHub