Skip to content

Railway

Deploy long-running BullMQ worker services on Railway with the monorepo build and Railway CLI.

5 min read

This guide covers orchestrator worker deployments on Railway: always-on processes that consume BullMQ queues while your API may live on Vercel (or another host). The pattern matches Railway’s API + Redis + worker model described in Deploy an AI Agent with Async Workers—here the queue is BullMQ instead of a custom Redis list.

Railway treats these as persistent services (containers that keep running). See also Orchestrator workers 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 global railway command.
  • 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 (at minimum PUBLIC_SUPABASE_URL and PUBLIC_SUPABASE_ANON_KEY; in production also SUPABASE_SERVICE_ROLE_KEY). See Orchestrator workers.
    • RAILPACK_CONFIG_FILE set per worker service (see “Set variables per worker service” below).

Build and start (summary)

PhaseCommand / setting
Build (repo root)pnpm install && pnpm railway:orchestrator:build
Start — integration refreshpnpm railway:orchestrator:start:integration-refresh
Start — notification emailpnpm railway:orchestrator:start:notification-email

The repo includes railway.toml at the monorepo root with buildCommand and a restart policy; start command is not fixed in that file so two Railway services can use the same image/build with different start lines (config as code).

Dashboard setup

Create project resources

Add Redis (and reference its connection into worker variables as REDIS_*). Add one Railway service per worker (integration refresh vs notification email), or split later—each needs its own Start Command.

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 Orchestrator workers.

Set Start Command

In the service Settings → Deploy → Start Command, set exactly one of the following (from the monorepo root).

Integration refresh worker

pnpm railway:orchestrator:start:integration-refresh

Notification email worker

pnpm railway:orchestrator:start:notification-email

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:notification-email (one-time: create service + set variables via CLI)
  • pnpm railway:deploy:integration-refresh
  • pnpm railway:deploy:notification-email

Deploy a worker from your machine

Authenticate and link

From the repository root:

railway login
railway init
railway link
railway service

Choose your Railway project and the worker service (integration refresh or notification email). Run railway link again only if you need a fresh link. Use railway unlink to clear the link for this directory.

If you see No service linked, run railway service to link a service for this directory.

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:notification-email

These scripts create empty services (if missing) and call railway variable set with —skip-deploys so you can safely configure variables before deploying.

Deploy

Before deploying, confirm the linked service is configured for the integration-refresh worker. It must have RAILPACK_CONFIG_FILE set to railpack.integration-refresh.json (see above), otherwise Railpack will report No start command detected.

pnpm railway:deploy:integration-refresh

This ships the current directory to the linked service and deploys the integration-refresh worker.

To deploy the other worker, switch the linked service (railway service or railway unlink + railway link), ensure it has RAILPACK_CONFIG_FILE=railpack.notification-email.json, then run:

pnpm railway:deploy:notification-email

Full CLI reference: Railway CLI.

Related