This document covers the complete development lifecycle from local development to production deployment. It explains the trigger dev command for rapid iteration, the trigger deploy command for building and deploying worker containers, the multi-stage Docker build pipeline, environment configuration management, and CI/CD integration patterns.
For information about the runtime execution environment and task orchestration after deployment, see Task Execution Engine. For details about the webapp server infrastructure, see Web Application.
The Trigger.dev platform supports two distinct operational modes with different build and execution flows:
Sources: packages/cli-v3/src/commands/deploy.ts155-590 apps/webapp/app/v3/marqs/devQueueConsumer.server.ts1-500 apps/webapp/app/v3/services/createBackgroundWorker.server.ts1-286
The trigger dev command provides a hot-reloading development environment where tasks execute in a local Node.js process rather than containers. This enables rapid iteration without Docker builds.
The development flow involves several key services and data structures:
| Component | Class/Function | Purpose |
|---|---|---|
| CLI Command | Not provided in files | Parses args, authenticates, starts build |
| Config Loading | loadConfig() | Resolves trigger.config.ts and workspace |
| Worker Build | buildWorker() | Bundles code with esbuild, no Docker |
| Session Management | createNewSession(), disconnectSession() | Tracks active dev connections |
| Dev Queue Consumer | DevQueueConsumer | Processes messages from Marqs queue |
| Worker Registration | registerBackgroundWorker() | Links worker ID to queue consumer |
Sources: apps/webapp/app/v3/marqs/devQueueConsumer.server.ts42-500 apps/webapp/app/models/runtimeEnvironment.server.ts14
The apps/webapp/app/v3/marqs/devQueueConsumer.server.ts42-500 implements message processing for development workers:
Key characteristics of development mode:
trigger dev creates one RuntimeEnvironmentSessionSources: apps/webapp/app/v3/marqs/devQueueConsumer.server.ts42-500 apps/webapp/app/v3/marqs/devPubSub.server.ts1-50
The trigger deploy command builds a Docker image containing the worker code and pushes it to a container registry. The deployment then goes through an indexing phase to discover tasks before becoming active.
The deployCommand() function in packages/cli-v3/src/commands/deploy.ts159-163 orchestrates a multi-phase process:
Sources: packages/cli-v3/src/commands/deploy.ts159-590 apps/webapp/app/v3/services/initializeDeployment.server.ts18-120 apps/webapp/app/v3/services/finalizeDeploymentV2.server.ts11-164
The DeployCommandOptions schema in packages/cli-v3/src/commands/deploy.ts46-64 defines deployment parameters:
| Option | Type | Default | Purpose |
|---|---|---|---|
env | "prod" | "staging" | "preview" | "production" | "prod" | Target environment (production coerced to prod) |
branch | string? | Auto-detect | Preview branch name |
config | string? | undefined | Config file name override |
projectRef | string? | undefined | Project ref override |
dryRun | boolean | false | Skip actual deployment |
skipSyncEnvVars | boolean | false | Skip env var sync |
skipPromotion | boolean | false | Don't promote to current |
skipUpdateCheck | boolean | false | Skip package version check |
noCache | boolean | false | Disable Docker cache |
saveLogs | boolean | false | Save logs even on success |
envFile | string? | undefined | Path to .env file |
load | boolean? | undefined | Load image into local Docker |
push | boolean? | undefined | Push after local build |
forceLocalBuild | boolean? | undefined | Force local build instead of remote |
network | "default" | "none" | "host"? | undefined | Docker build networking |
builder | string | "trigger" | Docker buildx builder |
Sources: packages/cli-v3/src/commands/deploy.ts46-64
The buildWorker() function (referenced but not provided in files) uses esbuild to bundle task code with the following characteristics:
The build produces a BuildManifest containing:
contentHash: SHA-256 of bundled code for deduplicationruntime: Node.js or other runtime identifierdeploy.sync.env: Environment variables to sync with serverdeploy.sync.parentEnv: Parent env vars (for preview branches)build.env: Build-time environment variablesSources: packages/cli-v3/src/commands/deploy.ts277-298
The buildImage() function in packages/cli-v3/src/deploy/buildImage.ts53-99 supports two build strategies:
Local Builds (when isLocalBuild is true):
localBuildImage() packages/cli-v3/src/deploy/buildImage.ts133-266docker buildx version availableauthenticateToRegistry is truedocker buildx build --platform={imagePlatform} --pushbuilder optionnetwork optionload optionRemote Builds (when isLocalBuild is false):
remoteBuildImage() packages/cli-v3/src/deploy/buildImage.ts268-457externalBuildId and externalBuildToken from deploymentdepot.builds.create() with project IDBoth strategies produce the same image format:
{imageTag} from deploymentimagePlatform parameterSources: packages/cli-v3/src/deploy/buildImage.ts53-457 packages/cli-v3/src/commands/deploy.ts327-445
After the image is built and pushed, the FinalizeDeploymentV2Service in apps/webapp/app/v3/services/finalizeDeploymentV2.server.ts11-164 handles finalization:
The finalization process:
imageReference or fetches from Depot build manifest and resolves digest via getDeploymentImageRef() apps/webapp/app/v3/getDeploymentImageRef.server.ts28-170INDEX_TASKS=true environment variableWorkerManifestCreateBackgroundWorkerService creates database records for tasks, queues, and schedulesThe WorkerManifest structure contains:
tasks: TaskResource[]: All discovered tasks with metadatasourceFiles: BackgroundWorkerSourceFileMetadata[]: Source file mappingsEach TaskResource includes:
id: Task identifierfilePath: Source file locationexportName: Named export in filequeue: Queue configuration (name, concurrency)retry: Retry policy configurationmachine: Machine preset requirementsSources: apps/webapp/app/v3/services/finalizeDeploymentV2.server.ts11-164 apps/webapp/app/v3/getDeploymentImageRef.server.ts28-170 apps/webapp/app/v3/services/createBackgroundWorker.server.ts30-204
The CreateBackgroundWorkerService in apps/webapp/app/v3/services/createBackgroundWorker.server.ts30-286 creates database records for the deployed worker:
The service performs these operations in sequence:
calculateNextBuildVersion() to increment semantic version (e.g., "v1.2.3" → "v1.2.4")BackgroundWorker with:
friendlyId: Generated unique identifiercontentHash: SHA-256 of bundled codesdkVersion, cliVersion: Version metadataengine: RunEngineVersion (V1 or V2)supportsLazyAttempts: Feature flagBackgroundWorkerFile records mapping contentHash to source file contentsTaskQueue records (named or virtual per-task queues)BackgroundWorkerTask records with:
slug: Task identifierqueueConfig: Queue name and concurrencyretryConfig: Retry policy settingsmachineConfig: Machine preset requirementsmaxDurationInSeconds: Timeout configurationCheckScheduleService to validate and setup schedulesWORKER_READY event via projectPubSub to notify other servicesscheduleEnqueueRunsForBackgroundWorker() to schedule pending runsSources: apps/webapp/app/v3/services/createBackgroundWorker.server.ts30-286
The multi-stage Dockerfile produces an optimized production image with minimal attack surface and fast startup.
Sources: docker/Dockerfile1-116
| Stage | FROM | Purpose | Key Operations |
|---|---|---|---|
pruner | node:20.11.1 | Extract workspace | turbo prune --scope=webapp --docker |
base | node:20.11.1 | Layer caching foundation | Copy package.json, lock files |
dev-deps | base | Development dependencies | pnpm install with dev packages |
production-deps | base | Production dependencies | pnpm install --prod, prisma generate |
builder | base | Build webapp | turbo build, upload sourcemaps |
runner | node:20.11.1 | Final runtime image | Copy built assets, setup entrypoint |
Sources: docker/Dockerfile1-116
The pruner stage uses Turborepo's prune command to extract only the webapp and its dependencies:
turbo prune --scope=webapp --docker
This produces a minimal out/ directory containing:
out/json/: All package.json files for webapp and dependenciesout/pnpm-lock.yaml: Filtered lockfileout/full/: Full source code after pruningThe pruned workspace is significantly smaller than the full monorepo, reducing Docker context size and build time.
Sources: docker/Dockerfile6-12 turbo.json1-147
The docker/scripts/entrypoint.sh1-52 script runs at container startup:
Key behaviors:
wait-for-it.sh to ensure PostgreSQL is readypnpm --filter @trigger.dev/database db:migrate:deployCLICKHOUSE_URL is set--max-old-space-size from NODE_MAX_OLD_SPACE_SIZE (default 8192 MB)exec to replace shell with Node.js process (proper signal handling)Sources: docker/scripts/entrypoint.sh1-52
The webapp uses a comprehensive environment variable system defined in apps/webapp/app/env.server.ts1-688 with Zod validation.
Sources: apps/webapp/app/env.server.ts1-688
| Variable | Type | Default | Purpose |
|---|---|---|---|
DATABASE_URL | PostgreSQL URL | required | Primary database connection |
DATABASE_READ_REPLICA_URL | PostgreSQL URL | optional | Read replica for queries |
REDIS_HOST | string | optional | Primary Redis instance |
SESSION_SECRET | string | required | Session cookie signing |
ENCRYPTION_KEY | 32-byte string | required | API key encryption |
DEPLOY_REGISTRY_HOST | string | required | Docker registry host |
DEPLOY_REGISTRY_NAMESPACE | string | "trigger" | Registry namespace |
V4_DEPLOY_REGISTRY_* | various | Falls back to V3 | Separate v4 registry config |
MARQS_VISIBILITY_TIMEOUT_MS | number | 900000 (15 min) | Queue message timeout |
RUN_ENGINE_WORKER_COUNT | number | 4 | Number of run engine workers |
Sources: apps/webapp/app/env.server.ts28-554
The platform uses separate Redis instances for different concerns to enable independent scaling:
Each Redis configuration includes:
*_HOST: Primary host*_READER_HOST: Read replica host*_PORT: Port number*_USERNAME: Authentication username*_PASSWORD: Authentication password*_TLS_DISABLED: Disable TLS (default: false)*_CLUSTER_MODE_ENABLED: Enable cluster mode (default: 0)Sources: apps/webapp/app/env.server.ts95-237
The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts2 provides Zod-based boolean parsing:
This is used throughout the environment schema to handle boolean flags consistently.
Sources: apps/webapp/app/env.server.ts2
The deployment command is designed for integration with continuous integration pipelines.
When running in GitHub Actions (detected via isCI from std-env), the deploy command in packages/cli-v3/src/commands/deploy.ts159-590:
TRIGGER_ACCESS_TOKEN early in CI environments and provides helpful error message with documentation link packages/cli-v3/src/commands/deploy.ts177-192console.log() instead of spinner updates for build logssetGithubActionsOutputAndEnvVars() with deployment metadataSources: packages/cli-v3/src/commands/deploy.ts159-590 packages/cli-v3/src/utilities/githubActions.ts1-50
The command detects CI via the isCI constant from std-env:
When isCI is true:
console.log() instead of spinner updatesSources: packages/cli-v3/src/commands/deploy.ts9 packages/cli-v3/src/commands/deploy.ts397-437
The setGithubActionsOutputAndEnvVars() function sets these outputs:
| Output Variable | Example Value | Description |
|---|---|---|
TRIGGER_DEPLOYMENT_VERSION | "v1.2.3" | Semantic version |
TRIGGER_VERSION | "v1.2.3" | Alias for version |
TRIGGER_DEPLOYMENT_SHORT_CODE | "abc123" | Short identifier |
TRIGGER_DEPLOYMENT_URL | https://cloud.trigger.dev/... | Dashboard URL |
TRIGGER_TEST_URL | https://cloud.trigger.dev/.../test | Test page URL |
These can be used in subsequent workflow steps:
Sources: packages/cli-v3/src/commands/deploy.ts584-590
When builds fail, the command in packages/cli-v3/src/commands/deploy.ts449-521:
checkLogsForErrors() and checkLogsForWarnings() from packages/cli-v3/src/deploy/logs.ts1-150saveLogs() based on --save-logs flagPOST /api/v1/deployments/:id/fail with:
error: Serialized error detailserrorData: Additional context (stack trace, stderr)prettyError() to format error messages for terminal outputThe failure flow ensures deployments are marked as FAILED status in the database even if the CLI process is interrupted. The FailDeploymentService in apps/webapp/app/v3/services/failDeployment.server.ts1-100 updates the deployment record and cleans up any associated resources.
Sources: packages/cli-v3/src/commands/deploy.ts449-521 packages/cli-v3/src/deploy/logs.ts1-150
The webapp server in apps/webapp/server.ts1-270 runs as either a single process or in cluster mode with multiple workers.
Cluster configuration:
ENABLE_CLUSTER=1WEB_CONCURRENCY or CLUSTER_WORKERS or CPU countSIGTERM/SIGINT to workersGRACEFUL_SHUTDOWN_TIMEOUT (default 30s) for workers to exitSources: apps/webapp/server.ts18-90
The Express app configures middleware in this order:
Sources: apps/webapp/server.ts91-230
The server uses runWithHttpContext() from apps/webapp/app/services/httpAsyncStorage.server.ts14 to track request metadata:
This creates an AsyncLocalStorage context available throughout request processing for logging and tracing.
Sources: apps/webapp/server.ts145-153
Worker processes handle shutdown signals gracefully:
The graceful shutdown sequence:
SIGTERM signalbeforeExit event to Socket.IO clientsGRACEFUL_SHUTDOWN_TIMEOUT ms (default 30s)Sources: apps/webapp/server.ts192-230
When the HTTP server starts, it:
socketIo module exists, attach to HTTP serverwss module exists, handle upgrade requestsREMIX_APP_PORT or PORT (default 3000)broadcastDevReady() for HMRThe server can be disabled entirely with HTTP_SERVER_DISABLED=true for testing.
Sources: apps/webapp/server.ts119-230
Refresh this wiki