From 31c705b38eeab17ba05938e5ef27aad85d7cbef7 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 12:00:03 +0100
Subject: [PATCH 01/21] self-hosting stuff goes in /hosting
---
docker/README.md | 3 +++
hosting/README.md | 1 +
hosting/docker/clickhouse/override.xml | 21 +++++++++++++++++++++
hosting/docker/registry/auth.htpasswd | 1 +
4 files changed, 26 insertions(+)
create mode 100644 docker/README.md
create mode 100644 hosting/README.md
create mode 100644 hosting/docker/clickhouse/override.xml
create mode 100644 hosting/docker/registry/auth.htpasswd
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000000..84c28a67a7
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,3 @@
+# Internal Docker files
+
+If you're looking for self-hosting files you're in the wrong place. Have a look [here](../hosting/).
\ No newline at end of file
diff --git a/hosting/README.md b/hosting/README.md
new file mode 100644
index 0000000000..ed117eea92
--- /dev/null
+++ b/hosting/README.md
@@ -0,0 +1 @@
+# Self-hosting files
\ No newline at end of file
diff --git a/hosting/docker/clickhouse/override.xml b/hosting/docker/clickhouse/override.xml
new file mode 100644
index 0000000000..7642e22b09
--- /dev/null
+++ b/hosting/docker/clickhouse/override.xml
@@ -0,0 +1,21 @@
+
+
+
+ warning
+
+
+
\ No newline at end of file
diff --git a/hosting/docker/registry/auth.htpasswd b/hosting/docker/registry/auth.htpasswd
new file mode 100644
index 0000000000..3f659261e4
--- /dev/null
+++ b/hosting/docker/registry/auth.htpasswd
@@ -0,0 +1 @@
+registry-user:$2y$05$6ingYqw0.3j13dxHY4w3neMSvKhF3pvRmc0AFifScWsVA9JpuLwNK
From 043e04abdebcb20d939eebaf213b7723ffeab2ea Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 12:00:29 +0100
Subject: [PATCH 02/21] add v4 tags
---
docs/cli-preview-archive.mdx | 1 +
docs/deployment/preview-branches.mdx | 1 +
docs/guides/example-projects/human-in-the-loop-workflow.mdx | 1 +
.../example-projects/meme-generator-human-in-the-loop.mdx | 1 +
docs/limits.mdx | 2 +-
docs/wait-for-token.mdx | 1 +
6 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/docs/cli-preview-archive.mdx b/docs/cli-preview-archive.mdx
index 3b21c08c08..2f0fb81ad1 100644
--- a/docs/cli-preview-archive.mdx
+++ b/docs/cli-preview-archive.mdx
@@ -2,6 +2,7 @@
title: "CLI preview archive command"
sidebarTitle: "preview archive"
description: "The `trigger.dev preview archive` command can be used to archive a preview branch."
+tag: "v4"
---
import UpgradeToV4Note from "/snippets/upgrade-to-v4-note.mdx";
diff --git a/docs/deployment/preview-branches.mdx b/docs/deployment/preview-branches.mdx
index d6fa098030..f7882970df 100644
--- a/docs/deployment/preview-branches.mdx
+++ b/docs/deployment/preview-branches.mdx
@@ -1,6 +1,7 @@
---
title: "Preview branches"
description: "Create isolated environments for each branch of your code, allowing you to test changes before merging to production. You can create preview branches manually or automatically from your git branches."
+tag: "v4"
---
import UpgradeToV4Note from "/snippets/upgrade-to-v4-note.mdx";
diff --git a/docs/guides/example-projects/human-in-the-loop-workflow.mdx b/docs/guides/example-projects/human-in-the-loop-workflow.mdx
index 6975b1bbe2..36d5bcc66f 100644
--- a/docs/guides/example-projects/human-in-the-loop-workflow.mdx
+++ b/docs/guides/example-projects/human-in-the-loop-workflow.mdx
@@ -2,6 +2,7 @@
title: "Human-in-the-loop workflow with ReactFlow and Trigger.dev waitpoint tokens"
sidebarTitle: "Human-in-the-loop workflow"
description: "This example project creates audio summaries of newspaper articles using a human-in-the-loop workflow built with ReactFlow and Trigger.dev waitpoint tokens."
+tag: "v4"
---
import UpgradeToV4Note from "/snippets/upgrade-to-v4-note.mdx";
diff --git a/docs/guides/example-projects/meme-generator-human-in-the-loop.mdx b/docs/guides/example-projects/meme-generator-human-in-the-loop.mdx
index 76b5e336f0..bbf0aac412 100644
--- a/docs/guides/example-projects/meme-generator-human-in-the-loop.mdx
+++ b/docs/guides/example-projects/meme-generator-human-in-the-loop.mdx
@@ -2,6 +2,7 @@
title: "Meme generator with human-in-the-loop approval"
sidebarTitle: "AI meme generator"
description: "This example project creates memes using OpenAI's DALL-E 3 with a human-in-the-loop approval workflow built using Trigger.dev waitpoint tokens."
+tag: "v4"
---
import UpgradeToV4Note from "/snippets/upgrade-to-v4-note.mdx";
diff --git a/docs/limits.mdx b/docs/limits.mdx
index bcc669a8f7..21b094333d 100644
--- a/docs/limits.mdx
+++ b/docs/limits.mdx
@@ -39,7 +39,7 @@ The number of queued tasks by environment.
| Pricing tier | Limit |
| :----------- | :----------------- |
-| Free | 10 per project |
+| Free | 10 per project |
| Hobby | 100 per project |
| Pro | 1,000+ per project |
diff --git a/docs/wait-for-token.mdx b/docs/wait-for-token.mdx
index 9bda1edfea..8098243dfd 100644
--- a/docs/wait-for-token.mdx
+++ b/docs/wait-for-token.mdx
@@ -1,6 +1,7 @@
---
title: "Wait for token"
description: "Wait until a token is completed."
+tag: "v4"
---
import UpgradeToV4Note from "/snippets/upgrade-to-v4-note.mdx";
From 9f50e1e408737c9f54539c76b46846aa0bf7423a Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 12:04:25 +0100
Subject: [PATCH 03/21] add main compose file
---
hosting/docker/docker-compose.yml | 283 ++++++++++++++++++++++++++++++
1 file changed, 283 insertions(+)
create mode 100644 hosting/docker/docker-compose.yml
diff --git a/hosting/docker/docker-compose.yml b/hosting/docker/docker-compose.yml
new file mode 100644
index 0000000000..12c1b4f23e
--- /dev/null
+++ b/hosting/docker/docker-compose.yml
@@ -0,0 +1,283 @@
+name: trigger
+
+services:
+ webapp:
+ image: ghcr.io/triggerdotdev/trigger.dev:${TRIGGER_IMAGE_TAG:-main}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${WEBAPP_PUBLISH_IP:-0.0.0.0}:8030:3000
+ depends_on:
+ - postgres
+ - redis
+ networks:
+ - webapp
+ - supervisor
+ - traefik
+ volumes:
+ - shared:/home/node/shared
+ user: root
+ command: sh -c "chown -R node:node /home/node/shared && exec ./scripts/entrypoint.sh"
+ healthcheck:
+ test: ["CMD", "node", "-e", "http.get('http://localhost:3000/healthcheck', res => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ start_period: 10s
+ environment:
+ APP_ORIGIN: http://localhost:8030
+ ELECTRIC_ORIGIN: http://electric:3000
+ DATABASE_URL: postgresql://postgres:postgres@postgres:5432/main?schema=public&sslmode=disable
+ DIRECT_URL: postgresql://postgres:postgres@postgres:5432/main?schema=public&sslmode=disable
+ SESSION_SECRET: ${SESSION_SECRET}
+ MAGIC_LINK_SECRET: ${MAGIC_LINK_SECRET}
+ ENCRYPTION_KEY: ${ENCRYPTION_KEY}
+ MANAGED_WORKER_SECRET: ${MANAGED_WORKER_SECRET}
+ REDIS_HOST: redis
+ REDIS_PORT: 6379
+ REDIS_TLS_DISABLED: true
+ APP_LOG_LEVEL: info
+ DEV_OTEL_EXPORTER_OTLP_ENDPOINT: http://localhost:8030/otel
+ TRIGGER_BOOTSTRAP_ENABLED: 1
+ TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME: bootstrap-3
+ TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH: /home/node/shared/worker_token
+ DEPLOY_REGISTRY_HOST: localhost:5000
+ OBJECT_STORE_BASE_URL: http://minio:9000
+ OBJECT_STORE_ACCESS_KEY_ID: BlJ2Hcd6Cgsz3ITBWy4p
+ OBJECT_STORE_SECRET_ACCESS_KEY: RskeTL9VAQ7saSSI2hK1MUMaaAIavf7vyQyVPiDu
+ GRACEFUL_SHUTDOWN_TIMEOUT: 1000
+ # Limits
+ # TASK_PAYLOAD_OFFLOAD_THRESHOLD: 524288 # 512KB
+ # TASK_PAYLOAD_MAXIMUM_SIZE: 3145728 # 3MB
+ # BATCH_TASK_PAYLOAD_MAXIMUM_SIZE: 1000000 # 1MB
+ # TASK_RUN_METADATA_MAXIMUM_SIZE: 262144 # 256KB
+ # DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT: 100
+ # DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT: 100
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.webapp.rule=Host(`webapp.localhost`)"
+ - "traefik.http.routers.webapp.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
+ # - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
+ - "traefik.http.services.webapp.loadbalancer.server.port=3000"
+
+ supervisor:
+ image: ghcr.io/triggerdotdev/supervisor:${TRIGGER_IMAGE_TAG:-main}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ depends_on:
+ - webapp
+ - docker-proxy
+ networks:
+ - supervisor
+ - docker-proxy
+ volumes:
+ - shared:/home/node/shared
+ user: root
+ command: sh -c "chown -R node:node /home/node/shared && exec /usr/bin/dumb-init -- pnpm run --filter supervisor start"
+ environment:
+ # This needs to match the token of the worker group you want to connect to
+ # TRIGGER_WORKER_TOKEN: ${TRIGGER_WORKER_TOKEN}
+ TRIGGER_WORKER_TOKEN: file:///home/node/shared/worker_token
+ MANAGED_WORKER_SECRET: ${MANAGED_WORKER_SECRET}
+ # Point this at the webapp in prod
+ TRIGGER_API_URL: http://webapp:3000
+ # Point this at the OTel collector or the webapp in prod
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://webapp:3000/otel
+ TRIGGER_WORKLOAD_API_DOMAIN: supervisor
+ TRIGGER_WORKLOAD_API_PORT_EXTERNAL: 8020
+ # Optional settings
+ DEBUG: 1
+ ENFORCE_MACHINE_PRESETS: 1
+ TRIGGER_DEQUEUE_INTERVAL_MS: 1000
+ DOCKER_HOST: tcp://docker-proxy:2375
+ DOCKER_RUNNER_NETWORKS: webapp,supervisor
+ DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-localhost:5000}
+ DOCKER_REGISTRY_USERNAME: ${DOCKER_REGISTRY_USERNAME:-}
+ DOCKER_REGISTRY_PASSWORD: ${DOCKER_REGISTRY_PASSWORD:-}
+ DOCKER_AUTOREMOVE_EXITED_CONTAINERS: 0
+ healthcheck:
+ test: ["CMD", "node", "-e", "http.get('http://localhost:8020/health', res => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ start_period: 10s
+
+ docker-proxy:
+ image: tecnativa/docker-socket-proxy:${DOCKER_PROXY_IMAGE_TAG:-latest}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ networks:
+ - docker-proxy
+ environment:
+ - LOG_LEVEL=info
+ - POST=1
+ - CONTAINERS=1
+ - IMAGES=1
+ - INFO=1
+ - NETWORKS=1
+ healthcheck:
+ test: ["CMD", "nc", "-z", "127.0.0.1", "2375"]
+ interval: 30s
+ timeout: 5s
+ retries: 5
+ start_period: 5s
+
+ postgres:
+ image: postgres:${POSTGRES_IMAGE_TAG:-14}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${POSTGRES_PUBLISH_IP:-127.0.0.1}:5433:5432
+ volumes:
+ - postgres:/var/lib/postgresql/data/
+ networks:
+ - webapp
+ command:
+ - -c
+ - wal_level=logical
+ environment:
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: postgres
+ healthcheck:
+ test: ["CMD", "pg_isready", "-U", "postgres"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ redis:
+ image: redis:${REDIS_IMAGE_TAG:-7}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${REDIS_PUBLISH_IP:-127.0.0.1}:6389:6379
+ volumes:
+ - redis:/data
+ networks:
+ - webapp
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ electric:
+ image: electricsql/electric:${ELECTRIC_IMAGE_TAG:-1.0.13}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ depends_on:
+ - postgres
+ networks:
+ - webapp
+ environment:
+ DATABASE_URL: postgresql://postgres:postgres@postgres:5432/main?schema=public&sslmode=disable
+ ELECTRIC_INSECURE: true
+ ELECTRIC_USAGE_REPORTING: false
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/v1/health"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ clickhouse:
+ image: bitnami/clickhouse:${CLICKHOUSE_IMAGE_TAG:-latest}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${CLICKHOUSE_PUBLISH_IP:-127.0.0.1}:9090:9000
+ environment:
+ CLICKHOUSE_ADMIN_USER: default
+ CLICKHOUSE_ADMIN_PASSWORD: password
+ volumes:
+ - clickhouse:/bitnami/clickhouse
+ - ./clickhouse/override.xml:/bitnami/clickhouse/etc/config.d/override.xml:ro
+ networks:
+ - webapp
+ healthcheck:
+ test: ["CMD", "clickhouse-client", "--host", "localhost", "--port", "9000", "--user", "default", "--password", "password", "--query", "SELECT 1"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ registry:
+ image: registry:${REGISTRY_IMAGE_TAG:-2}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${REGISTRY_PUBLISH_IP:-127.0.0.1}:5000:5000
+ networks:
+ - webapp
+ volumes:
+ # registry-user:very-secure-indeed
+ - ./registry/auth.htpasswd:/auth/htpasswd:ro
+ environment:
+ REGISTRY_AUTH: htpasswd
+ REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
+ REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
+ healthcheck:
+ test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
+
+ minio:
+ image: minio/minio:${MINIO_IMAGE_TAG:-latest}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - ${MINIO_PUBLISH_IP:-127.0.0.1}:9000:9000
+ - ${MINIO_PUBLISH_IP:-127.0.0.1}:9001:9001
+ networks:
+ - webapp
+ volumes:
+ - minio:/data
+ environment:
+ MINIO_ROOT_USER: admin
+ MINIO_ROOT_PASSWORD: very-safe-password
+ command: server --console-address ":9001" /data
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
+ interval: 5s
+ timeout: 10s
+ retries: 5
+ start_period: 10s
+
+ traefik:
+ image: traefik:${TRAEFIK_IMAGE_TAG:-v3.4}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - "80:80"
+ - "443:443"
+ - "8080:8080" # Traefik dashboard
+ networks:
+ - traefik
+ command:
+ - --api.insecure=true
+ - --providers.docker=true
+ - --providers.docker.exposedbydefault=false
+ - --providers.docker.network=traefik
+ - --entrypoints.web.address=:80
+ - --entrypoints.websecure.address=:443
+ # - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
+ # - --certificatesresolvers.letsencrypt.acme.email=local@foo.bar
+ # - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
+ - --log.level=DEBUG
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ - traefik-letsencrypt:/letsencrypt
+
+volumes:
+ clickhouse:
+ postgres:
+ redis:
+ shared:
+ minio:
+ traefik-letsencrypt:
+
+networks:
+ traefik:
+ name: traefik
+ docker-proxy:
+ name: docker-proxy
+ supervisor:
+ name: supervisor
+ webapp:
+ name: webapp
From 7b2bec79bd1750e026e0bc2113a71e7be0bec5f7 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 12:41:06 +0100
Subject: [PATCH 04/21] draft overview
---
docs/self-hosting/overview.mdx | 67 ++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 docs/self-hosting/overview.mdx
diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx
new file mode 100644
index 0000000000..cf2a1b3b00
--- /dev/null
+++ b/docs/self-hosting/overview.mdx
@@ -0,0 +1,67 @@
+---
+title: "Overview"
+description: "You can self-host Trigger.dev on your own infrastructure."
+tag: "v4"
+---
+
+Self-hosting Trigger.dev means you run and manage the platform on your own infrastructure, giving you full control over your environment, deployment process, and the URLs you expose the service on.
+
+You are responsible for provisioning resources, handling updates, and managing any security, scaling or reliability challenges that arise.
+
+We provide version-tagged releases for self-hosted deployments. It's highly advised to use these tags exclusively and keep them locked with your CLI version.
+
+## Should you self-host?
+
+For most users, Trigger.dev Cloud offers the best experience - it's fully managed, scalable, and comes with dedicated support. However, if you have specific requirements around data residency, compliance, or infrastructure control, self-hosting may be the right choice for you.
+
+The self-hosted version is functionally the same as Trigger.dev Cloud with [some exceptions](#feature-comparison), but our managed Cloud infrastructure is designed for high availability, security, and scale.
+
+Because we do not manage self-hosted instances, we cannot guarantee how Trigger.dev will perform on your infrastructure. You assume all responsibility and risk for your deployment, including security, uptime, and data integrity.
+
+You can read the rest of this overview and follow our guides for instructions on setting up a self-hosted Trigger.dev instance. If you prefer a managed experience, you can [sign up](https://cloud.trigger.dev/login) for our Cloud offering instead - we have a generous [free tier](https://trigger.dev/pricing) for you to try it out.
+
+{/* TODO: Architecture section with updated diagram */}
+
+## Feature comparison
+
+While [limits](#limits) are generally configurable when self-hosting, some features are only available on Trigger.dev Cloud:
+
+| Feature | Cloud | Self-hosted | Description |
+| :---------------- | :---- | :---------- | :-------------------------------------- |
+| Warm starts | ✅ | ❌ | Faster startups for consecutive runs |
+| Auto-scaling | ✅ | ❌ | No need for manual worker node scaling |
+| Checkpoints | ✅ | ❌ | Non-blocking waits, less resource usage |
+| Dedicated support | ✅ | ❌ | Direct access to our support team |
+| Community support | ✅ | ✅ | Access to our Discord community |
+| ARM support | ✅ | ✅ | ARM-based deployments |
+
+
+## Limits
+
+Most of the [limits](/limits) are configurable when self-hosting, with some hardcoded exceptions. You can configure them via environment variables on the [webapp](/self-hosting/env/webapp) container.
+
+| Limit | Configurable | Hardcoded value |
+| :---------------- | :----------- | :-------------- |
+| Concurrency | ✅ | — |
+| Rate limits | ✅ | — |
+| Queued tasks | ✅ | — |
+| Task payloads | ✅ | — |
+| Batch payloads | ✅ | — |
+| Task outputs | ✅ | — |
+| Batch size | ✅ | — |
+| Log size | ✅ | — |
+| Machines | ✅ | — |
+| Log retention | — | Never deleted |
+| I/O packet length | ❌ | 128KB |
+| Alerts | ❌ | 100M |
+| Schedules | ❌ | 100M |
+| Team members | ❌ | 100M |
+| Preview branches | ❌ | 100M |
+
+{/* TODO: Maybe clarify re OFFLOAD_IO_PACKET_LENGTH_LIMIT */}
+
+{/* TODO: add machine overrides section */}
+
+## Community support
+
+It's dangerous to go alone! Join the self-hosting channel on our [Discord server](https://discord.gg/NQTxt5NA7s).
From 87aae09d24829f97007fa21e3e92e95652b6858a Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 12:54:23 +0100
Subject: [PATCH 05/21] add webapp env vars
---
docs/self-hosting/env/webapp.mdx | 135 +++++++++++++++++++++++++++++++
1 file changed, 135 insertions(+)
create mode 100644 docs/self-hosting/env/webapp.mdx
diff --git a/docs/self-hosting/env/webapp.mdx b/docs/self-hosting/env/webapp.mdx
new file mode 100644
index 0000000000..697cfa1755
--- /dev/null
+++ b/docs/self-hosting/env/webapp.mdx
@@ -0,0 +1,135 @@
+---
+title: "Webapp"
+description: "Environment variables for the webapp container."
+sidebarTitle: "Webapp"
+tag: "v4"
+mode: "wide"
+---
+
+| Name | Required | Default | Description |
+| :--------------------------------------------- | :------- | :-------------------- | :----------------------------------------------------------------- |
+| **Secrets** | | | |
+| `SESSION_SECRET` | Yes | — | Session encryption secret. Run: `openssl rand -hex 16` |
+| `MAGIC_LINK_SECRET` | Yes | — | Magic link encryption secret. Run: `openssl rand -hex 16` |
+| `ENCRYPTION_KEY` | Yes | — | Secret store encryption key. Run: `openssl rand -hex 16` |
+| `MANAGED_WORKER_SECRET` | No | managed-secret | Managed worker secret. Should be changed and match supervisor. |
+| **Domains & ports** | | | |
+| `REMIX_APP_PORT` | No | 3030 | Remix app port. |
+| `APP_ORIGIN` | Yes | http://localhost:3030 | App origin URL. |
+| `LOGIN_ORIGIN` | Yes | http://localhost:3030 | Login origin URL. Most likely the same as `APP_ORIGIN`. |
+| `API_ORIGIN` | No | `APP_ORIGIN` | API origin URL. |
+| `STREAM_ORIGIN` | No | `APP_ORIGIN` | Realtime stream origin URL. |
+| `ELECTRIC_ORIGIN` | No | http://localhost:3060 | Electric origin URL. |
+| **Postgres** | | | |
+| `DATABASE_URL` | Yes | — | PostgreSQL connection string. |
+| `DIRECT_URL` | Yes | — | Direct DB connection string used for migrations etc. |
+| `DATABASE_CONNECTION_LIMIT` | No | 10 | Max DB connections. |
+| `DATABASE_POOL_TIMEOUT` | No | 60 | DB pool timeout (s). |
+| `DATABASE_CONNECTION_TIMEOUT` | No | 20 | DB connect timeout (s). |
+| `DATABASE_READ_REPLICA_URL` | No | `DATABASE_URL` | Read-replica DB string. |
+| **Redis** | | | |
+| `REDIS_HOST` | Yes | — | Redis host. |
+| `REDIS_PORT` | Yes | — | Redis port. |
+| `REDIS_READER_HOST` | No | `REDIS_HOST` | Redis reader host. |
+| `REDIS_READER_PORT` | No | `REDIS_PORT` | Redis reader port. |
+| `REDIS_USERNAME` | No | — | Redis username. |
+| `REDIS_PASSWORD` | No | — | Redis password. |
+| `REDIS_TLS_DISABLED` | No | — | Disable Redis TLS. |
+| **Auth** | | | |
+| `WHITELISTED_EMAILS` | No | — | Whitelisted emails regex. |
+| `AUTH_GITHUB_CLIENT_ID` | No | — | GitHub client ID. |
+| `AUTH_GITHUB_CLIENT_SECRET` | No | — | GitHub client secret. |
+| **Email** | | | |
+| `EMAIL_TRANSPORT` | No | — | Email transport type. One of `resend`, `smtp`, `aws-ses`. |
+| `FROM_EMAIL` | No | — | From email address. |
+| `REPLY_TO_EMAIL` | No | — | Reply-to email address. |
+| `RESEND_API_KEY` | No | — | Resend API key. |
+| `SMTP_HOST` | No | — | SMTP host. |
+| `SMTP_PORT` | No | — | SMTP port. |
+| `SMTP_SECURE` | No | — | SMTP secure flag. |
+| `SMTP_USER` | No | — | SMTP user. |
+| `SMTP_PASSWORD` | No | — | SMTP password. |
+| `AWS_REGION` | No | — | AWS region for SES. |
+| `AWS_ACCESS_KEY_ID` | No | — | AWS access key ID for SES. |
+| `AWS_SECRET_ACCESS_KEY` | No | — | AWS secret access key for SES. |
+| **Graphile & Redis worker** | | | |
+| `WORKER_CONCURRENCY` | No | 10 | Redis worker concurrency. |
+| `WORKER_POLL_INTERVAL` | No | 1000 | Redis worker poll interval (ms). |
+| `WORKER_SCHEMA` | No | graphile_worker | Graphile worker schema. |
+| `GRACEFUL_SHUTDOWN_TIMEOUT` | No | 60000 (1m) | Graphile graceful shutdown timeout (ms). Affects shutdown time. |
+| **Concurrency limits** | | | |
+| `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT` | No | 100 | Default env execution concurrency. |
+| `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT` | No | 300 | Default org execution concurrency, needs to be 3x env concurrency. |
+| **Dev** | | | |
+| `DEV_MAX_CONCURRENT_RUNS` | No | 25 | Sets the max concurrency for dev runs via the CLI. |
+| `DEV_OTEL_EXPORTER_OTLP_ENDPOINT` | No | `APP_ORIGIN/otel` | OTel endpoint for dev runs. |
+| **Rate limiting** | | | |
+| `API_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | API rate limit refill interval. |
+| `API_RATE_LIMIT_MAX` | No | 750 | API rate limit max. |
+| `API_RATE_LIMIT_REFILL_RATE` | No | 250 | API rate limit refill rate. |
+| `API_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | API rate limit request logs. |
+| `API_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | API rate limit rejection logs. |
+| `API_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | API rate limit limiter logs. |
+| `API_RATE_LIMIT_JWT_WINDOW` | No | 1m | API rate limit JWT window. |
+| `API_RATE_LIMIT_JWT_TOKENS` | No | 60 | API rate limit JWT tokens. |
+| **Deploy & Registry** | | | |
+| `DEPLOY_REGISTRY_HOST` | Yes | — | Deploy registry host. |
+| `DEPLOY_REGISTRY_USERNAME` | No | — | Deploy registry username. |
+| `DEPLOY_REGISTRY_PASSWORD` | No | — | Deploy registry password. |
+| `DEPLOY_REGISTRY_NAMESPACE` | No | trigger | Deploy registry namespace. |
+| `DEPLOY_IMAGE_PLATFORM` | No | linux/amd64 | Deploy image platform, same values as docker `--platform` flag. |
+| `DEPLOY_TIMEOUT_MS` | No | 480000 (8m) | Deploy timeout (ms). |
+| **Object store (S3)** | | | |
+| `OBJECT_STORE_BASE_URL` | No | — | Object store base URL. |
+| `OBJECT_STORE_ACCESS_KEY_ID` | No | — | Object store access key. |
+| `OBJECT_STORE_SECRET_ACCESS_KEY` | No | — | Object store secret key. |
+| `OBJECT_STORE_REGION` | No | — | Object store region. |
+| `OBJECT_STORE_SERVICE` | No | s3 | Object store service. |
+| **Alerts** | | | |
+| `ORG_SLACK_INTEGRATION_CLIENT_ID` | No | — | Slack client ID. Required for Slack alerts. |
+| `ORG_SLACK_INTEGRATION_CLIENT_SECRET` | No | — | Slack client secret. Required for Slack alerts. |
+| `ALERT_EMAIL_TRANSPORT` | No | — | Alert email transport. |
+| `ALERT_FROM_EMAIL` | No | — | Alert from email. |
+| `ALERT_REPLY_TO_EMAIL` | No | — | Alert reply-to email. |
+| `ALERT_RESEND_API_KEY` | No | — | Alert Resend API key. |
+| `ALERT_SMTP_HOST` | No | — | Alert SMTP host. |
+| `ALERT_SMTP_PORT` | No | — | Alert SMTP port. |
+| `ALERT_SMTP_SECURE` | No | — | Alert SMTP secure. |
+| `ALERT_SMTP_USER` | No | — | Alert SMTP user. |
+| `ALERT_SMTP_PASSWORD` | No | — | Alert SMTP password. |
+| **Limits** | | | |
+| `TASK_PAYLOAD_OFFLOAD_THRESHOLD` | No | 524288 (512KB) | Max task payload size before offloading to S3. |
+| `TASK_PAYLOAD_MAXIMUM_SIZE` | No | 3145728 (3MB) | Max task payload size. |
+| `BATCH_TASK_PAYLOAD_MAXIMUM_SIZE` | No | 1000000 (1MB) | Max batch payload size. |
+| `TASK_RUN_METADATA_MAXIMUM_SIZE` | No | 262144 (256KB) | Max metadata size. |
+| `MAX_BATCH_V2_TRIGGER_ITEMS` | No | 500 | Max batch size. |
+| `MAXIMUM_DEV_QUEUE_SIZE` | No | — | Max dev queue size. |
+| `MAXIMUM_DEPLOYED_QUEUE_SIZE` | No | — | Max deployed queue size. |
+| **Realtime** | | | |
+| `REALTIME_STREAM_MAX_LENGTH` | No | 1000 | Realtime stream max length. |
+| `REALTIME_STREAM_TTL` | No | 86400 (1d) | Realtime stream TTL (s). |
+| **Bootstrap** | | | |
+| `TRIGGER_BOOTSTRAP_ENABLED` | No | 0 | Trigger bootstrap enabled. |
+| `TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME` | No | — | Trigger bootstrap worker group name. |
+| `TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH` | No | — | Trigger bootstrap worker token path. |
+| **Run engine** | | | |
+| `RUN_ENGINE_WORKER_COUNT` | No | 4 | Run engine worker count. |
+| `RUN_ENGINE_TASKS_PER_WORKER` | No | 10 | Run engine tasks per worker. |
+| `RUN_ENGINE_WORKER_CONCURRENCY_LIMIT` | No | 10 | Run engine worker concurrency limit. |
+| `RUN_ENGINE_WORKER_POLL_INTERVAL` | No | 100 | Run engine worker poll interval (ms). |
+| `RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL` | No | 100 | Run engine worker immediate poll interval (ms). |
+| `RUN_ENGINE_WORKER_SHUTDOWN_TIMEOUT_MS` | No | 60000 (1m) | Run engine worker shutdown timeout (ms). |
+| `RUN_ENGINE_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | Run engine rate limit refill interval. |
+| `RUN_ENGINE_RATE_LIMIT_MAX` | No | 1200 | Run engine rate limit max. |
+| `RUN_ENGINE_RATE_LIMIT_REFILL_RATE` | No | 400 | Run engine rate limit refill rate. |
+| `RUN_ENGINE_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | Run engine rate limit request logs. |
+| `RUN_ENGINE_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | Run engine rate limit rejection logs. |
+| `RUN_ENGINE_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | Run engine rate limit limiter logs. |
+| **Misc** | | | |
+| `TRIGGER_TELEMETRY_DISABLED` | No | — | Disable telemetry. |
+| `NODE_MAX_OLD_SPACE_SIZE` | No | — | Maximum memory allocation for Node.js heap (e.g. "4096" for 4GB). |
+| `OPENAI_API_KEY` | No | — | OpenAI API key. |
+| `MACHINE_PRESETS_OVERRIDE_PATH` | No | — | Path to machine presets override file. |
+| `APP_ENV` | No | `NODE_ENV` | App environment. Used for things like the title tag. |
+| `ADMIN_EMAILS` | No | — | Regex of user emails to automatically promote to admin. |
+| `EVENT_LOOP_MONITOR_ENABLED` | No | 1 | Node.js event loop lag monitor. |
\ No newline at end of file
From d9d71efe192f8cd68798146ae5e51ea5849b46b1 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 13:30:21 +0100
Subject: [PATCH 06/21] overview tweaks
---
docs/self-hosting/overview.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx
index cf2a1b3b00..0e60d8f169 100644
--- a/docs/self-hosting/overview.mdx
+++ b/docs/self-hosting/overview.mdx
@@ -12,13 +12,13 @@ We provide version-tagged releases for self-hosted deployments. It's highly advi
## Should you self-host?
-For most users, Trigger.dev Cloud offers the best experience - it's fully managed, scalable, and comes with dedicated support. However, if you have specific requirements around data residency, compliance, or infrastructure control, self-hosting may be the right choice for you.
+Trigger.dev Cloud is fully managed, scalable, and comes with dedicated support. For most users, it offers the best experience. However, if you have specific requirements around data residency, compliance, or infrastructure control, self-hosting may be the right choice for you.
The self-hosted version is functionally the same as Trigger.dev Cloud with [some exceptions](#feature-comparison), but our managed Cloud infrastructure is designed for high availability, security, and scale.
-Because we do not manage self-hosted instances, we cannot guarantee how Trigger.dev will perform on your infrastructure. You assume all responsibility and risk for your deployment, including security, uptime, and data integrity.
+Because we don't manage self-hosted instances, we cannot guarantee how Trigger.dev will perform on your infrastructure. You assume all responsibility and risk for your deployment, including security, uptime, and data integrity.
-You can read the rest of this overview and follow our guides for instructions on setting up a self-hosted Trigger.dev instance. If you prefer a managed experience, you can [sign up](https://cloud.trigger.dev/login) for our Cloud offering instead - we have a generous [free tier](https://trigger.dev/pricing) for you to try it out.
+For more details, carry on reading and follow our guides for instructions on setting up a self-hosted Trigger.dev instance. If you prefer a managed experience, you can [sign up](https://cloud.trigger.dev/login) for our Cloud offering instead - we have a generous [free tier](https://trigger.dev/pricing) for you to try it out.
{/* TODO: Architecture section with updated diagram */}
From 6c1ffd594f0e163c7d29f999c8d766113f1ca064 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 13:45:42 +0100
Subject: [PATCH 07/21] add supervisor env vars
---
docs/self-hosting/env/supervisor.mdx | 75 ++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 docs/self-hosting/env/supervisor.mdx
diff --git a/docs/self-hosting/env/supervisor.mdx b/docs/self-hosting/env/supervisor.mdx
new file mode 100644
index 0000000000..a624469ec6
--- /dev/null
+++ b/docs/self-hosting/env/supervisor.mdx
@@ -0,0 +1,75 @@
+---
+title: "Supervisor"
+description: "Environment variables for the supervisor container."
+sidebarTitle: "Supervisor"
+tag: "v4"
+mode: "wide"
+---
+
+| Name | Required | Default | Description |
+| :------------------------------------------ | :------- | :---------- | :---------------------------------------------------------- |
+| **Required settings** | | | |
+| `TRIGGER_API_URL` | Yes | — | Trigger.dev API URL. Should point at the webapp. |
+| `TRIGGER_WORKER_TOKEN` | Yes | — | Worker token (can be a file path with file://). |
+| `MANAGED_WORKER_SECRET` | Yes | — | Managed worker secret. Needs to match webapp value. |
+| `OTEL_EXPORTER_OTLP_ENDPOINT` | Yes | — | OTel exporter endpoint. Point at: `/otel` |
+| **Worker instance** | | | |
+| `TRIGGER_WORKER_INSTANCE_NAME` | No | random UUID | Worker instance name. Set to `spec.nodeName` on k8s. |
+| `TRIGGER_WORKER_HEARTBEAT_INTERVAL_SECONDS` | No | 30 | Worker heartbeat interval (seconds). |
+| **Workload API settings** | | | |
+| `TRIGGER_WORKLOAD_API_ENABLED` | No | true | Enable workload API. Runs use this to perform actions. |
+| `TRIGGER_WORKLOAD_API_PROTOCOL` | No | http | Workload API protocol (http/https). |
+| `TRIGGER_WORKLOAD_API_DOMAIN` | No | — | Workload API domain. Keep empty for auto-detection. |
+| `TRIGGER_WORKLOAD_API_HOST_INTERNAL` | No | 0.0.0.0 | Workload API internal host. |
+| `TRIGGER_WORKLOAD_API_PORT_INTERNAL` | No | 8020 | Workload API internal port. |
+| `TRIGGER_WORKLOAD_API_PORT_EXTERNAL` | No | 8020 | Workload API external port. |
+| **Runner settings** | | | |
+| `RUNNER_HEARTBEAT_INTERVAL_SECONDS` | No | — | Runner heartbeat interval (seconds). |
+| `RUNNER_SNAPSHOT_POLL_INTERVAL_SECONDS` | No | — | Runner snapshot poll interval (seconds). |
+| `RUNNER_ADDITIONAL_ENV_VARS` | No | — | Additional runner env vars (CSV). |
+| `RUNNER_PRETTY_LOGS` | No | false | Pretty logs for runner. |
+| **Dequeue settings** | | | |
+| `TRIGGER_DEQUEUE_ENABLED` | No | true | Enable dequeue to pull runs from the queue. |
+| `TRIGGER_DEQUEUE_INTERVAL_MS` | No | 250 | Dequeue interval (ms). |
+| `TRIGGER_DEQUEUE_IDLE_INTERVAL_MS` | No | 1000 (1s) | Dequeue idle interval (ms). |
+| `TRIGGER_DEQUEUE_MAX_RUN_COUNT` | No | 10 | Max dequeue run count. |
+| `TRIGGER_DEQUEUE_MAX_CONSUMER_COUNT` | No | 1 | Max dequeue consumer count. |
+| **Docker settings** | | | |
+| `DOCKER_API_VERSION` | No | v1.41 | Docker API version. You should probably not touch this. |
+| `DOCKER_STRIP_IMAGE_DIGEST` | No | true | Strip image digest in Docker. Turning off can cause issues. |
+| `DOCKER_ENFORCE_MACHINE_PRESETS` | No | true | Enforce Docker machine cpu and memory limits. |
+| `DOCKER_AUTOREMOVE_EXITED_CONTAINERS` | No | true | Auto-remove exited containers. |
+| `DOCKER_RUNNER_NETWORKS` | No | host | Docker runner networks (CSV). |
+| **Registry auth** | | | |
+| `DOCKER_REGISTRY_URL` | No | — | Docker registry URL, e.g. `docker.io`. |
+| `DOCKER_REGISTRY_USERNAME` | No | — | Docker registry username. |
+| `DOCKER_REGISTRY_PASSWORD` | No | — | Docker registry password. |
+| **Kubernetes settings** | | | |
+| `KUBERNETES_FORCE_ENABLED` | No | false | Force Kubernetes mode. |
+| `KUBERNETES_NAMESPACE` | No | default | The namespace that runs should be in. |
+| `KUBERNETES_WORKER_NODETYPE_LABEL` | No | v4-worker | Nodes for runs need this label, e.g. `nodetype=v4-worker`. |
+| `KUBERNETES_IMAGE_PULL_SECRETS` | No | — | Image pull secrets (CSV). |
+| `KUBERNETES_EPHEMERAL_STORAGE_SIZE_LIMIT` | No | 10Gi | Ephemeral storage size limit. Applies to all runs. |
+| `KUBERNETES_EPHEMERAL_STORAGE_SIZE_REQUEST` | No | 2Gi | Ephemeral storage size request. Applies to all runs. |
+| **Metrics** | | | |
+| `METRICS_ENABLED` | No | true | Enable metrics. |
+| `METRICS_COLLECT_DEFAULTS` | No | true | Collect default metrics. |
+| `METRICS_HOST` | No | 127.0.0.1 | Metrics host. |
+| `METRICS_PORT` | No | 9090 | Metrics port. |
+| **Pod cleaner** | | | |
+| `POD_CLEANER_ENABLED` | No | true | Enable pod cleaner. |
+| `POD_CLEANER_INTERVAL_MS` | No | 10000 (10s) | Pod cleaner interval (ms). Best not to touch this. |
+| `POD_CLEANER_BATCH_SIZE` | No | 500 | Pod cleaner batch size. |
+| **Failed pod handler** | | | |
+| `FAILED_POD_HANDLER_ENABLED` | No | true | Enable failed pod handler. |
+| `FAILED_POD_HANDLER_RECONNECT_INTERVAL_MS` | No | 1000 (1s) | Failed pod handler reconnect interval (ms). |
+| **Debug** | | | |
+| `DEBUG` | No | false | Enable debug logs. |
+| `SEND_RUN_DEBUG_LOGS` | No | false | Send run debug logs to the platform. |
+| **Not used for self-hosting** | | | |
+| `TRIGGER_WARM_START_URL` | No | — | Warm start URL. |
+| `TRIGGER_CHECKPOINT_URL` | No | — | Checkpoint URL. |
+| `TRIGGER_METADATA_URL` | No | — | Metadata URL. |
+| `RESOURCE_MONITOR_ENABLED` | No | false | Enable resource monitor. |
+| `RESOURCE_MONITOR_OVERRIDE_CPU_TOTAL` | No | — | Override CPU total for resource monitor. |
+| `RESOURCE_MONITOR_OVERRIDE_MEMORY_TOTAL_GB` | No | — | Override memory total (GB) for resource monitor. |
From be5c4118916861136d3d41e096f99333fedb1b6f Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 13:45:54 +0100
Subject: [PATCH 08/21] move old docker guide
---
docs/open-source-self-hosting.mdx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/docs/open-source-self-hosting.mdx b/docs/open-source-self-hosting.mdx
index d2e785093d..a3c56274ec 100644
--- a/docs/open-source-self-hosting.mdx
+++ b/docs/open-source-self-hosting.mdx
@@ -1,8 +1,10 @@
---
-title: "Self-hosting"
-description: "You can self-host Trigger.dev on your own infrastructure."
+title: "Docker (legacy)"
+description: "You can self-host Trigger.dev on your own infrastructure using Docker."
---
+This guide is for v3, you can find the v4 guide [here](/self-hosting/docker).
+
Security, scaling, and reliability concerns are not fully addressed here. This guide is meant for evaluation purposes and won't result in a production-ready deployment.
This guide is for Docker only. We don't currently provide documentation for Kubernetes.
From 955483eb8b3a3323a0b515f5543c90bc0b5c2a3a Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 13:47:09 +0100
Subject: [PATCH 09/21] new sidebar structure
---
docs/docs.json | 20 ++++++++++++++------
docs/self-hosting/docker.mdx | 13 +++++++++++++
2 files changed, 27 insertions(+), 6 deletions(-)
create mode 100644 docs/self-hosting/docker.mdx
diff --git a/docs/docs.json b/docs/docs.json
index 12916eccbf..71f83cf500 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -171,15 +171,23 @@
]
},
{
- "group": "Open source",
+ "group": "Self-hosting",
"pages": [
- "open-source-self-hosting",
- "open-source-contributing",
- "github-repo",
- "changelog",
- "roadmap"
+ "self-hosting/overview",
+ "self-hosting/docker",
+ {
+ "group": "Environment variables",
+ "pages": ["self-hosting/env/webapp", "self-hosting/env/supervisor"],
+ "tags": ["v4"],
+ "tag": "v4"
+ },
+ "open-source-self-hosting"
]
},
+ {
+ "group": "Open source",
+ "pages": ["open-source-contributing", "github-repo", "changelog", "roadmap"]
+ },
{
"group": "Help",
"pages": ["community", "help-slack", "help-email"]
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
new file mode 100644
index 0000000000..22a0c82800
--- /dev/null
+++ b/docs/self-hosting/docker.mdx
@@ -0,0 +1,13 @@
+---
+title: "Docker compose"
+description: "You can self-host Trigger.dev on your own infrastructure using Docker."
+tag: "v4"
+---
+
+## Introduction
+
+This guide will use docker compose to spin up a Trigger.dev instance. Make sure to read the [overview](/self-hosting/overview) first.
+
+We've split the compose files into Webapp and Worker components so you can easily run them independently. This will also allow you to scale your workers as needed.
+
+**Warning:** This guide alone is unlikely to result in a production-ready deployment. Security, scaling, and reliability concerns are not fully addressed here.
From 19fbad52fefa18f60ebf9e84f2377005c143afcd Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 13:55:49 +0100
Subject: [PATCH 10/21] update github actions docs
---
docs/github-actions.mdx | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/github-actions.mdx b/docs/github-actions.mdx
index 0732f5c4f1..698c1e735e 100644
--- a/docs/github-actions.mdx
+++ b/docs/github-actions.mdx
@@ -130,6 +130,10 @@ When self-hosting, you will have to take a few additional steps:
- Add your registry credentials to the GitHub secrets.
- Use the `--self-hosted` and `--push` flags when deploying.
+
+ If you're self-hosting v4, the `--self-hosted` and `--push` flags are **NOT** needed.
+
+
Other than that, your GitHub action file will look very similar to the one above:
From 15c6d450b934a9dc960b8a28bee5e6945eb93264 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 14:49:32 +0100
Subject: [PATCH 11/21] docker draft
---
docs/self-hosting/docker.mdx | 183 +++++++++++++++++++++++++++++++++++
1 file changed, 183 insertions(+)
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index 22a0c82800..0d1ed5fd10 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -11,3 +11,186 @@ This guide will use docker compose to spin up a Trigger.dev instance. Make sure
We've split the compose files into Webapp and Worker components so you can easily run them independently. This will also allow you to scale your workers as needed.
**Warning:** This guide alone is unlikely to result in a production-ready deployment. Security, scaling, and reliability concerns are not fully addressed here.
+
+## Caveats
+
+As self-hosted deployments tend to have unique requirements and configurations, we don't provide specific advice for securing your deployment, scaling up, or improving reliability.
+
+Should the burden ever get too much, we'd be happy to see you on [Trigger.dev cloud](https://trigger.dev/pricing) where we deal with these concerns for you.
+
+## Comparison with v3
+
+We made quite a few changes:
+- **Support for multiple worker machines.** This is a big one, and we're very excited about it! You can now scale your workers horizontally as needed.
+- **Resource limits enforced by default.** This means that tasks will be limited to the total CPU and RAM of the machine, preventing noisy neighbours.
+- **No direct Docker socket access.** The compose file now comes with [Docker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy) by default.
+- **No host networking.** All containers are now running with network isolation, using only the network access they need.
+- **No checkpoint support.** This was only ever an experimental feature and not recommended, it caused a bunch of issues. We decided to focus on the core features and remove it.
+- **Built-in container registry and object storage.** You can now deploy and execute tasks without needing third party services for this.
+- **Improved CLI commands.** You don't need any additional flags to deploy anymore, and there's a new `switch` command to easily switch between profiles.
+
+{/* TODO: requirements */}
+
+{/* TODO: setup */}
+
+{/* TODO: multiple workers */}
+
+{/* TODO: Upgrading from v3 */}
+
+## Version locking
+
+There are several reasons to lock the version of your Docker images:
+- **Backwards compatibility.** We try our best to maintain compatibility with older CLI versions, but it's not always possible. If you don't want to update your CLI, you can lock your Docker images to that specific version.
+- **Ensuring full feature support.** Sometimes, new CLI releases will also require new or updated platform features. Running unlocked images can make any issues difficult to debug. Using a specific tag can help here as well.
+
+By default, the images will point at the latest versioned release via the `v4-beta` tag. You can override this by specifying a different tag in your `.env` file. For example:
+
+```bash
+TRIGGER_IMAGE_TAG=v4.0.0-v4-beta.21
+```
+
+## Authentication
+
+### Magic link
+
+By default, magic link auth is the only login option. If the `EMAIL_TRANSPORT` env var is not set, the magic links will be logged by the webapp container and not sent via email.
+
+The specific set of variables required will depend on your choice of email transport.
+
+#### Resend
+
+```bash
+EMAIL_TRANSPORT=resend
+FROM_EMAIL=
+REPLY_TO_EMAIL=
+RESEND_API_KEY=
+```
+
+#### SMTP
+
+Note that setting `SMTP_SECURE=false` does _not_ mean the email is sent insecurely.
+This simply means that the connection is secured using the modern STARTTLS protocol command instead of implicit TLS.
+You should only set this to true when the SMTP server host directs you to do so (generally when using port 465)
+
+```bash
+EMAIL_TRANSPORT=smtp
+FROM_EMAIL=
+REPLY_TO_EMAIL=
+SMTP_HOST=
+SMTP_PORT=587
+SMTP_SECURE=false
+SMTP_USER=
+SMTP_PASSWORD=
+```
+
+#### AWS SES
+
+Credentials are to be supplied as with any other program using the AWS SDK.
+
+In this scenario, you would likely either supply the additional environment variables `AWS_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` or, when running on AWS, use credentials supplied by the EC2 IMDS.
+
+```bash
+EMAIL_TRANSPORT=aws-ses
+FROM_EMAIL=
+REPLY_TO_EMAIL=
+```
+
+### GitHub OAuth
+
+To authenticate with GitHub, you will need to set up a GitHub OAuth app. It needs a callback URL `https:///auth/github/callback` and you will have to set the following env vars:
+
+```bash
+AUTH_GITHUB_CLIENT_ID=
+AUTH_GITHUB_CLIENT_SECRET=
+```
+
+### Restricting access
+
+All email addresses can sign up and log in this way. If you would like to restrict this, you can use the `WHITELISTED_EMAILS` env var. For example:
+
+```bash
+# every email that does not match this regex will be rejected
+WHITELISTED_EMAILS="authorized@yahoo\.com|authorized@gmail\.com"
+```
+
+This will apply to all auth methods.
+
+## CLI usage
+
+This section highlights some of the CLI commands and options that are useful when self-hosting. Please check the [CLI reference](/cli-introduction) for more in-depth documentation.
+
+### Login
+
+To avoid being redirected to [Trigger.dev Cloud](https://cloud.trigger.dev) when using the CLI, you need to specify the URL of your self-hosted instance with the `--api-url` or `-a` flag. For example:
+
+```bash
+npx trigger.dev@v4-beta login -a http://trigger.example.com
+```
+
+Once you've logged in, you shouldn't have to specify the URL again with other commands.
+
+#### Profiles
+
+You can specify a profile when logging in. This allows you to easily use the CLI with multiple instances of Trigger.dev. For example:
+
+```bash
+npx trigger.dev@v4-beta login -a http://trigger.example.com \
+ --profile self-hosted
+```
+
+Logging in with a new profile will also make it the new default profile.
+
+To use a specific profile, you can use the `--profile` flag with other commands:
+
+```bash
+npx trigger.dev@v4-beta dev --profile self-hosted
+```
+
+To list all your profiles, use the `list-profiles` command:
+
+```bash
+npx trigger.dev@v4-beta list-profiles
+```
+
+To remove a profile, use the `logout` command:
+
+```bash
+npx trigger.dev@v4-beta logout --profile self-hosted
+```
+
+To switch to a different profile, use the `switch` command:
+
+```bash
+# To run interactively
+npx trigger.dev@v4-beta switch
+
+# To switch to a specific profile
+npx trigger.dev@v4-beta switch self-hosted
+```
+
+#### Whoami
+
+It can be useful to check you are logged into the correct instance. Running this will also show the API URL:
+
+```bash
+npx trigger.dev@v4-beta whoami
+```
+
+### CI / GitHub Actions
+
+When running the CLI in a CI environment, your login profiles won't be available. Instead, you can use the `TRIGGER_API_URL` and `TRIGGER_ACCESS_TOKEN` environment
+variables to point at your self-hosted instance and authenticate.
+
+For more detailed instructions, see the [GitHub Actions guide](/github-actions).
+
+## Telemetry
+
+By default, the Trigger.dev webapp sends telemetry data to our servers. This data is used to improve the product and is not shared with third parties. If you would like to opt-out of this, you can set the `TRIGGER_TELEMETRY_DISABLED` environment variable on the webapp container. The value doesn't matter, it just can't be empty. For example:
+
+```yaml
+services:
+ webapp:
+ ...
+ environment:
+ TRIGGER_TELEMETRY_DISABLED: 1
+```
From 1f45afd51a2c9b5e9bbca03246d568986a020fbb Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 17:57:52 +0100
Subject: [PATCH 12/21] use env vars for s3 creds
---
hosting/docker/docker-compose.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hosting/docker/docker-compose.yml b/hosting/docker/docker-compose.yml
index 12c1b4f23e..15088986a2 100644
--- a/hosting/docker/docker-compose.yml
+++ b/hosting/docker/docker-compose.yml
@@ -42,8 +42,8 @@ services:
TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH: /home/node/shared/worker_token
DEPLOY_REGISTRY_HOST: localhost:5000
OBJECT_STORE_BASE_URL: http://minio:9000
- OBJECT_STORE_ACCESS_KEY_ID: BlJ2Hcd6Cgsz3ITBWy4p
- OBJECT_STORE_SECRET_ACCESS_KEY: RskeTL9VAQ7saSSI2hK1MUMaaAIavf7vyQyVPiDu
+ OBJECT_STORE_ACCESS_KEY_ID: ${OBJECT_STORE_ACCESS_KEY_ID}
+ OBJECT_STORE_SECRET_ACCESS_KEY: ${OBJECT_STORE_SECRET_ACCESS_KEY}
GRACEFUL_SHUTDOWN_TIMEOUT: 1000
# Limits
# TASK_PAYLOAD_OFFLOAD_THRESHOLD: 524288 # 512KB
From b356c79eb7eb7734f19a588dc4f0a39a437148aa Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 17:58:32 +0100
Subject: [PATCH 13/21] this might just work
---
docs/docs.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/docs.json b/docs/docs.json
index 71f83cf500..a7c84d2658 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -182,7 +182,9 @@
"tag": "v4"
},
"open-source-self-hosting"
- ]
+ ],
+ "tags": ["v4"],
+ "tag": "v4"
},
{
"group": "Open source",
From 39b0cf27ff067c49e870c04818b69672bc784444 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 19:02:46 +0100
Subject: [PATCH 14/21] split into multiple files
---
hosting/docker/docker-compose.traefik.yml | 64 +++++++++++++
...-compose.yml => docker-compose.webapp.yml} | 96 -------------------
hosting/docker/docker-compose.worker.yml | 72 ++++++++++++++
3 files changed, 136 insertions(+), 96 deletions(-)
create mode 100644 hosting/docker/docker-compose.traefik.yml
rename hosting/docker/{docker-compose.yml => docker-compose.webapp.yml} (62%)
create mode 100644 hosting/docker/docker-compose.worker.yml
diff --git a/hosting/docker/docker-compose.traefik.yml b/hosting/docker/docker-compose.traefik.yml
new file mode 100644
index 0000000000..2bd8d602f0
--- /dev/null
+++ b/hosting/docker/docker-compose.traefik.yml
@@ -0,0 +1,64 @@
+name: trigger
+
+services:
+ webapp:
+ networks:
+ - traefik
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.webapp.rule=Host(`webapp.localhost`)"
+ - "traefik.http.routers.webapp.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
+ # - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
+ - "traefik.http.services.webapp.loadbalancer.server.port=3000"
+
+ registry:
+ networks:
+ - traefik
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.registry.rule=Host(`registry.localhost`)"
+ - "traefik.http.routers.registry.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
+ # - "traefik.http.routers.registry.tls.certresolver=letsencrypt"
+ - "traefik.http.services.registry.loadbalancer.server.port=5000"
+
+ minio:
+ networks:
+ - traefik
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.minio.rule=Host(`minio.localhost`)"
+ - "traefik.http.routers.minio.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
+ # - "traefik.http.routers.minio.tls.certresolver=letsencrypt"
+ - "traefik.http.services.minio.loadbalancer.server.port=9000"
+
+ traefik:
+ image: traefik:${TRAEFIK_IMAGE_TAG:-v3.4}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ ports:
+ - "80:80"
+ - "443:443"
+ - "8080:8080" # Traefik dashboard
+ networks:
+ - traefik
+ command:
+ - --api.insecure=true
+ - --providers.docker=true
+ - --providers.docker.exposedbydefault=false
+ - --providers.docker.network=traefik
+ - --entrypoints.web.address=:80
+ - --entrypoints.websecure.address=:443
+ # - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
+ # - --certificatesresolvers.letsencrypt.acme.email=local@example.com
+ # - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
+ - --log.level=DEBUG
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ - traefik-letsencrypt:/letsencrypt
+
+volumes:
+ minio:
+ traefik-letsencrypt:
+
+networks:
+ traefik:
+ name: traefik
diff --git a/hosting/docker/docker-compose.yml b/hosting/docker/docker-compose.webapp.yml
similarity index 62%
rename from hosting/docker/docker-compose.yml
rename to hosting/docker/docker-compose.webapp.yml
index 15088986a2..af3d7f88ed 100644
--- a/hosting/docker/docker-compose.yml
+++ b/hosting/docker/docker-compose.webapp.yml
@@ -12,7 +12,6 @@ services:
networks:
- webapp
- supervisor
- - traefik
volumes:
- shared:/home/node/shared
user: root
@@ -52,74 +51,6 @@ services:
# TASK_RUN_METADATA_MAXIMUM_SIZE: 262144 # 256KB
# DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT: 100
# DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT: 100
- labels:
- - "traefik.enable=true"
- - "traefik.http.routers.webapp.rule=Host(`webapp.localhost`)"
- - "traefik.http.routers.webapp.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- # - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
- - "traefik.http.services.webapp.loadbalancer.server.port=3000"
-
- supervisor:
- image: ghcr.io/triggerdotdev/supervisor:${TRIGGER_IMAGE_TAG:-main}
- restart: ${RESTART_POLICY:-unless-stopped}
- depends_on:
- - webapp
- - docker-proxy
- networks:
- - supervisor
- - docker-proxy
- volumes:
- - shared:/home/node/shared
- user: root
- command: sh -c "chown -R node:node /home/node/shared && exec /usr/bin/dumb-init -- pnpm run --filter supervisor start"
- environment:
- # This needs to match the token of the worker group you want to connect to
- # TRIGGER_WORKER_TOKEN: ${TRIGGER_WORKER_TOKEN}
- TRIGGER_WORKER_TOKEN: file:///home/node/shared/worker_token
- MANAGED_WORKER_SECRET: ${MANAGED_WORKER_SECRET}
- # Point this at the webapp in prod
- TRIGGER_API_URL: http://webapp:3000
- # Point this at the OTel collector or the webapp in prod
- OTEL_EXPORTER_OTLP_ENDPOINT: http://webapp:3000/otel
- TRIGGER_WORKLOAD_API_DOMAIN: supervisor
- TRIGGER_WORKLOAD_API_PORT_EXTERNAL: 8020
- # Optional settings
- DEBUG: 1
- ENFORCE_MACHINE_PRESETS: 1
- TRIGGER_DEQUEUE_INTERVAL_MS: 1000
- DOCKER_HOST: tcp://docker-proxy:2375
- DOCKER_RUNNER_NETWORKS: webapp,supervisor
- DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-localhost:5000}
- DOCKER_REGISTRY_USERNAME: ${DOCKER_REGISTRY_USERNAME:-}
- DOCKER_REGISTRY_PASSWORD: ${DOCKER_REGISTRY_PASSWORD:-}
- DOCKER_AUTOREMOVE_EXITED_CONTAINERS: 0
- healthcheck:
- test: ["CMD", "node", "-e", "http.get('http://localhost:8020/health', res => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 10s
-
- docker-proxy:
- image: tecnativa/docker-socket-proxy:${DOCKER_PROXY_IMAGE_TAG:-latest}
- restart: ${RESTART_POLICY:-unless-stopped}
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock:ro
- networks:
- - docker-proxy
- environment:
- - LOG_LEVEL=info
- - POST=1
- - CONTAINERS=1
- - IMAGES=1
- - INFO=1
- - NETWORKS=1
- healthcheck:
- test: ["CMD", "nc", "-z", "127.0.0.1", "2375"]
- interval: 30s
- timeout: 5s
- retries: 5
- start_period: 5s
postgres:
image: postgres:${POSTGRES_IMAGE_TAG:-14}
@@ -240,41 +171,14 @@ services:
retries: 5
start_period: 10s
- traefik:
- image: traefik:${TRAEFIK_IMAGE_TAG:-v3.4}
- restart: ${RESTART_POLICY:-unless-stopped}
- ports:
- - "80:80"
- - "443:443"
- - "8080:8080" # Traefik dashboard
- networks:
- - traefik
- command:
- - --api.insecure=true
- - --providers.docker=true
- - --providers.docker.exposedbydefault=false
- - --providers.docker.network=traefik
- - --entrypoints.web.address=:80
- - --entrypoints.websecure.address=:443
- # - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
- # - --certificatesresolvers.letsencrypt.acme.email=local@foo.bar
- # - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- - --log.level=DEBUG
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock:ro
- - traefik-letsencrypt:/letsencrypt
-
volumes:
clickhouse:
postgres:
redis:
shared:
minio:
- traefik-letsencrypt:
networks:
- traefik:
- name: traefik
docker-proxy:
name: docker-proxy
supervisor:
diff --git a/hosting/docker/docker-compose.worker.yml b/hosting/docker/docker-compose.worker.yml
new file mode 100644
index 0000000000..e48ed84ab1
--- /dev/null
+++ b/hosting/docker/docker-compose.worker.yml
@@ -0,0 +1,72 @@
+name: trigger
+
+services:
+ supervisor:
+ image: ghcr.io/triggerdotdev/supervisor:${TRIGGER_IMAGE_TAG:-main}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ depends_on:
+ - docker-proxy
+ networks:
+ - supervisor
+ - docker-proxy
+ volumes:
+ - shared:/home/node/shared
+ user: root
+ command: sh -c "chown -R node:node /home/node/shared && exec /usr/bin/dumb-init -- pnpm run --filter supervisor start"
+ environment:
+ # This needs to match the token of the worker group you want to connect to
+ # TRIGGER_WORKER_TOKEN: ${TRIGGER_WORKER_TOKEN}
+ TRIGGER_WORKER_TOKEN: file:///home/node/shared/worker_token
+ MANAGED_WORKER_SECRET: ${MANAGED_WORKER_SECRET}
+ # Point this at the webapp in prod
+ TRIGGER_API_URL: http://webapp:3000
+ # Point this at the OTel collector or the webapp in prod
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://webapp:3000/otel
+ TRIGGER_WORKLOAD_API_DOMAIN: supervisor
+ TRIGGER_WORKLOAD_API_PORT_EXTERNAL: 8020
+ # Optional settings
+ DEBUG: 1
+ ENFORCE_MACHINE_PRESETS: 1
+ TRIGGER_DEQUEUE_INTERVAL_MS: 1000
+ DOCKER_HOST: tcp://docker-proxy:2375
+ DOCKER_RUNNER_NETWORKS: webapp,supervisor
+ DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-localhost:5000}
+ DOCKER_REGISTRY_USERNAME: ${DOCKER_REGISTRY_USERNAME:-}
+ DOCKER_REGISTRY_PASSWORD: ${DOCKER_REGISTRY_PASSWORD:-}
+ DOCKER_AUTOREMOVE_EXITED_CONTAINERS: 0
+ healthcheck:
+ test: ["CMD", "node", "-e", "http.get('http://localhost:8020/health', res => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ start_period: 10s
+
+ docker-proxy:
+ image: tecnativa/docker-socket-proxy:${DOCKER_PROXY_IMAGE_TAG:-latest}
+ restart: ${RESTART_POLICY:-unless-stopped}
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ networks:
+ - docker-proxy
+ environment:
+ - LOG_LEVEL=info
+ - POST=1
+ - CONTAINERS=1
+ - IMAGES=1
+ - INFO=1
+ - NETWORKS=1
+ healthcheck:
+ test: ["CMD", "nc", "-z", "127.0.0.1", "2375"]
+ interval: 30s
+ timeout: 5s
+ retries: 5
+ start_period: 5s
+
+volumes:
+ shared:
+
+networks:
+ docker-proxy:
+ name: docker-proxy
+ supervisor:
+ name: supervisor
From 0b55255ab7de4be4a119bc9035a865f1775f76b6 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 19:21:12 +0100
Subject: [PATCH 15/21] update guide
---
docs/self-hosting/docker.mdx | 140 ++++++++++++++++++++++++++++++-----
1 file changed, 122 insertions(+), 18 deletions(-)
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index 0d1ed5fd10..83ecec2229 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -4,38 +4,142 @@ description: "You can self-host Trigger.dev on your own infrastructure using Doc
tag: "v4"
---
-## Introduction
-
-This guide will use docker compose to spin up a Trigger.dev instance. Make sure to read the [overview](/self-hosting/overview) first.
-
-We've split the compose files into Webapp and Worker components so you can easily run them independently. This will also allow you to scale your workers as needed.
-
-**Warning:** This guide alone is unlikely to result in a production-ready deployment. Security, scaling, and reliability concerns are not fully addressed here.
-
-## Caveats
+The following instructions will use docker compose to spin up a Trigger.dev instance. Make sure to read the self-hosting [overview](/self-hosting/overview) first.
As self-hosted deployments tend to have unique requirements and configurations, we don't provide specific advice for securing your deployment, scaling up, or improving reliability.
Should the burden ever get too much, we'd be happy to see you on [Trigger.dev cloud](https://trigger.dev/pricing) where we deal with these concerns for you.
-## Comparison with v3
+**Warning:** This guide alone is unlikely to result in a production-ready deployment. Security, scaling, and reliability concerns are not fully addressed here.
+
+## What's new?
-We made quite a few changes:
+Goodbye v3, hello v4! We made quite a few changes:
+- **Much simpler setup.** Provider + coordinator = supervisor. No more startup scripts. Just `docker compose up`.
- **Support for multiple worker machines.** This is a big one, and we're very excited about it! You can now scale your workers horizontally as needed.
-- **Resource limits enforced by default.** This means that tasks will be limited to the total CPU and RAM of the machine, preventing noisy neighbours.
-- **No direct Docker socket access.** The compose file now comes with [Docker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy) by default.
+- **Resource limits enforced by default.** This means that tasks will be limited to the total CPU and RAM of the machine preset, preventing noisy neighbours.
+- **No direct Docker socket access.** The compose file now comes with [Docker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy) by default. Yes, you want this.
- **No host networking.** All containers are now running with network isolation, using only the network access they need.
-- **No checkpoint support.** This was only ever an experimental feature and not recommended, it caused a bunch of issues. We decided to focus on the core features and remove it.
+- **No checkpoint support.** This was only ever an experimental self-hosting feature and not recommended. It caused a bunch of issues. We decided to focus on the core features instead.
- **Built-in container registry and object storage.** You can now deploy and execute tasks without needing third party services for this.
- **Improved CLI commands.** You don't need any additional flags to deploy anymore, and there's a new `switch` command to easily switch between profiles.
+- **Whitelisting for GitHub OAuth.** Any whitelisted email addresses will now also apply to sign ins via GitHub, unlike v3 where they only applied to magic links.
+
+## Requirements
+
+These are the minimum requirements for running the webapp and worker components. They can run on the same, or on separate machines.
+
+It's fine to run everything on the same machine for testing. To be able to scale your workers, you will want to run them separately.
+
+### Prerequisites
+
+To run the webapp and worker components, you will need:
+
+- [Docker](https://docs.docker.com/get-docker/) 20.10.0+
+- [Docker Compose](https://docs.docker.com/compose/install/) 2.20.0+
+
+### Webapp
+
+This will host the webapp, postgres, redis, and related services.
+
+- 2+ vCPU
+- 4+ GB RAM
+
+### Worker
+
+This will host the supervisor and all of the runs.
+
+- 2+ vCPU
+- 4+ GB RAM
+
+How many workers and resources you need will depend on your workloads and concurrency requirements.
+
+For example:
+
+- 10 concurrency x `small-1x` (0.5 vCPU, 0.5 GB RAM) = 5 vCPU and 5 GB RAM
+- 20 concurrency x `small-1x` (0.5 vCPU, 0.5 GB RAM) = 10 vCPU and 10 GB RAM
+- 100 concurrency x `small-1x` (0.5 vCPU, 0.5 GB RAM) = 50 vCPU and 50 GB RAM
+- 100 concurrency x `small-2x` (1 vCPU, 1 GB RAM) = 100 vCPU and 100 GB RAM
+
+You may need to spin up multiple workers to handle peak concurrency. The good news is you don't have to know the exact numbers upfront. You can start with a single worker and add more as needed.
+
+## Setup
+
+### Webapp
+
+1. Clone the repository
+
+```bash
+git clone https://github.com/triggerdotdev/trigger.dev
+cd trigger.dev/hosting/docker
+```
+
+2. Create a `.env` file
+
+```bash
+cp .env.example .env
+```
+
+3. Start the webapp
-{/* TODO: requirements */}
+```bash
+docker compose -f docker-compose.webapp.yml up -d
+```
+
+4. Optional: Add traefik as a reverse proxy
+
+```bash
+docker compose -f docker-compose.webapp.yml -f docker-compose.traefik.yml up -d
+```
+
+Configure the webapp as needed using the [environment variables](/self-hosting/env/webapp) and apply the changes:
+
+```bash
+docker compose -f docker-compose.webapp.yml up -d
+```
+
+### Worker
+
+1. Clone the repository
+
+```bash
+git clone https://github.com/triggerdotdev/trigger.dev
+cd trigger.dev/hosting/docker
+```
+
+2. Create a `.env` file
+
+```bash
+cp .env.example .env
+```
+
+3. Start the worker
-{/* TODO: setup */}
+```bash
+docker compose -f docker-compose.worker.yml up -d
+```
+
+Configure the supervisor as needed using the [environment variables](/self-hosting/env/supervisor) and apply the changes:
+
+```bash
+docker compose -f docker-compose.worker.yml up -d
+```
-{/* TODO: multiple workers */}
+Repeat as needed for additional workers.
-{/* TODO: Upgrading from v3 */}
+### Combined
+
+If you want to run the webapp and worker on the same machine, just replace the `up` command with the following:
+
+```bash
+docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml up -d
+```
+
+And optionally add traefik as a reverse proxy:
+
+```bash
+docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml up -f docker-compose.traefik.yml up -d
+```
## Version locking
From 4f7d1e881c5975a959c2d3e3a2ebbfaacdb79fb5 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 19:33:20 +0100
Subject: [PATCH 16/21] document machine overrides
---
docs/self-hosting/env/webapp.mdx | 254 +++++++++++++++----------------
docs/self-hosting/overview.mdx | 29 +++-
2 files changed, 153 insertions(+), 130 deletions(-)
diff --git a/docs/self-hosting/env/webapp.mdx b/docs/self-hosting/env/webapp.mdx
index 697cfa1755..d28027c0a6 100644
--- a/docs/self-hosting/env/webapp.mdx
+++ b/docs/self-hosting/env/webapp.mdx
@@ -6,130 +6,130 @@ tag: "v4"
mode: "wide"
---
-| Name | Required | Default | Description |
-| :--------------------------------------------- | :------- | :-------------------- | :----------------------------------------------------------------- |
-| **Secrets** | | | |
-| `SESSION_SECRET` | Yes | — | Session encryption secret. Run: `openssl rand -hex 16` |
-| `MAGIC_LINK_SECRET` | Yes | — | Magic link encryption secret. Run: `openssl rand -hex 16` |
-| `ENCRYPTION_KEY` | Yes | — | Secret store encryption key. Run: `openssl rand -hex 16` |
-| `MANAGED_WORKER_SECRET` | No | managed-secret | Managed worker secret. Should be changed and match supervisor. |
-| **Domains & ports** | | | |
-| `REMIX_APP_PORT` | No | 3030 | Remix app port. |
-| `APP_ORIGIN` | Yes | http://localhost:3030 | App origin URL. |
-| `LOGIN_ORIGIN` | Yes | http://localhost:3030 | Login origin URL. Most likely the same as `APP_ORIGIN`. |
-| `API_ORIGIN` | No | `APP_ORIGIN` | API origin URL. |
-| `STREAM_ORIGIN` | No | `APP_ORIGIN` | Realtime stream origin URL. |
-| `ELECTRIC_ORIGIN` | No | http://localhost:3060 | Electric origin URL. |
-| **Postgres** | | | |
-| `DATABASE_URL` | Yes | — | PostgreSQL connection string. |
-| `DIRECT_URL` | Yes | — | Direct DB connection string used for migrations etc. |
-| `DATABASE_CONNECTION_LIMIT` | No | 10 | Max DB connections. |
-| `DATABASE_POOL_TIMEOUT` | No | 60 | DB pool timeout (s). |
-| `DATABASE_CONNECTION_TIMEOUT` | No | 20 | DB connect timeout (s). |
-| `DATABASE_READ_REPLICA_URL` | No | `DATABASE_URL` | Read-replica DB string. |
-| **Redis** | | | |
-| `REDIS_HOST` | Yes | — | Redis host. |
-| `REDIS_PORT` | Yes | — | Redis port. |
-| `REDIS_READER_HOST` | No | `REDIS_HOST` | Redis reader host. |
-| `REDIS_READER_PORT` | No | `REDIS_PORT` | Redis reader port. |
-| `REDIS_USERNAME` | No | — | Redis username. |
-| `REDIS_PASSWORD` | No | — | Redis password. |
-| `REDIS_TLS_DISABLED` | No | — | Disable Redis TLS. |
-| **Auth** | | | |
-| `WHITELISTED_EMAILS` | No | — | Whitelisted emails regex. |
-| `AUTH_GITHUB_CLIENT_ID` | No | — | GitHub client ID. |
-| `AUTH_GITHUB_CLIENT_SECRET` | No | — | GitHub client secret. |
-| **Email** | | | |
-| `EMAIL_TRANSPORT` | No | — | Email transport type. One of `resend`, `smtp`, `aws-ses`. |
-| `FROM_EMAIL` | No | — | From email address. |
-| `REPLY_TO_EMAIL` | No | — | Reply-to email address. |
-| `RESEND_API_KEY` | No | — | Resend API key. |
-| `SMTP_HOST` | No | — | SMTP host. |
-| `SMTP_PORT` | No | — | SMTP port. |
-| `SMTP_SECURE` | No | — | SMTP secure flag. |
-| `SMTP_USER` | No | — | SMTP user. |
-| `SMTP_PASSWORD` | No | — | SMTP password. |
-| `AWS_REGION` | No | — | AWS region for SES. |
-| `AWS_ACCESS_KEY_ID` | No | — | AWS access key ID for SES. |
-| `AWS_SECRET_ACCESS_KEY` | No | — | AWS secret access key for SES. |
-| **Graphile & Redis worker** | | | |
-| `WORKER_CONCURRENCY` | No | 10 | Redis worker concurrency. |
-| `WORKER_POLL_INTERVAL` | No | 1000 | Redis worker poll interval (ms). |
-| `WORKER_SCHEMA` | No | graphile_worker | Graphile worker schema. |
-| `GRACEFUL_SHUTDOWN_TIMEOUT` | No | 60000 (1m) | Graphile graceful shutdown timeout (ms). Affects shutdown time. |
-| **Concurrency limits** | | | |
-| `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT` | No | 100 | Default env execution concurrency. |
-| `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT` | No | 300 | Default org execution concurrency, needs to be 3x env concurrency. |
-| **Dev** | | | |
-| `DEV_MAX_CONCURRENT_RUNS` | No | 25 | Sets the max concurrency for dev runs via the CLI. |
-| `DEV_OTEL_EXPORTER_OTLP_ENDPOINT` | No | `APP_ORIGIN/otel` | OTel endpoint for dev runs. |
-| **Rate limiting** | | | |
-| `API_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | API rate limit refill interval. |
-| `API_RATE_LIMIT_MAX` | No | 750 | API rate limit max. |
-| `API_RATE_LIMIT_REFILL_RATE` | No | 250 | API rate limit refill rate. |
-| `API_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | API rate limit request logs. |
-| `API_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | API rate limit rejection logs. |
-| `API_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | API rate limit limiter logs. |
-| `API_RATE_LIMIT_JWT_WINDOW` | No | 1m | API rate limit JWT window. |
-| `API_RATE_LIMIT_JWT_TOKENS` | No | 60 | API rate limit JWT tokens. |
-| **Deploy & Registry** | | | |
-| `DEPLOY_REGISTRY_HOST` | Yes | — | Deploy registry host. |
-| `DEPLOY_REGISTRY_USERNAME` | No | — | Deploy registry username. |
-| `DEPLOY_REGISTRY_PASSWORD` | No | — | Deploy registry password. |
-| `DEPLOY_REGISTRY_NAMESPACE` | No | trigger | Deploy registry namespace. |
-| `DEPLOY_IMAGE_PLATFORM` | No | linux/amd64 | Deploy image platform, same values as docker `--platform` flag. |
-| `DEPLOY_TIMEOUT_MS` | No | 480000 (8m) | Deploy timeout (ms). |
-| **Object store (S3)** | | | |
-| `OBJECT_STORE_BASE_URL` | No | — | Object store base URL. |
-| `OBJECT_STORE_ACCESS_KEY_ID` | No | — | Object store access key. |
-| `OBJECT_STORE_SECRET_ACCESS_KEY` | No | — | Object store secret key. |
-| `OBJECT_STORE_REGION` | No | — | Object store region. |
-| `OBJECT_STORE_SERVICE` | No | s3 | Object store service. |
-| **Alerts** | | | |
-| `ORG_SLACK_INTEGRATION_CLIENT_ID` | No | — | Slack client ID. Required for Slack alerts. |
-| `ORG_SLACK_INTEGRATION_CLIENT_SECRET` | No | — | Slack client secret. Required for Slack alerts. |
-| `ALERT_EMAIL_TRANSPORT` | No | — | Alert email transport. |
-| `ALERT_FROM_EMAIL` | No | — | Alert from email. |
-| `ALERT_REPLY_TO_EMAIL` | No | — | Alert reply-to email. |
-| `ALERT_RESEND_API_KEY` | No | — | Alert Resend API key. |
-| `ALERT_SMTP_HOST` | No | — | Alert SMTP host. |
-| `ALERT_SMTP_PORT` | No | — | Alert SMTP port. |
-| `ALERT_SMTP_SECURE` | No | — | Alert SMTP secure. |
-| `ALERT_SMTP_USER` | No | — | Alert SMTP user. |
-| `ALERT_SMTP_PASSWORD` | No | — | Alert SMTP password. |
-| **Limits** | | | |
-| `TASK_PAYLOAD_OFFLOAD_THRESHOLD` | No | 524288 (512KB) | Max task payload size before offloading to S3. |
-| `TASK_PAYLOAD_MAXIMUM_SIZE` | No | 3145728 (3MB) | Max task payload size. |
-| `BATCH_TASK_PAYLOAD_MAXIMUM_SIZE` | No | 1000000 (1MB) | Max batch payload size. |
-| `TASK_RUN_METADATA_MAXIMUM_SIZE` | No | 262144 (256KB) | Max metadata size. |
-| `MAX_BATCH_V2_TRIGGER_ITEMS` | No | 500 | Max batch size. |
-| `MAXIMUM_DEV_QUEUE_SIZE` | No | — | Max dev queue size. |
-| `MAXIMUM_DEPLOYED_QUEUE_SIZE` | No | — | Max deployed queue size. |
-| **Realtime** | | | |
-| `REALTIME_STREAM_MAX_LENGTH` | No | 1000 | Realtime stream max length. |
-| `REALTIME_STREAM_TTL` | No | 86400 (1d) | Realtime stream TTL (s). |
-| **Bootstrap** | | | |
-| `TRIGGER_BOOTSTRAP_ENABLED` | No | 0 | Trigger bootstrap enabled. |
-| `TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME` | No | — | Trigger bootstrap worker group name. |
-| `TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH` | No | — | Trigger bootstrap worker token path. |
-| **Run engine** | | | |
-| `RUN_ENGINE_WORKER_COUNT` | No | 4 | Run engine worker count. |
-| `RUN_ENGINE_TASKS_PER_WORKER` | No | 10 | Run engine tasks per worker. |
-| `RUN_ENGINE_WORKER_CONCURRENCY_LIMIT` | No | 10 | Run engine worker concurrency limit. |
-| `RUN_ENGINE_WORKER_POLL_INTERVAL` | No | 100 | Run engine worker poll interval (ms). |
-| `RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL` | No | 100 | Run engine worker immediate poll interval (ms). |
-| `RUN_ENGINE_WORKER_SHUTDOWN_TIMEOUT_MS` | No | 60000 (1m) | Run engine worker shutdown timeout (ms). |
-| `RUN_ENGINE_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | Run engine rate limit refill interval. |
-| `RUN_ENGINE_RATE_LIMIT_MAX` | No | 1200 | Run engine rate limit max. |
-| `RUN_ENGINE_RATE_LIMIT_REFILL_RATE` | No | 400 | Run engine rate limit refill rate. |
-| `RUN_ENGINE_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | Run engine rate limit request logs. |
-| `RUN_ENGINE_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | Run engine rate limit rejection logs. |
-| `RUN_ENGINE_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | Run engine rate limit limiter logs. |
-| **Misc** | | | |
-| `TRIGGER_TELEMETRY_DISABLED` | No | — | Disable telemetry. |
-| `NODE_MAX_OLD_SPACE_SIZE` | No | — | Maximum memory allocation for Node.js heap (e.g. "4096" for 4GB). |
-| `OPENAI_API_KEY` | No | — | OpenAI API key. |
-| `MACHINE_PRESETS_OVERRIDE_PATH` | No | — | Path to machine presets override file. |
-| `APP_ENV` | No | `NODE_ENV` | App environment. Used for things like the title tag. |
-| `ADMIN_EMAILS` | No | — | Regex of user emails to automatically promote to admin. |
-| `EVENT_LOOP_MONITOR_ENABLED` | No | 1 | Node.js event loop lag monitor. |
\ No newline at end of file
+| Name | Required | Default | Description |
+| :--------------------------------------------- | :------- | :-------------------- | :-------------------------------------------------------------------------------------------------------- |
+| **Secrets** | | | |
+| `SESSION_SECRET` | Yes | — | Session encryption secret. Run: `openssl rand -hex 16` |
+| `MAGIC_LINK_SECRET` | Yes | — | Magic link encryption secret. Run: `openssl rand -hex 16` |
+| `ENCRYPTION_KEY` | Yes | — | Secret store encryption key. Run: `openssl rand -hex 16` |
+| `MANAGED_WORKER_SECRET` | No | managed-secret | Managed worker secret. Should be changed and match supervisor. |
+| **Domains & ports** | | | |
+| `REMIX_APP_PORT` | No | 3030 | Remix app port. |
+| `APP_ORIGIN` | Yes | http://localhost:3030 | App origin URL. |
+| `LOGIN_ORIGIN` | Yes | http://localhost:3030 | Login origin URL. Most likely the same as `APP_ORIGIN`. |
+| `API_ORIGIN` | No | `APP_ORIGIN` | API origin URL. |
+| `STREAM_ORIGIN` | No | `APP_ORIGIN` | Realtime stream origin URL. |
+| `ELECTRIC_ORIGIN` | No | http://localhost:3060 | Electric origin URL. |
+| **Postgres** | | | |
+| `DATABASE_URL` | Yes | — | PostgreSQL connection string. |
+| `DIRECT_URL` | Yes | — | Direct DB connection string used for migrations etc. |
+| `DATABASE_CONNECTION_LIMIT` | No | 10 | Max DB connections. |
+| `DATABASE_POOL_TIMEOUT` | No | 60 | DB pool timeout (s). |
+| `DATABASE_CONNECTION_TIMEOUT` | No | 20 | DB connect timeout (s). |
+| `DATABASE_READ_REPLICA_URL` | No | `DATABASE_URL` | Read-replica DB string. |
+| **Redis** | | | |
+| `REDIS_HOST` | Yes | — | Redis host. |
+| `REDIS_PORT` | Yes | — | Redis port. |
+| `REDIS_READER_HOST` | No | `REDIS_HOST` | Redis reader host. |
+| `REDIS_READER_PORT` | No | `REDIS_PORT` | Redis reader port. |
+| `REDIS_USERNAME` | No | — | Redis username. |
+| `REDIS_PASSWORD` | No | — | Redis password. |
+| `REDIS_TLS_DISABLED` | No | — | Disable Redis TLS. |
+| **Auth** | | | |
+| `WHITELISTED_EMAILS` | No | — | Whitelisted emails regex. |
+| `AUTH_GITHUB_CLIENT_ID` | No | — | GitHub client ID. |
+| `AUTH_GITHUB_CLIENT_SECRET` | No | — | GitHub client secret. |
+| **Email** | | | |
+| `EMAIL_TRANSPORT` | No | — | Email transport type. One of `resend`, `smtp`, `aws-ses`. |
+| `FROM_EMAIL` | No | — | From email address. |
+| `REPLY_TO_EMAIL` | No | — | Reply-to email address. |
+| `RESEND_API_KEY` | No | — | Resend API key. |
+| `SMTP_HOST` | No | — | SMTP host. |
+| `SMTP_PORT` | No | — | SMTP port. |
+| `SMTP_SECURE` | No | — | SMTP secure flag. |
+| `SMTP_USER` | No | — | SMTP user. |
+| `SMTP_PASSWORD` | No | — | SMTP password. |
+| `AWS_REGION` | No | — | AWS region for SES. |
+| `AWS_ACCESS_KEY_ID` | No | — | AWS access key ID for SES. |
+| `AWS_SECRET_ACCESS_KEY` | No | — | AWS secret access key for SES. |
+| **Graphile & Redis worker** | | | |
+| `WORKER_CONCURRENCY` | No | 10 | Redis worker concurrency. |
+| `WORKER_POLL_INTERVAL` | No | 1000 | Redis worker poll interval (ms). |
+| `WORKER_SCHEMA` | No | graphile_worker | Graphile worker schema. |
+| `GRACEFUL_SHUTDOWN_TIMEOUT` | No | 60000 (1m) | Graphile graceful shutdown timeout (ms). Affects shutdown time. |
+| **Concurrency limits** | | | |
+| `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT` | No | 100 | Default env execution concurrency. |
+| `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT` | No | 300 | Default org execution concurrency, needs to be 3x env concurrency. |
+| **Dev** | | | |
+| `DEV_MAX_CONCURRENT_RUNS` | No | 25 | Sets the max concurrency for dev runs via the CLI. |
+| `DEV_OTEL_EXPORTER_OTLP_ENDPOINT` | No | `APP_ORIGIN/otel` | OTel endpoint for dev runs. |
+| **Rate limiting** | | | |
+| `API_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | API rate limit refill interval. |
+| `API_RATE_LIMIT_MAX` | No | 750 | API rate limit max. |
+| `API_RATE_LIMIT_REFILL_RATE` | No | 250 | API rate limit refill rate. |
+| `API_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | API rate limit request logs. |
+| `API_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | API rate limit rejection logs. |
+| `API_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | API rate limit limiter logs. |
+| `API_RATE_LIMIT_JWT_WINDOW` | No | 1m | API rate limit JWT window. |
+| `API_RATE_LIMIT_JWT_TOKENS` | No | 60 | API rate limit JWT tokens. |
+| **Deploy & Registry** | | | |
+| `DEPLOY_REGISTRY_HOST` | Yes | — | Deploy registry host. |
+| `DEPLOY_REGISTRY_USERNAME` | No | — | Deploy registry username. |
+| `DEPLOY_REGISTRY_PASSWORD` | No | — | Deploy registry password. |
+| `DEPLOY_REGISTRY_NAMESPACE` | No | trigger | Deploy registry namespace. |
+| `DEPLOY_IMAGE_PLATFORM` | No | linux/amd64 | Deploy image platform, same values as docker `--platform` flag. |
+| `DEPLOY_TIMEOUT_MS` | No | 480000 (8m) | Deploy timeout (ms). |
+| **Object store (S3)** | | | |
+| `OBJECT_STORE_BASE_URL` | No | — | Object store base URL. |
+| `OBJECT_STORE_ACCESS_KEY_ID` | No | — | Object store access key. |
+| `OBJECT_STORE_SECRET_ACCESS_KEY` | No | — | Object store secret key. |
+| `OBJECT_STORE_REGION` | No | — | Object store region. |
+| `OBJECT_STORE_SERVICE` | No | s3 | Object store service. |
+| **Alerts** | | | |
+| `ORG_SLACK_INTEGRATION_CLIENT_ID` | No | — | Slack client ID. Required for Slack alerts. |
+| `ORG_SLACK_INTEGRATION_CLIENT_SECRET` | No | — | Slack client secret. Required for Slack alerts. |
+| `ALERT_EMAIL_TRANSPORT` | No | — | Alert email transport. |
+| `ALERT_FROM_EMAIL` | No | — | Alert from email. |
+| `ALERT_REPLY_TO_EMAIL` | No | — | Alert reply-to email. |
+| `ALERT_RESEND_API_KEY` | No | — | Alert Resend API key. |
+| `ALERT_SMTP_HOST` | No | — | Alert SMTP host. |
+| `ALERT_SMTP_PORT` | No | — | Alert SMTP port. |
+| `ALERT_SMTP_SECURE` | No | — | Alert SMTP secure. |
+| `ALERT_SMTP_USER` | No | — | Alert SMTP user. |
+| `ALERT_SMTP_PASSWORD` | No | — | Alert SMTP password. |
+| **Limits** | | | |
+| `TASK_PAYLOAD_OFFLOAD_THRESHOLD` | No | 524288 (512KB) | Max task payload size before offloading to S3. |
+| `TASK_PAYLOAD_MAXIMUM_SIZE` | No | 3145728 (3MB) | Max task payload size. |
+| `BATCH_TASK_PAYLOAD_MAXIMUM_SIZE` | No | 1000000 (1MB) | Max batch payload size. |
+| `TASK_RUN_METADATA_MAXIMUM_SIZE` | No | 262144 (256KB) | Max metadata size. |
+| `MAX_BATCH_V2_TRIGGER_ITEMS` | No | 500 | Max batch size. |
+| `MAXIMUM_DEV_QUEUE_SIZE` | No | — | Max dev queue size. |
+| `MAXIMUM_DEPLOYED_QUEUE_SIZE` | No | — | Max deployed queue size. |
+| **Realtime** | | | |
+| `REALTIME_STREAM_MAX_LENGTH` | No | 1000 | Realtime stream max length. |
+| `REALTIME_STREAM_TTL` | No | 86400 (1d) | Realtime stream TTL (s). |
+| **Bootstrap** | | | |
+| `TRIGGER_BOOTSTRAP_ENABLED` | No | 0 | Trigger bootstrap enabled. |
+| `TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME` | No | — | Trigger bootstrap worker group name. |
+| `TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH` | No | — | Trigger bootstrap worker token path. |
+| **Run engine** | | | |
+| `RUN_ENGINE_WORKER_COUNT` | No | 4 | Run engine worker count. |
+| `RUN_ENGINE_TASKS_PER_WORKER` | No | 10 | Run engine tasks per worker. |
+| `RUN_ENGINE_WORKER_CONCURRENCY_LIMIT` | No | 10 | Run engine worker concurrency limit. |
+| `RUN_ENGINE_WORKER_POLL_INTERVAL` | No | 100 | Run engine worker poll interval (ms). |
+| `RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL` | No | 100 | Run engine worker immediate poll interval (ms). |
+| `RUN_ENGINE_WORKER_SHUTDOWN_TIMEOUT_MS` | No | 60000 (1m) | Run engine worker shutdown timeout (ms). |
+| `RUN_ENGINE_RATE_LIMIT_REFILL_INTERVAL` | No | 10s | Run engine rate limit refill interval. |
+| `RUN_ENGINE_RATE_LIMIT_MAX` | No | 1200 | Run engine rate limit max. |
+| `RUN_ENGINE_RATE_LIMIT_REFILL_RATE` | No | 400 | Run engine rate limit refill rate. |
+| `RUN_ENGINE_RATE_LIMIT_REQUEST_LOGS_ENABLED` | No | 0 | Run engine rate limit request logs. |
+| `RUN_ENGINE_RATE_LIMIT_REJECTION_LOGS_ENABLED` | No | 1 | Run engine rate limit rejection logs. |
+| `RUN_ENGINE_RATE_LIMIT_LIMITER_LOGS_ENABLED` | No | 0 | Run engine rate limit limiter logs. |
+| **Misc** | | | |
+| `TRIGGER_TELEMETRY_DISABLED` | No | — | Disable telemetry. |
+| `NODE_MAX_OLD_SPACE_SIZE` | No | — | Maximum memory allocation for Node.js heap (e.g. "4096" for 4GB). |
+| `OPENAI_API_KEY` | No | — | OpenAI API key. |
+| `MACHINE_PRESETS_OVERRIDE_PATH` | No | — | Path to machine presets override file. See [machine overrides](/self-hosting/overview#machine-overrides). |
+| `APP_ENV` | No | `NODE_ENV` | App environment. Used for things like the title tag. |
+| `ADMIN_EMAILS` | No | — | Regex of user emails to automatically promote to admin. |
+| `EVENT_LOOP_MONITOR_ENABLED` | No | 1 | Node.js event loop lag monitor. |
\ No newline at end of file
diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx
index 0e60d8f169..a69e4a6e60 100644
--- a/docs/self-hosting/overview.mdx
+++ b/docs/self-hosting/overview.mdx
@@ -58,9 +58,32 @@ Most of the [limits](/limits) are configurable when self-hosting, with some hard
| Team members | ❌ | 100M |
| Preview branches | ❌ | 100M |
-{/* TODO: Maybe clarify re OFFLOAD_IO_PACKET_LENGTH_LIMIT */}
-
-{/* TODO: add machine overrides section */}
+### Machine overrides
+
+You can override the machine type for a task by setting the `MACHINE_PRESETS_OVERRIDE_PATH` environment variable to a JSON file with the following structure.
+
+```json
+{
+ "defaultMachine": "small-1x",
+ "machines": {
+ "micro": { "cpu": 0.25, "memory": 0.25 },
+ "small-1x": { "cpu": 0.5, "memory": 0.5 },
+ "small-2x": { "cpu": 1, "memory": 1 }
+ // ...etc
+ }
+}
+```
+
+All fields are optional. Partial overrides are supported:
+
+```json
+{
+ "defaultMachine": "small-2x",
+ "machines": {
+ "small-1x": { "memory": 2 }
+ }
+}
+```
## Community support
From debab2aa6019acc348e950788577405c690c6f94 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 19:44:08 +0100
Subject: [PATCH 17/21] split legacy docs into different section
---
docs/docs.json | 11 ++++++-----
docs/self-hosting/docker.mdx | 1 -
docs/self-hosting/env/supervisor.mdx | 1 -
docs/self-hosting/env/webapp.mdx | 1 -
docs/self-hosting/overview.mdx | 1 -
5 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/docs/docs.json b/docs/docs.json
index a7c84d2658..6eab50625a 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -177,15 +177,16 @@
"self-hosting/docker",
{
"group": "Environment variables",
- "pages": ["self-hosting/env/webapp", "self-hosting/env/supervisor"],
- "tags": ["v4"],
- "tag": "v4"
- },
- "open-source-self-hosting"
+ "pages": ["self-hosting/env/webapp", "self-hosting/env/supervisor"]
+ }
],
"tags": ["v4"],
"tag": "v4"
},
+ {
+ "group": "Self-hosting",
+ "pages": ["open-source-self-hosting"]
+ },
{
"group": "Open source",
"pages": ["open-source-contributing", "github-repo", "changelog", "roadmap"]
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index 83ecec2229..d4aa6178f9 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -1,7 +1,6 @@
---
title: "Docker compose"
description: "You can self-host Trigger.dev on your own infrastructure using Docker."
-tag: "v4"
---
The following instructions will use docker compose to spin up a Trigger.dev instance. Make sure to read the self-hosting [overview](/self-hosting/overview) first.
diff --git a/docs/self-hosting/env/supervisor.mdx b/docs/self-hosting/env/supervisor.mdx
index a624469ec6..ffa057747f 100644
--- a/docs/self-hosting/env/supervisor.mdx
+++ b/docs/self-hosting/env/supervisor.mdx
@@ -2,7 +2,6 @@
title: "Supervisor"
description: "Environment variables for the supervisor container."
sidebarTitle: "Supervisor"
-tag: "v4"
mode: "wide"
---
diff --git a/docs/self-hosting/env/webapp.mdx b/docs/self-hosting/env/webapp.mdx
index d28027c0a6..825183100e 100644
--- a/docs/self-hosting/env/webapp.mdx
+++ b/docs/self-hosting/env/webapp.mdx
@@ -2,7 +2,6 @@
title: "Webapp"
description: "Environment variables for the webapp container."
sidebarTitle: "Webapp"
-tag: "v4"
mode: "wide"
---
diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx
index a69e4a6e60..184ff1bcba 100644
--- a/docs/self-hosting/overview.mdx
+++ b/docs/self-hosting/overview.mdx
@@ -1,7 +1,6 @@
---
title: "Overview"
description: "You can self-host Trigger.dev on your own infrastructure."
-tag: "v4"
---
Self-hosting Trigger.dev means you run and manage the platform on your own infrastructure, giving you full control over your environment, deployment process, and the URLs you expose the service on.
From e7ecb03e99cf803d3d4aa4a597cdfa0ea375004a Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Fri, 6 Jun 2025 20:20:21 +0100
Subject: [PATCH 18/21] some fixes
---
docs/self-hosting/docker.mdx | 2 +-
hosting/docker/docker-compose.webapp.yml | 17 +++++++++--------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index d4aa6178f9..f319933a86 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -137,7 +137,7 @@ docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml up -d
And optionally add traefik as a reverse proxy:
```bash
-docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml up -f docker-compose.traefik.yml up -d
+docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml -f docker-compose.traefik.yml up -d
```
## Version locking
diff --git a/hosting/docker/docker-compose.webapp.yml b/hosting/docker/docker-compose.webapp.yml
index af3d7f88ed..697edeb509 100644
--- a/hosting/docker/docker-compose.webapp.yml
+++ b/hosting/docker/docker-compose.webapp.yml
@@ -143,12 +143,13 @@ services:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
- healthcheck:
- test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"]
- interval: 10s
- timeout: 5s
- retries: 5
- start_period: 10s
+ # This won't work with auth enabled
+ # healthcheck:
+ # test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"]
+ # interval: 10s
+ # timeout: 5s
+ # retries: 5
+ # start_period: 10s
minio:
image: minio/minio:${MINIO_IMAGE_TAG:-latest}
@@ -161,8 +162,8 @@ services:
volumes:
- minio:/data
environment:
- MINIO_ROOT_USER: admin
- MINIO_ROOT_PASSWORD: very-safe-password
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER:-admin}
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-very-safe-password}
command: server --console-address ":9001" /data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
From a73208d754ff8b16391d6dd8cbab500c86d8ae00 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Sat, 7 Jun 2025 00:40:03 +0100
Subject: [PATCH 19/21] some tweaks
---
docs/self-hosting/docker.mdx | 30 ++++++--
hosting/docker/.env.example | 71 +++++++++++++++++++
hosting/docker/docker-compose.traefik.yml | 6 +-
hosting/docker/webapp/.env | 1 +
hosting/docker/webapp/.gitignore | 2 +
.../docker-compose.yml} | 40 ++++++-----
hosting/docker/worker/.env | 1 +
hosting/docker/worker/.gitignore | 2 +
.../docker-compose.yml} | 4 +-
9 files changed, 130 insertions(+), 27 deletions(-)
create mode 100644 hosting/docker/.env.example
create mode 120000 hosting/docker/webapp/.env
create mode 100644 hosting/docker/webapp/.gitignore
rename hosting/docker/{docker-compose.webapp.yml => webapp/docker-compose.yml} (84%)
create mode 120000 hosting/docker/worker/.env
create mode 100644 hosting/docker/worker/.gitignore
rename hosting/docker/{docker-compose.worker.yml => worker/docker-compose.yml} (94%)
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index f319933a86..aa3b4d202e 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -82,19 +82,20 @@ cp .env.example .env
3. Start the webapp
```bash
-docker compose -f docker-compose.webapp.yml up -d
+cd webapp
+docker compose up -d
```
4. Optional: Add traefik as a reverse proxy
```bash
-docker compose -f docker-compose.webapp.yml -f docker-compose.traefik.yml up -d
+docker compose -f ../docker-compose.traefik.yml up -d
```
Configure the webapp as needed using the [environment variables](/self-hosting/env/webapp) and apply the changes:
```bash
-docker compose -f docker-compose.webapp.yml up -d
+docker compose up -d
```
### Worker
@@ -115,13 +116,14 @@ cp .env.example .env
3. Start the worker
```bash
-docker compose -f docker-compose.worker.yml up -d
+cd worker
+docker compose up -d
```
Configure the supervisor as needed using the [environment variables](/self-hosting/env/supervisor) and apply the changes:
```bash
-docker compose -f docker-compose.worker.yml up -d
+docker compose up -d
```
Repeat as needed for additional workers.
@@ -131,12 +133,14 @@ Repeat as needed for additional workers.
If you want to run the webapp and worker on the same machine, just replace the `up` command with the following:
```bash
+# Run this from the /hosting/docker directory
docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml up -d
```
And optionally add traefik as a reverse proxy:
```bash
+# Run this from the /hosting/docker directory
docker compose -f docker-compose.webapp.yml -f docker-compose.worker.yml -f docker-compose.traefik.yml up -d
```
@@ -218,6 +222,22 @@ WHITELISTED_EMAILS="authorized@yahoo\.com|authorized@gmail\.com"
This will apply to all auth methods.
+## Troubleshooting
+
+- **Deployment fails at the push step.** The machine running `deploy` needs registry access:
+
+```bash
+docker login -u
+# this should now succeed
+npx trigger.dev@v4-beta deploy
+```
+
+This needs to match the registry credentials. Defaults for the `localhost:5000` registry are `registry-user` and `very-safe-password`. You should change these.
+
+- **Magic links don't arrive.** The webapp container needs to be able to send emails. You probably need to set up an email transport. See the [authentication](#authentication) section for more details.
+
+You should check the logs of the webapp container to see the magic link: `docker logs -f trigger-webapp-1`
+
## CLI usage
This section highlights some of the CLI commands and options that are useful when self-hosting. Please check the [CLI reference](/cli-introduction) for more in-depth documentation.
diff --git a/hosting/docker/.env.example b/hosting/docker/.env.example
new file mode 100644
index 0000000000..cc35b9e6e1
--- /dev/null
+++ b/hosting/docker/.env.example
@@ -0,0 +1,71 @@
+# Secrets
+# - Do NOT use these defaults in production
+# - Generate your own by running `openssl rand -hex 16` for each secret
+SESSION_SECRET=2818143646516f6fffd707b36f334bbb
+MAGIC_LINK_SECRET=44da78b7bbb0dfe709cf38931d25dcdd
+ENCRYPTION_KEY=f686147ab967943ebbe9ed3b496e465a
+MANAGED_WORKER_SECRET=447c29678f9eaf289e9c4b70d3dd8a7f
+
+# Trigger image tags
+# - You should lock these to a specific version in production
+# - For example: SUPERVISOR_IMAGE_TAG=v4-beta.21
+SUPERVISOR_IMAGE_TAG=v4-beta
+WEBAPP_IMAGE_TAG=v4-beta
+
+# Webapp
+APP_ORIGIN=http://localhost:8030
+LOGIN_ORIGIN=http://localhost:8030
+# WEBAPP_PUBLISH_IP=0.0.0.0
+
+# Object store
+# - You need to set these up via the Minio dashboard first: http://localhost:9001
+# - See Minio section for login details
+# OBJECT_STORE_ACCESS_KEY_ID=
+# OBJECT_STORE_SECRET_ACCESS_KEY=
+
+# Postgres
+# - Do NOT use these defaults in production
+# - Especially if you decide to expose the database to the internet
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=postgres
+POSTGRES_DB=postgres
+# POSTGRES_PUBLISH_IP=127.0.0.1
+# POSTGRES_IMAGE_TAG=14
+
+# Redis
+# REDIS_PUBLISH_IP=127.0.0.1
+# REDIS_IMAGE_TAG=7
+
+# ElectricSQL
+# ELECTRIC_IMAGE_TAG=
+
+# Clickhouse
+# CLICKHOUSE_IMAGE_TAG=latest
+# CLICKHOUSE_PUBLISH_IP=127.0.0.1
+
+# Registry
+# REGISTRY_IMAGE_TAG=2
+# REGISTRY_PUBLISH_IP=127.0.0.1
+
+# Minio
+# MINIO_IMAGE_TAG=latest
+# MINIO_PUBLISH_IP=127.0.0.1
+# MINIO_ROOT_USER=admin
+# MINIO_ROOT_PASSWORD=very-safe-password
+
+# Docker Registry
+# DEPLOY_REGISTRY_HOST=localhost:5000
+# DOCKER_REGISTRY_URL=
+# DOCKER_REGISTRY_USERNAME=
+# DOCKER_REGISTRY_PASSWORD=
+
+# Docker Socket Proxy
+# DOCKER_PROXY_IMAGE_TAG=latest
+
+# Traefik
+# TRAEFIK_ENTRYPOINT=websecure
+# TRAEFIK_IMAGE_TAG=latest
+# RESTART_POLICY=
+# TRAEFIK_HTTP_PUBLISH_IP=0.0.0.0
+# TRAEFIK_HTTPS_PUBLISH_IP=0.0.0.0
+# TRAEFIK_DASHBOARD_PUBLISH_IP=127.0.0.1
\ No newline at end of file
diff --git a/hosting/docker/docker-compose.traefik.yml b/hosting/docker/docker-compose.traefik.yml
index 2bd8d602f0..297c55d74c 100644
--- a/hosting/docker/docker-compose.traefik.yml
+++ b/hosting/docker/docker-compose.traefik.yml
@@ -35,9 +35,9 @@ services:
image: traefik:${TRAEFIK_IMAGE_TAG:-v3.4}
restart: ${RESTART_POLICY:-unless-stopped}
ports:
- - "80:80"
- - "443:443"
- - "8080:8080" # Traefik dashboard
+ - "${TRAEFIK_HTTP_PUBLISH_IP:-0.0.0.0}:80:80"
+ - "${TRAEFIK_HTTPS_PUBLISH_IP:-0.0.0.0}:443:443"
+ - "${TRAEFIK_DASHBOARD_PUBLISH_IP:-127.0.0.1}:8080:8080" # dashboard
networks:
- traefik
command:
diff --git a/hosting/docker/webapp/.env b/hosting/docker/webapp/.env
new file mode 120000
index 0000000000..4a82335f5e
--- /dev/null
+++ b/hosting/docker/webapp/.env
@@ -0,0 +1 @@
+../.env
\ No newline at end of file
diff --git a/hosting/docker/webapp/.gitignore b/hosting/docker/webapp/.gitignore
new file mode 100644
index 0000000000..b0b1a8e81a
--- /dev/null
+++ b/hosting/docker/webapp/.gitignore
@@ -0,0 +1,2 @@
+# preserve the .env symlink
+!.env
diff --git a/hosting/docker/docker-compose.webapp.yml b/hosting/docker/webapp/docker-compose.yml
similarity index 84%
rename from hosting/docker/docker-compose.webapp.yml
rename to hosting/docker/webapp/docker-compose.yml
index 697edeb509..4a2e2ffdce 100644
--- a/hosting/docker/docker-compose.webapp.yml
+++ b/hosting/docker/webapp/docker-compose.yml
@@ -2,7 +2,7 @@ name: trigger
services:
webapp:
- image: ghcr.io/triggerdotdev/trigger.dev:${TRIGGER_IMAGE_TAG:-main}
+ image: ghcr.io/triggerdotdev/trigger.dev:${WEBAPP_IMAGE_TAG:-v4-beta}
restart: ${RESTART_POLICY:-unless-stopped}
ports:
- ${WEBAPP_PUBLISH_IP:-0.0.0.0}:8030:3000
@@ -14,7 +14,9 @@ services:
- supervisor
volumes:
- shared:/home/node/shared
+ # Only needed for bootstrap
user: root
+ # Only needed for bootstrap
command: sh -c "chown -R node:node /home/node/shared && exec ./scripts/entrypoint.sh"
healthcheck:
test: ["CMD", "node", "-e", "http.get('http://localhost:3000/healthcheck', res => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
@@ -23,7 +25,8 @@ services:
retries: 5
start_period: 10s
environment:
- APP_ORIGIN: http://localhost:8030
+ APP_ORIGIN: ${APP_ORIGIN:-http://localhost:8030}
+ LOGIN_ORIGIN: ${LOGIN_ORIGIN:-http://localhost:8030}
ELECTRIC_ORIGIN: http://electric:3000
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/main?schema=public&sslmode=disable
DIRECT_URL: postgresql://postgres:postgres@postgres:5432/main?schema=public&sslmode=disable
@@ -36,14 +39,16 @@ services:
REDIS_TLS_DISABLED: true
APP_LOG_LEVEL: info
DEV_OTEL_EXPORTER_OTLP_ENDPOINT: http://localhost:8030/otel
- TRIGGER_BOOTSTRAP_ENABLED: 1
- TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME: bootstrap-3
- TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH: /home/node/shared/worker_token
- DEPLOY_REGISTRY_HOST: localhost:5000
+ DEPLOY_REGISTRY_HOST: ${DEPLOY_REGISTRY_HOST:-localhost:5000}
OBJECT_STORE_BASE_URL: http://minio:9000
OBJECT_STORE_ACCESS_KEY_ID: ${OBJECT_STORE_ACCESS_KEY_ID}
OBJECT_STORE_SECRET_ACCESS_KEY: ${OBJECT_STORE_SECRET_ACCESS_KEY}
GRACEFUL_SHUTDOWN_TIMEOUT: 1000
+ # Bootstrap - this will automatically set up a worker group for you
+ # This will NOT work for split deployments
+ TRIGGER_BOOTSTRAP_ENABLED: 1
+ TRIGGER_BOOTSTRAP_WORKER_GROUP_NAME: bootstrap
+ TRIGGER_BOOTSTRAP_WORKER_TOKEN_PATH: /home/node/shared/worker_token
# Limits
# TASK_PAYLOAD_OFFLOAD_THRESHOLD: 524288 # 512KB
# TASK_PAYLOAD_MAXIMUM_SIZE: 3145728 # 3MB
@@ -65,9 +70,9 @@ services:
- -c
- wal_level=logical
environment:
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
- POSTGRES_DB: postgres
+ POSTGRES_USER: ${POSTGRES_USER:-postgres}
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
+ POSTGRES_DB: ${POSTGRES_DB:-postgres}
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
@@ -119,7 +124,7 @@ services:
CLICKHOUSE_ADMIN_PASSWORD: password
volumes:
- clickhouse:/bitnami/clickhouse
- - ./clickhouse/override.xml:/bitnami/clickhouse/etc/config.d/override.xml:ro
+ - ../clickhouse/override.xml:/bitnami/clickhouse/etc/config.d/override.xml:ro
networks:
- webapp
healthcheck:
@@ -138,18 +143,17 @@ services:
- webapp
volumes:
# registry-user:very-secure-indeed
- - ./registry/auth.htpasswd:/auth/htpasswd:ro
+ - ../registry/auth.htpasswd:/auth/htpasswd:ro
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
- # This won't work with auth enabled
- # healthcheck:
- # test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/v2/"]
- # interval: 10s
- # timeout: 5s
- # retries: 5
- # start_period: 10s
+ healthcheck:
+ test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 10s
minio:
image: minio/minio:${MINIO_IMAGE_TAG:-latest}
diff --git a/hosting/docker/worker/.env b/hosting/docker/worker/.env
new file mode 120000
index 0000000000..4a82335f5e
--- /dev/null
+++ b/hosting/docker/worker/.env
@@ -0,0 +1 @@
+../.env
\ No newline at end of file
diff --git a/hosting/docker/worker/.gitignore b/hosting/docker/worker/.gitignore
new file mode 100644
index 0000000000..b0b1a8e81a
--- /dev/null
+++ b/hosting/docker/worker/.gitignore
@@ -0,0 +1,2 @@
+# preserve the .env symlink
+!.env
diff --git a/hosting/docker/docker-compose.worker.yml b/hosting/docker/worker/docker-compose.yml
similarity index 94%
rename from hosting/docker/docker-compose.worker.yml
rename to hosting/docker/worker/docker-compose.yml
index e48ed84ab1..7e0cc9f91a 100644
--- a/hosting/docker/docker-compose.worker.yml
+++ b/hosting/docker/worker/docker-compose.yml
@@ -2,7 +2,7 @@ name: trigger
services:
supervisor:
- image: ghcr.io/triggerdotdev/supervisor:${TRIGGER_IMAGE_TAG:-main}
+ image: ghcr.io/triggerdotdev/supervisor:${SUPERVISOR_IMAGE_TAG:-v4-beta}
restart: ${RESTART_POLICY:-unless-stopped}
depends_on:
- docker-proxy
@@ -11,7 +11,9 @@ services:
- docker-proxy
volumes:
- shared:/home/node/shared
+ # Only needed for bootstrap
user: root
+ # Only needed for bootstrap
command: sh -c "chown -R node:node /home/node/shared && exec /usr/bin/dumb-init -- pnpm run --filter supervisor start"
environment:
# This needs to match the token of the worker group you want to connect to
From daf97fc9ae6c04990c03076baa72d2ef0191853b Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Sat, 7 Jun 2025 00:52:39 +0100
Subject: [PATCH 20/21] add login and init instructions
---
docs/self-hosting/docker.mdx | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx
index aa3b4d202e..8261653675 100644
--- a/docs/self-hosting/docker.mdx
+++ b/docs/self-hosting/docker.mdx
@@ -92,12 +92,20 @@ docker compose up -d
docker compose -f ../docker-compose.traefik.yml up -d
```
-Configure the webapp as needed using the [environment variables](/self-hosting/env/webapp) and apply the changes:
+5. Configure the webapp as needed using the [environment variables](/self-hosting/env/webapp) and apply the changes:
```bash
docker compose up -d
```
+6. You should now be able to access the webapp at `http://localhost:8030`. When logging in, check the container logs for the magic link: `docker compose logs -f webapp`
+
+7. Optional: To initialize a new project, run the following command:
+
+```bash
+npx trigger.dev@v4-beta init -p -a http://localhost:8030
+```
+
### Worker
1. Clone the repository
From 64de0043f895154a914acc3d085c2c15ed657657 Mon Sep 17 00:00:00 2001
From: nicktrn <55853254+nicktrn@users.noreply.github.com>
Date: Sat, 7 Jun 2025 00:53:00 +0100
Subject: [PATCH 21/21] don't cursorignore .env.example
---
.cursorignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.cursorignore b/.cursorignore
index 4b81818369..8430ce365f 100644
--- a/.cursorignore
+++ b/.cursorignore
@@ -5,4 +5,5 @@ apps/coordinator/
packages/rsc/
.changeset
.zed
-.env
\ No newline at end of file
+.env
+!.env.example
\ No newline at end of file