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. The Trigger.dev server is open-source and ships as a Docker image. Railway is an ideal host: it handles persistent volumes, environment variables, and service networking without requiring Kubernetes or manual infrastructure management.
This guide walks through a complete self-hosted Trigger.dev setup on Railway, including the Postgres database, Redis queue, and the Trigger server itself.
Architecture Overview
| Service | Purpose | Railway Template |
|---|---|---|
| trigger-server | The Trigger.dev API + dashboard | Custom Docker image |
| postgres | Job metadata, run history | Railway Postgres plugin |
| redis | Job queue and pub/sub | Railway Redis plugin |
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 Trigger.dev Server
Create a `railway.toml` and `Dockerfile` for the Trigger server service:
# Dockerfile FROM ghcr.io/triggerdotdev/trigger.dev:latest # Trigger.dev reads all config from environment variables # No additional build steps needed
# railway.toml [build] dockerfilePath = "Dockerfile" [deploy] startCommand = "" healthcheckPath = "/api/v1/health" healthcheckTimeout = 300 restartPolicyType = "on_failure" restartPolicyMaxRetries = 3
Step 4: Set Environment Variables
# Required variables for the Trigger.dev server # Set these in Railway dashboard → Service → Variables DATABASE_URL=${{Postgres.DATABASE_URL}} REDIS_URL=${{Redis.REDIS_URL}} # Generate a secure random string (32+ chars) SESSION_SECRET=your-long-random-session-secret-here MAGIC_LINK_SECRET=your-long-random-magic-link-secret-here ENCRYPTION_KEY=your-32-char-hex-encryption-key # The public URL Railway assigns your service LOGIN_ORIGIN=https://trigger-server-production.up.railway.app APP_ORIGIN=https://trigger-server-production.up.railway.app # Email (required for magic link auth) RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx FROM_EMAIL=noreply@yourapp.com REPLY_TO_EMAIL=support@yourapp.com
# Generate secrets with Node.js node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Step 5: Run Database Migrations
Trigger.dev uses Prisma for database migrations. You need to run them once after the Postgres database is ready:
# SSH into the Railway service or use railway run railway run --service trigger-server npx prisma migrate deploy
Railway's service-to-service networking uses internal hostnames. If the migration command can't reach Postgres, check that `DATABASE_URL` uses the internal Railway URL (not the public one) for security and lower latency.Step 6: Configure Your App to Use Self-Hosted Trigger
// trigger/index.ts export const client = new TriggerClient({ id: "my-app", apiKey: process.env.TRIGGER_API_KEY!, // Point to your self-hosted instance instead of Trigger's cloud apiUrl: process.env.TRIGGER_API_URL!, // https://trigger-server-production.up.railway.app });
# .env.local (your Next.js app) TRIGGER_API_KEY=tr_dev_xxxxxxxxxxxxxxxxxxxx # created in your self-hosted dashboard TRIGGER_API_URL=https://trigger-server-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 | Check Railway Redis config |
| HTTPS | Automatic | Railway provisions TLS for all public domains |
| Horizontal scaling | Not needed | Trigger server is single-instance; scale your workers instead |
| Worker scaling | Scale your app service | Multiple replicas of your Next.js/Node worker all connect to the same Trigger server |
| 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
Navigate to your Trigger server's Railway URL (e.g., `https://trigger-server-production.up.railway.app`). Create an account using a magic link (sent to the email you configured), then create an organization and project to get your API key.
The self-hosted Trigger dashboard is currently single-tenant (one organization per server). If you need multiple isolated organizations, deploy separate Trigger server instances.Cost Estimate on Railway
| Service | Railway Cost |
|---|---|
| Trigger server (1 vCPU, 512MB) | ~$5/month |
| Postgres (1GB storage) | ~$5/month |
| Redis (256MB) | ~$3/month |
| Total | ~$13/month vs Trigger.dev cloud (usage-based) |