This document describes the Docker build pipeline for the Trigger.dev webapp, which produces the OSS container image. The multi-stage Dockerfile orchestrates workspace extraction, dependency installation, Prisma client generation, Remix application building, and runtime configuration. For information about the deployment process that uses these images, see 7.3. For local development without Docker, see 7.1.
The Docker build pipeline transforms the monorepo into a production-ready container image through a series of optimized stages. The build process uses Turborepo's prune command to extract only the webapp and its dependencies, installs production and development dependencies separately for layer caching, generates the Prisma client, builds the Remix application with esbuild, and packages everything into a minimal runtime image.
Sources: docker/Dockerfile1-116
Sources: docker/Dockerfile3-115
The goose_builder stage compiles the Goose database migration tool from source using a Go 1.23 Alpine image. Goose is used for executing ClickHouse schema migrations at container startup.
Sources: docker/Dockerfile3-4
The binary is later copied into both the builder stage (for potential build-time migrations) and the runner stage (for runtime migrations): docker/Dockerfile59-60 docker/Dockerfile92
The pruner stage extracts only the webapp workspace and its dependencies from the monorepo using Turborepo's prune command. This significantly reduces the build context size by excluding unrelated packages.
Sources: docker/Dockerfile6-12
The --docker flag generates a directory structure optimized for Docker layer caching, with out/json/ containing package.json files and out/full/ containing source code (used later in the builder stage).
The base stage establishes the foundation for both dependency installation stages. It installs system-level dependencies (OpenSSL, dumb-init) and copies the pruned workspace structure without source code or dependencies.
| File | Purpose |
|---|---|
.gitignore | Git ignore patterns |
out/json/ | Package manifests only |
pnpm-lock.yaml | Lockfile for reproducible installs |
pnpm-workspace.yaml | Workspace configuration |
Sources: docker/Dockerfile15-21
This stage serves as a parent for both dev-deps and production-deps, allowing Docker to cache the layer when package versions haven't changed.
The dev-deps stage installs all dependencies (including devDependencies) using pnpm with aggressive caching. This stage is used exclusively by the builder stage.
Sources: docker/Dockerfile24-29
The --mount=type=cache directive at docker/Dockerfile29 enables Docker BuildKit to maintain a persistent pnpm store across builds, dramatically speeding up dependency installation.
The production-deps stage installs only production dependencies and generates the Prisma client. This stage produces the node_modules directory used in the final runtime image.
Sources: docker/Dockerfile32-41
The Prisma schema is copied from internal-packages/database/prisma/schema.prisma at docker/Dockerfile38 and the client is generated at docker/Dockerfile41 using the exact version 6.14.0 to match the runtime dependency in internal-packages/database/package.json8
The builder stage compiles the webapp by copying the full source tree, installing the Goose binary, and running the Turborepo build pipeline. This stage uses dev dependencies for build tools like esbuild, TypeScript, and the Sentry CLI.
Sources: docker/Dockerfile44-74
The builder stage accepts three build arguments for Sentry sourcemap uploading:
SENTRY_RELEASE: Release identifierSENTRY_ORG: Sentry organization slugSENTRY_PROJECT: Sentry project slugThese are passed through to the webapp build script which triggers sourcemap upload via apps/webapp/upload-sourcemaps.sh The SENTRY_AUTH_TOKEN is injected as a Docker secret at docker/Dockerfile72-73 to avoid leaking credentials in the image history.
Sources: docker/Dockerfile51-56 docker/Dockerfile72-74
The Turborepo build command --filter=webapp... at docker/Dockerfile74 builds the webapp and all its internal dependencies in the correct order, as defined in turbo.json4-14 This includes generating the Prisma client, compiling TypeScript packages, and building the Remix application.
Sources: turbo.json4-14
The runner stage assembles the final production image by combining production dependencies, build artifacts, the Goose binary, and the entrypoint script. This stage runs as the non-root node user for security.
| Component | Source Stage | Destination |
|---|---|---|
| Production node_modules | production-deps | /triggerdotdev/ |
| Remix server bundle | builder | apps/webapp/build/server.js |
| Remix client assets | builder | apps/webapp/build/ |
| Static files | builder | apps/webapp/public/ |
| Prisma seed script | builder | apps/webapp/prisma/seed.js |
| Goose binary | goose_builder | /usr/local/bin/goose |
| ClickHouse schemas | builder | internal-packages/clickhouse/schema/ |
| Entrypoint scripts | builder | scripts/ |
Sources: docker/Dockerfile77-94
The runner stage accepts four build arguments that are baked into the image as environment variables for versioning and observability:
BUILD_APP_VERSION: Application version (e.g., "3.2.1")BUILD_GIT_SHA: Git commit hashBUILD_GIT_REF_NAME: Git branch or tag nameBUILD_TIMESTAMP_SECONDS: Unix timestamp of the buildSources: docker/Dockerfile96-103
The image uses a two-step process to ensure pnpm is available to the node user:
This prevents pnpm from being silently downloaded at runtime, which would fail due to permission issues.
Sources: docker/Dockerfile108-115
The container startup is orchestrated by docker/scripts/entrypoint.sh which executes database migrations before starting the webapp server.
Sources: docker/scripts/entrypoint.sh1-51
Prisma migrations are executed against the PostgreSQL database using the pnpm workspace command:
This runs prisma migrate deploy from internal-packages/database/package.json20 which applies any pending migrations from internal-packages/database/prisma/migrations/
Sources: docker/scripts/entrypoint.sh9-11
If CLICKHOUSE_URL is set, the script configures Goose and executes ClickHouse migrations:
The script ensures the connection string includes secure=true for TLS, appending it if necessary at docker/scripts/entrypoint.sh19-28
Sources: docker/scripts/entrypoint.sh13-35
The webapp server starts with configurable heap memory:
The NODE_PATH environment variable is set to the pnpm node_modules directory to ensure proper module resolution. dumb-init is used as PID 1 to properly handle signals and reap zombie processes.
Sources: docker/scripts/entrypoint.sh44-50 apps/webapp/server.ts1-269
The .dockerignore file excludes development artifacts and unnecessary files from the build context:
| Pattern | Purpose |
|---|---|
**/*.log, **/*.pem | Secrets and logs |
**/.cache, **/.turbo | Build caches |
**/node_modules | Dependencies (reinstalled) |
apps/webapp/build, apps/webapp/public/build | Build output (regenerated) |
.git, .github | Version control |
docs, examples | Documentation |
Sources: .dockerignore1-49
The Dockerfile is structured to maximize Docker layer cache hits:
Sources: docker/Dockerfile15-29
Both dependency stages use BuildKit cache mounts to persist the pnpm store across builds:
This reduces network bandwidth and installation time for unchanged dependencies.
Sources: docker/Dockerfile29 docker/Dockerfile37
The workspace extraction via turbo prune reduces build context size by excluding:
Sources: docker/Dockerfile11
The separation of dev-deps and production-deps ensures:
Sources: docker/Dockerfile24-41
The Docker image is built during the deployment process described in 7.3. The build can occur either:
DEPOT_TOKEN is configured in apps/webapp/app/env.server.ts266)The image is tagged with the deployment content hash and pushed to the registry configured via environment variables:
DEPLOY_REGISTRY_HOST: Container registry hostnameDEPLOY_REGISTRY_USERNAME, DEPLOY_REGISTRY_PASSWORD: CredentialsDEPLOY_REGISTRY_NAMESPACE: Registry namespace (default: "trigger")Sources: apps/webapp/app/env.server.ts271-274
The Kubernetes provider or Docker provider then pulls this image to create worker containers for task execution. For v4 deployments, a separate set of registry environment variables (V4_DEPLOY_REGISTRY_*) can be configured, falling back to the v3 registry configuration if not set.
Sources: apps/webapp/app/env.server.ts279-309
Refresh this wiki
This wiki was recently refreshed. Please wait 7 days to refresh again.