Thanks to visit codestin.com
Credit goes to deepwiki.com

Menu

Docker Build Pipeline

Relevant source files

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.

Overview

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

Goose Builder Stage

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

Pruner Stage

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).

Base 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.

FilePurpose
.gitignoreGit ignore patterns
out/json/Package manifests only
pnpm-lock.yamlLockfile for reproducible installs
pnpm-workspace.yamlWorkspace 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.

Dev Dependencies Stage

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.

Production Dependencies Stage

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

Builder Stage

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.

Build Process

Sources: docker/Dockerfile44-74

Sentry Integration

The builder stage accepts three build arguments for Sentry sourcemap uploading:

  • SENTRY_RELEASE: Release identifier
  • SENTRY_ORG: Sentry organization slug
  • SENTRY_PROJECT: Sentry project slug

These 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

Build Filter

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

Runner Stage

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.

Image Composition

ComponentSource StageDestination
Production node_modulesproduction-deps/triggerdotdev/
Remix server bundlebuilderapps/webapp/build/server.js
Remix client assetsbuilderapps/webapp/build/
Static filesbuilderapps/webapp/public/
Prisma seed scriptbuilderapps/webapp/prisma/seed.js
Goose binarygoose_builder/usr/local/bin/goose
ClickHouse schemasbuilderinternal-packages/clickhouse/schema/
Entrypoint scriptsbuilderscripts/

Sources: docker/Dockerfile77-94

Build Metadata

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 hash
  • BUILD_GIT_REF_NAME: Git branch or tag name
  • BUILD_TIMESTAMP_SECONDS: Unix timestamp of the build

Sources: docker/Dockerfile96-103

User Configuration

The image uses a two-step process to ensure pnpm is available to the node user:

  1. Root user: Enable corepack and prepare pnpm docker/Dockerfile108
  2. Switch to node user: docker/Dockerfile110
  3. Node user: Prepare pnpm again docker/Dockerfile113

This prevents pnpm from being silently downloaded at runtime, which would fail due to permission issues.

Sources: docker/Dockerfile108-115

Entrypoint Script

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

PostgreSQL Migrations

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

ClickHouse Migrations

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

Node.js Server Startup

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

Docker Ignore Configuration

The .dockerignore file excludes development artifacts and unnecessary files from the build context:

PatternPurpose
**/*.log, **/*.pemSecrets and logs
**/.cache, **/.turboBuild caches
**/node_modulesDependencies (reinstalled)
apps/webapp/build, apps/webapp/public/buildBuild output (regenerated)
.git, .githubVersion control
docs, examplesDocumentation

Sources: .dockerignore1-49

Build Optimization Strategies

Layer Caching

The Dockerfile is structured to maximize Docker layer cache hits:

  1. Package manifests first (base stage): Changes to package.json invalidate fewer layers
  2. Dependencies second (dev-deps, production-deps): Only reinstalls when package.json changes
  3. Source code last (builder stage): Most frequently changing content

Sources: docker/Dockerfile15-29

pnpm Cache Mounting

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

Turbo Prune

The workspace extraction via turbo prune reduces build context size by excluding:

  • Unrelated workspaces (packages, examples, docs)
  • Source files from dependencies (keeps only manifests until builder stage)
  • Test files and other development artifacts

Sources: docker/Dockerfile11

Multi-Stage Isolation

The separation of dev-deps and production-deps ensures:

  • Build tools (TypeScript, esbuild, Sentry CLI) are not included in the final image
  • Production image size is minimized
  • Security surface area is reduced

Sources: docker/Dockerfile24-41

Integration with Deployment

The Docker image is built during the deployment process described in 7.3. The build can occur either:

  1. Locally: Developer's machine builds and pushes to registry
  2. Remotely: Depot.ai builds the image (when 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 hostname
  • DEPLOY_REGISTRY_USERNAME, DEPLOY_REGISTRY_PASSWORD: Credentials
  • DEPLOY_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