Production - Railway
Deploy long-running BullMQ worker services on Railway
Connect your agent today
Draft from chat, review in your calendar, and publish only what you approve.
This guide covers orchestrator worker deployments on Railway: always-on processes that consume BullMQ queues while your API may live on Vercel.
Railway treats these as persistent services (containers that keep running). See also Configuration - Worker for env vars and Orchestrator workflows for when to use bullmq transport.
Prerequisites
- Railway account and Railway CLI on your
PATH. This guide uses the globalrailwaycommand. - Worker environment variables set on each Railway service (applies to dashboard deploys and CLI deploys):
- REDIS_* (shared with your API). See Redis cache.
- Supabase keys — PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY, and SUPABASE_SECRET_KEY. Legacy JWT keys are not accepted; see Supabase and Configuration - Worker.
- Storage public URL for media publishing (recommended if you schedule posts with images/videos): STORAGE_PROVIDER and STORAGE_R2_PUBLIC_BASE_URL (no trailing slash). See R2 or local storage.
- RAILPACK_CONFIG_FILE set per worker service (see “Set variables per worker service” below).
Build and start (summary)
| Phase | Command / setting |
|---|---|
| Build (repo root) | pnpm install && pnpm railway:orchestrator:build |
| Build / start / limits | .railway/railway.ts (Infrastructure as Code) — apply with railway config apply |
| Start (Railpack + monorepo scripts) | pnpm railway:orchestrator:start:* in each orchestrator/railpack.*.json |
Worker services use .railway/railway.ts for build, start, health check, region us-west2, and replica limits (1 vCPU / 1 GiB for integration-refresh and housekeeping; 2 GiB for scheduled-social-post). Repo-root railway.toml is not used (Infrastructure as Code replaces deprecated Config as Code).
After you change .railway/railway.ts, sync Railway from the monorepo root (requires a CLI that supports railway config — run railway upgrade if needed):
railway config plan
railway config apply Dashboard setup
Create project resources
Add Redis (and reference its connection into worker variables as REDIS_*). Add one Railway service per worker you need (integration refresh, housekeeping, and/or scheduled social posts), or split later—each needs its own Start Command; set RAILPACK_CONFIG_FILE to the railpack file for that worker at the repo root (see the callout below).
Name the services after the worker they run (recommended), but the exact service names are up to you. The CLI bootstrap uses the linked service (see “Railway CLI” below).
Connect the GitHub repo
Use the same repository as the monorepo. Configure monorepo settings so install/build run from the repository root.
Set variables per worker service
Set NODE_ENV=production, Redis, Supabase, and any provider/email keys from Configuration - Worker.
Railpack config
*/dist into the runtime image and starts via pnpm railway:orchestrator:start:*. Retire legacy openquok-worker-notification-email — use housekeeping only.Start command (one per worker service)
Each Railway worker runs one start command from the monorepo root (/app in the container). The repo wires these in .railway/railway.ts (start) and the matching orchestrator/railpack.*.json (deploy.startCommand). After railway config apply, leave Settings → Deploy → Start Command empty on each service so deploys use that code-defined command—do not paste a different command in the dashboard unless you are intentionally overriding. Clear any Config file path still pointing at removed railway.toml files.
Use exactly one of the following per service (match RAILPACK_CONFIG_FILE to the same worker):
Integration refresh — service e.g. openquok-worker-integration-refresh, orchestrator/railpack.integration-refresh.json —
pnpm railway:orchestrator:start:integration-refresh Housekeeping (notification email digest + Directory SEO weekly refresh) — service e.g. openquok-worker-housekeeping, orchestrator/railpack.housekeeping.json —
pnpm railway:orchestrator:start:housekeeping Scheduled social post (calendar publishes at publish_date) — service e.g. openquok-worker-scheduled-social-post, orchestrator/railpack.scheduled-social-post.json —
pnpm railway:orchestrator:start:scheduled-social-post Local smoke (with pnpm installed): railway run pnpm railway:orchestrator:start:<worker> after railway link to that service.
Avoid scale-to-zero if it would stop a process during long integration-refresh sleeps.
Railway CLI
Prerequisite: install the Railway CLI
Install the CLI so the railway command is available globally (or on your PATH). Follow Installing the Railway CLI — for example:
npm i -g @railway/cli You can also deploy each worker with the Railway CLI instead of (or in addition to) GitHub-triggered deploys from the dashboard. To avoid deploying the wrong worker, prefer the repo scripts that set the correct Railpack config for you:
- pnpm railway:setup:integration-refresh (one-time: create service + set variables via CLI)
- pnpm railway:setup:housekeeping (one-time: create service + set variables via CLI)
- pnpm railway:setup:scheduled-social-post (one-time: create service + set variables via CLI)
- pnpm railway:deploy:integration-refresh
- pnpm railway:deploy:housekeeping
- pnpm railway:deploy:scheduled-social-post
Deploy a worker from your machine
Authenticate and link
Choose your Railway project and the worker service. Run railway link again only if you need a fresh link.
From the repository root:
railway login
railway init
railway link
railway service If you see No service linked, run railway service to link a service for this directory.
Warning
railway service to select the correct Railway service. The deploy scripts (pnpm railway:deploy:…) run railway up and will deploy to the currently linked service—if you forget to switch, you may deploy the wrong worker to the wrong service.Tip
railway link fails with Available options can not be empty, the CLI could not find any projects to link in the selected workspace.Create services and set variables (recommended once)
If this is the first time setting up a worker service, create the services and set variables via the setup scripts:
pnpm railway:setup:integration-refresh
pnpm railway:setup:housekeeping
pnpm railway:setup:scheduled-social-post These scripts create empty services (if missing) and call railway variable set with —skip-deploys so you can safely configure variables before deploying.
Deploy
For each deploy, the linked service must have RAILPACK_CONFIG_FILE set to the matching railpack file (or use the Create services and set variables setup scripts above), otherwise Railpack will report No start command detected. Switch services with railway service (or railway unlink and railway link) when moving between workers.
Integration refresh —
pnpm railway:deploy:integration-refresh Housekeeping —
pnpm railway:deploy:housekeeping Scheduled social post —
pnpm railway:deploy:scheduled-social-post Each command ships the current directory to the currently linked service. Full CLI reference: Railway CLI.