Configuration
Runner is configured through environment variables. The official compose file has working defaults baked in, so it runs out of the box with no changes:
docker compose up -dTo change a value — a password, the public URL — place a .env file next to docker-compose.yml. It’s read automatically, and anything you set there overrides the built-in default, so a value only needs to change in one place. Copy .env.example to .env to get started.
Core settings
The default compose file ships with working defaults for these three settings, mirrored by .env.example, so it runs out of the box for a local evaluation. Change them before exposing Runner to a network.
MongoDB password
Set MONGO_ROOT_USERNAME and MONGO_ROOT_PASSWORD in .env. Both the api and mongodb services read from the same two variables, so the connection string stays in sync automatically — there’s nothing else to edit.
# .env
MONGO_ROOT_USERNAME=root
MONGO_ROOT_PASSWORD=your-db-passwordWARNING
MONGO_ROOT_PASSWORD only sets the password when MongoDB initialises a new database. Changing it in .env after first run has no effect on an existing database. To rotate the password on a running installation, follow these steps:
Stop the API container so no connections are open:
shdocker compose stop apiOpen a MongoDB shell and change the password:
shdocker compose exec mongodb mongosh -u root -p # enter current password when prompted use admin db.changeUserPassword('root', passwordPrompt()) # enter new password when prompted exitUpdate
MONGO_ROOT_PASSWORDin.envto the new password.Restart the stack:
shdocker compose up -d
API key
A shared secret between the API and Executor services. Set it once in .env — both services read the same variable:
# .env
API_KEY=your-api-keyPublic URL
Set WEB_BASE_URL in .env to the hostname or IP address your users will access Runner at. The API uses this to generate links in notification emails and other outbound content — if it’s wrong, those links will be broken.
# .env
WEB_BASE_URL=https://runner.example.comExecutor tuning
The following settings aren’t wired to .env — add them directly to the executor service’s environment: block in docker-compose.yml.
Worker resources
Each executor spawns a Docker container per workflow run. Without limits, a runaway workflow can starve the host. Set CPU and memory limits to contain the blast radius:
# Executor service
DOCKER_CPU_PERIOD: 100000
DOCKER_CPU_QUOTA: 85000 # 85% of one CPU
DOCKER_MEMORY: 1500000000 # 1.5 GBSee the Docker documentation for details on these values.
To mount additional host paths into worker containers, use DOCKER_BINDS:
DOCKER_BINDS: /data/shared:/data/shared,/certs:/certs:roPrivate registry
Each executor builds and caches Docker images locally. When running multiple executors, each one rebuilds its own cache independently — wasting time and disk space.
A shared private registry solves this: images are pushed after the first build and pulled by all other executors instead of being rebuilt.
# Executor service
DOCKER_REGISTRY_URL: registry.example.com
DOCKER_REGISTRY_USER: your-registry-user
DOCKER_REGISTRY_PASSWORD: your-registry-passwordWhen a registry is configured, built images are automatically removed from the executor after being pushed. Set DOCKER_REMOVE_IMAGES: false to keep a local copy as well.
To try this locally without standing up your own registry, see the registry extension below.
Executor identity
Each executor registers itself with the API on startup. You can give it a display name and tags that appear in the Executors list and can be used to route specific projects to specific executors:
# Executor service
INITIAL_DESCRIPTION: Production executor
INITIAL_TAGS: prod,eu-westSee Executors for how to use tags for routing.
Extensions
The extensions/ directory has optional add-ons, each a self-contained compose file you layer on top of docker-compose.yml with -f. These wire in third-party open-source tools rather than software built or maintained by NodePit — see each one’s own project for support and documentation beyond what’s covered here. To avoid repeating -f flags on every command, set COMPOSE_FILE in .env instead:
COMPOSE_FILE=docker-compose.yml:extensions/nginx/docker-compose.yml:extensions/watchtower/docker-compose.ymlReverse proxy and HTTPS
For a publicly reachable installation, put Runner behind the nginx extension: an HTTPS reverse proxy with automatic Let’s Encrypt certificates. Set DOMAIN and LETSENCRYPT_EMAIL in .env — Runner refuses to start without them once the extension is layered on — and point a DNS record at the server before starting it, since certificate issuance needs the domain to resolve publicly. Set WEB_BASE_URL to https://<DOMAIN> too, so links generated by Runner use the public HTTPS URL.
docker compose \
-f docker-compose.yml \
-f extensions/nginx/docker-compose.yml \
up -dAutomatic updates
The watchtower extension pulls and restarts containers whenever new images are released.
docker compose \
-f docker-compose.yml \
-f extensions/watchtower/docker-compose.yml \
up -dBackups
The backup extension creates daily database and workflow-data backups, stored locally and optionally shipped to an S3-compatible bucket. Set the BACKUP_S3_* variables in .env to also ship backups off-site — see .env.example.
docker compose \
-f docker-compose.yml \
-f extensions/backup/docker-compose.yml \
up -dDocker management UI
The portainer extension adds a web UI for managing the Docker stack — starting and stopping containers, viewing logs, inspecting resource usage — without the CLI.
docker compose \
-f docker-compose.yml \
-f extensions/portainer/docker-compose.yml \
up -dPrivate image registry
The registry extension adds a private Docker registry already wired to the Executor via DOCKER_REGISTRY_URL, for trying that feature out without setting up your own registry first. It's unauthenticated and bound to 127.0.0.1 only, so it's meant for a single-host setup — sharing it across multiple executor hosts needs your own authenticated registry instead.
docker compose \
-f docker-compose.yml \
-f extensions/registry/docker-compose.yml \
up -dEnvironment variables
The tables below document the container environment variables Runner reads directly. The official compose file already wires API_KEY, WEB_BASE_URL, DB_URL, and the MongoDB credentials to the .env variables described above — you only need to set these yourself if you’re running a custom compose setup.
API
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY | yes | — | Secret token that authorises Executor connections to the API. Must match the API_KEY set on each Executor. |
DB_URL | yes | — | MongoDB connection string in the format mongodb://user:password@host/database?authSource=admin. |
WEB_BASE_URL | yes | — | Publicly reachable URL of the web UI. Used to build absolute links in notification emails and other outbound content. |
Executor
| Variable | Required | Default | Description |
|---|---|---|---|
API_BASE_URL | yes | — | Base URL of the API service as reachable from the Executor container, e.g. http://api:3000. |
API_KEY | yes | — | Secret token used to authenticate with the API. Must match the API_KEY set on the API. |
DOCKER_BINDS | no | — | Comma-separated volume mounts added to every worker container, in host:container[:options] format. Useful for sharing certificates, SSH keys, or data directories. |
DOCKER_CPU_PERIOD | no | 100000 | CPU scheduling period in microseconds. Used together with DOCKER_CPU_QUOTA to set a CPU limit: quota ÷ period = fraction of a CPU allocated per worker container. |
DOCKER_CPU_QUOTA | no | — | Microseconds of CPU time allocated per period. Set to 85000 with the default period to allow up to 85% of one CPU per worker container. |
DOCKER_MEMORY | no | — | Hard memory limit for worker containers in bytes. Containers that exceed this limit are killed. |
DOCKER_NETWORK_MODE | no | bridge | Docker network mode for worker containers. Accepts any value supported by Docker: bridge, host, none, or a named network. |
DOCKER_REGISTRY_PASSWORD | no | — | Password for authenticating with the registry configured in DOCKER_REGISTRY_URL. |
DOCKER_REGISTRY_URL | no | — | URL of a private Docker registry. When set, built images are pushed here so other Executor instances can pull them instead of rebuilding. |
DOCKER_REGISTRY_USER | no | — | Username for authenticating with the registry configured in DOCKER_REGISTRY_URL. |
DOCKER_REMOVE_IMAGES | no | true when registry is set | When true, images are deleted from the local Executor after being pushed to the registry, freeing disk space. Has no effect when no registry is configured. |
INITIAL_DESCRIPTION | no | — | Human-readable name for this Executor instance, shown in the Executors list in the UI. Useful when running multiple Executors. |
INITIAL_TAGS | no | — | Comma-separated tags assigned to this Executor on startup. Tags are used to route projects to specific Executors. |
MongoDB
| Variable | Required | Default | Description |
|---|---|---|---|
MONGO_INITDB_ROOT_PASSWORD | yes | — | Password for the MongoDB root user. Only applied when the database is initialised for the first time — changing it later has no effect on an existing database. |
MONGO_INITDB_ROOT_USERNAME | yes | — | Username for the MongoDB root user. Only applied on first initialisation. |
Troubleshooting
Runs fail on ARM Mac with "Errors were encountered while processing: ca-certificates"
This is a known issue with Rosetta on M2 and M3 processors. In Docker Desktop, go to Settings → General and disable Use Rosetta for x86/amd64 emulation on Apple Silicon. See the upstream issue for details.
Runs fail on ARM Mac with "Failed to restore cached image … not found"
This is a known issue with some versions of Docker Desktop. In Docker Desktop, go to Settings → General and disable Use containerd for pulling and storing images. See the upstream issue for details.