Trigger.dev's cloud is the easiest starting point, but for teams with strict data-residency requirements or high job volumes, self-hosting gives you full control. As of v4, the self-hosted platform is a multi-service stack rather than a single container: a webapp control plane (dashboard, API, Postgres, Redis, a built-in container registry, and object storage) plus a worker plane that runs each task in its own Docker container. Railway can comfortably host the control plane; the worker needs a Docker-capable machine.

This guide walks through a complete v4 self-hosted setup: the Postgres database and Redis cache, the Trigger.dev webapp on Railway, a worker (supervisor) on a Docker host, and deploying your tasks to the instance with the CLI.

Architecture Overview

Service Purpose Where it runs
webapp Dashboard, REST API, and realtime updates Railway service (Trigger.dev image)
postgres Task metadata and run history Railway Postgres plugin
redis Queues and pub/sub Railway Redis plugin
registry Stores the container images your task deploys produce Railway service or external registry
object storage (MinIO / S3) Large run payloads and outputs Railway volume or external S3 / R2
supervisor + runners Pulls task images and runs each execution in its own Docker container A Docker-capable machine (not a managed Railway service)

The split matters for hosting. The control-plane services are long-running and fit Railway's managed model well. The worker plane is different: the supervisor talks to a Docker daemon to spin up a fresh container for every run, so it has to run somewhere it can reach a Docker socket. That's why this setup keeps the webapp, Postgres, and Redis on Railway but runs the supervisor on a separate Docker machine.

Step 1: Create a Railway Project

# Install Railway CLI npm install -g @railway/cli # Login and create a project railway login railway init --name trigger-self-hosted

Step 2: Add Postgres and Redis

In the Railway dashboard, click Add Service → Database → PostgreSQL, then repeat for Redis. Railway automatically sets `DATABASE_URL` and `REDIS_URL` environment variables that other services in the project can reference.

Step 3: Deploy the Webapp (Control Plane)

Create a Dockerfile and railway.toml so Railway can run the Trigger.dev webapp image:

# Dockerfile FROM ghcr.io/triggerdotdev/trigger.dev:latest # The webapp reads all configuration from environment variables. # It listens on port 8030 and runs database migrations on boot. EXPOSE 8030

# railway.toml [build] dockerfilePath = "Dockerfile" [deploy] restartPolicyType = "on_failure" restartPolicyMaxRetries = 3 # The webapp listens on 8030. Set PORT=8030 in the service # variables so Railway routes external HTTPS to the container.

Step 4: Set Environment Variables

# Required variables for the Trigger.dev webapp # Set these in Railway dashboard -> Service -> Variables # Core secrets (generate each with the command below) SESSION_SECRET=your-32-byte-hex-secret MAGIC_LINK_SECRET=your-32-byte-hex-secret ENCRYPTION_KEY=your-32-byte-hex-secret MANAGED_WORKER_SECRET=your-32-byte-hex-secret # Database and cache (from the Railway plugins) DATABASE_URL=${{Postgres.DATABASE_URL}} DIRECT_URL=${{Postgres.DATABASE_URL}} REDIS_HOST=${{Redis.RAILWAY_PRIVATE_DOMAIN}} REDIS_PORT=6379 # Public URLs (use the domain Railway assigns the webapp) APP_ORIGIN=https://trigger-webapp-production.up.railway.app LOGIN_ORIGIN=https://trigger-webapp-production.up.railway.app API_ORIGIN=https://trigger-webapp-production.up.railway.app # Email for magic-link auth (SMTP, or a provider like Resend) RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx FROM_EMAIL=noreply@yourapp.com

# Generate each 32-byte secret (run once per secret) node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # Or, if you clone the repo, use the bundled helper: # ./hosting/docker/generate-secrets.sh

Step 5: First Boot and Login

The v4 webapp applies its database migrations automatically the first time it boots — there is no separate migrate command to run. Watch the service logs for the magic-link message you'll use to create the first account:

# Follow the webapp logs on Railway railway logs --service trigger-webapp # On a plain Docker host you would use: # docker compose logs -f webapp

Railway's service-to-service networking uses internal hostnames. If the webapp can't reach Postgres on boot, check that DATABASE_URL uses the internal Railway URL (not the public one) for security and lower latency.

Step 6: Run a Worker and Deploy Your Tasks

# --- On a Docker-capable machine: run the worker (supervisor) --- git clone --depth=1 https://github.com/triggerdotdev/trigger.dev cd trigger.dev/hosting/docker cp .env.example .env # The webapp prints a TRIGGER_WORKER_TOKEN in its logs on first boot. # In .env, set: # TRIGGER_API_URL=https://trigger-webapp-production.up.railway.app # TRIGGER_WORKER_TOKEN=tr_wgt_xxxxxxxxxxxxxxxxxxxx cd worker docker compose up -d # --- From your app repo: deploy your tasks to the instance --- export TRIGGER_API_URL=https://trigger-webapp-production.up.railway.app export TRIGGER_ACCESS_TOKEN=tr_pat_xxxxxxxxxxxxxxxxxxxx # personal access token npx trigger.dev@latest deploy --self-hosted --push

# .env.local (your Next.js app) TRIGGER_SECRET_KEY=tr_dev_xxxxxxxxxxxxxxxxxxxx # from your self-hosted dashboard TRIGGER_API_URL=https://trigger-webapp-production.up.railway.app

Production Checklist

Item Status Notes
Postgres backups Enable in Railway Railway offers point-in-time recovery on paid plans
Redis persistence AOF enabled by default Also back up object storage (MinIO) and the registry if you self-host them
HTTPS Automatic Railway provisions TLS for all public domains
Webapp scaling Single instance Run one webapp; scale throughput on the worker plane instead
Worker scaling Add worker machines Each supervisor runs task containers on its Docker host; add machines or raise concurrency to run more tasks in parallel
Monitoring Add Railway metrics Alert on Postgres connection count and Redis memory usage
Job history retention Configure in Trigger dashboard Default is 30 days; adjust for compliance needs

Viewing the Dashboard

Open your webapp's Railway URL (for example https://trigger-webapp-production.up.railway.app, which serves the dashboard on port 8030). Sign in with a magic link sent to the email you configured, then create an organization and project to get the TRIGGER_SECRET_KEY your app uses to trigger tasks.

The worker plane (supervisor + runners) needs a real Docker daemon to launch task containers, which Railway's managed service runtime does not expose. Run the supervisor on a Docker-capable machine — a small VM or your own server — and keep only the webapp, Postgres, and Redis on Railway.

Cost Estimate on Railway

Service Railway Cost
Webapp (recommended 3+ vCPU, 6+ GB RAM) ~$60-120/mo at sustained use
Postgres (managed plugin) ~$5-15/mo
Redis ~$3-10/mo
Registry + object storage (MinIO) ~$5-15/mo
Worker host (4+ vCPU, 8+ GB, Docker) ~$40-80/mo (separate Docker VM)
Total ~$120-250/mo, scaling with concurrency (vs ~$13/mo for the old v2 single-container setup)