Running with Docker
Squadron publishes Docker images to GitHub Container Registry on every release. Two variants are available:
| Image | Tag | Use case |
|---|---|---|
| Alpine | ghcr.io/mlund01/squadron:latest | Default — small, static binary |
| Debian | ghcr.io/mlund01/squadron:latest-debian | Plugins that need glibc/CGO |
Both images support linux/amd64 and linux/arm64.
Quick Start
docker run --rm ghcr.io/mlund01/squadron versionMounts
Squadron in Docker uses a single mount point:
| Mount | Purpose |
|---|---|
/config | Your HCL configuration files and .squadron/ state directory |
All runtime state (vault, plugins, database) lives in /config/.squadron/, right alongside your HCL config files. The container’s working directory is /config, so the -c flag is not needed.
Run with the Command Center UI
docker run -it \
-v ./my-project:/config \
-p 8080:8080 \
ghcr.io/mlund01/squadron engage-itis optional — with it, the quickstart wizard runs on an empty project. Without it, Squadron auto-initializes and you set everything up from the UI.-p 8080:8080publishes the command center to the host. Openhttp://localhost:8080.
The official image sets SQUADRON_CONTAINER=1, which makes Squadron run in the foreground and skip browser auto-open. You never need to pass --foreground here. Pass --headless if you want Squadron to run without the UI (for example, when missions run purely via schedules or a remote command center).
If some variables are missing, Squadron still starts — the UI lets you set them after the container is up.
Connect to a Remote Command Center
If your HCL config declares a command_center block, Squadron connects outbound — no port mapping needed:
docker run \
-v ./my-project:/config \
ghcr.io/mlund01/squadron engageThe local UI is skipped automatically when a command_center block is present.
Run a Single Mission
docker run \
-v ./my-project:/config \
ghcr.io/mlund01/squadron mission --init my_missionDocker Compose
services:
squadron:
image: ghcr.io/mlund01/squadron:latest
volumes:
- ./my-project:/config
command: ["engage"]
ports:
- "8080:8080"docker compose upSetting Variables
Variables are stored in an encrypted vault at /config/.squadron/vars.vault. There are two ways to set them:
Via the command center UI (easiest): Open the UI at http://localhost:8080, navigate to Variables, and add them there.
Via vars set in a running container:
docker exec <container> squadron vars set anthropic_api_key sk-ant-...Securing the Vault Passphrase
By default, when Squadron auto-initializes the vault, it writes the passphrase to /config/.squadron/vault.key with 0600 permissions. This works fine when the volume is local and trusted.
For stronger production setups, mount a passphrase file as a Docker secret:
services:
squadron:
image: ghcr.io/mlund01/squadron:latest
volumes:
- ./my-project:/config
command: ["engage"]
secrets:
- vault_passphrase
ports:
- "8080:8080"
secrets:
vault_passphrase:
file: ./vault_passphrase.txtSquadron checks /run/secrets/vault_passphrase automatically. If the file exists, it uses that passphrase instead of the on-disk key. Docker mounts secrets as tmpfs, so the passphrase never touches the container’s disk.
Pinning a Version
Use a version tag instead of latest:
docker pull ghcr.io/mlund01/squadron:v0.0.45
docker pull ghcr.io/mlund01/squadron:v0.0.45-debianAlpine vs Debian
- Alpine (
latest) — smaller image, static binary withCGO_ENABLED=0. Use this unless you have a reason not to. - Debian (
latest-debian) — larger image, built withCGO_ENABLED=1. Use this if you have plugins that depend on C libraries or glibc.
Both images ship with the toolchains needed to build local plugins at config-load time:
- Python plugins (
pyproject.tomlsource) —python3+pipare pre-installed; Squadron creates an isolated venv per plugin under/config/.squadron/plugins/. - Go plugins (
go.modsource) —go+gitare pre-installed; Squadron runsgo buildper plugin. The Debian image copies Go 1.25 from the builder stage (bookworm’s distro Go is too old); the Alpine image uses the distro Go.
Pre-built / released plugins (source = "github.com/..." or source = "npm:...") work without these toolchains, so if you only use released plugins you can build your own slim image by removing the relevant packages from the Dockerfile.