The default Compose stack runs one Foxhole service from ghcr.io/jacobthree/foxhole. The service serves the static dashboard, API, in-process scheduler, in-memory live events, and SQLite-backed history on 127.0.0.1:8000.
The Docker socket proxy is optional and available through the docker profile with read-only defaults. Redis, Celery worker, Celery beat, and Flower are not part of the default runtime; they are available only through iac/compose/docker-compose.distributed.yml for advanced installs.
Durable SQLite state is stored in iac/compose/data/foxhole.db on the host and mounted into the runtime as /app/data/foxhole.db. Settings changed through the API or dashboard are written to iac/compose/config/foxhole.env and mounted as /config/foxhole.env. Back up both files before recreating or moving the stack.
The example config sets FOXHOLE_SESSION_COOKIE_SECURE=false so browser login works on the default local HTTP URL. Set it to true when serving Foxhole behind HTTPS.
mkdir -p iac/compose/data iac/compose/config
cp iac/compose/.env.example iac/compose/config/foxhole.env
docker compose -f iac/compose/docker-compose.yml config
docker compose -f iac/compose/docker-compose.yml up -dEdit iac/compose/config/foxhole.env before the first start. FOXHOLE_API_BEARER_TOKEN is the only required first-run value.
Open the dashboard:
http://127.0.0.1:8000
Check process health:
curl http://127.0.0.1:8000/healthzReadiness is authenticated. In the default single runtime it does not require Redis:
curl -H "Authorization: Bearer $FOXHOLE_API_BEARER_TOKEN" http://127.0.0.1:8000/readyzThe backend image includes the Next.js static export and serves it from the FastAPI process. A separate Node.js process is only needed for frontend development.
Default Compose creates a single user-facing container:
foxhole
|-- FastAPI API
|-- static dashboard from /app/ui/out
|-- in-process scheduler
|-- in-memory event bus
|-- SQLite at /app/data/foxhole.db
When --profile docker is used, Compose adds docker-socket-proxy on an internal network. The app still remains the only browser-facing service.
By default, Compose pulls:
ghcr.io/jacobthree/foxhole:latest
Pin a tagged release by setting FOXHOLE_IMAGE_TAG:
FOXHOLE_IMAGE_TAG=v0.1.0 docker compose -f iac/compose/docker-compose.yml pull
FOXHOLE_IMAGE_TAG=v0.1.0 docker compose -f iac/compose/docker-compose.yml up -dContributors can build locally and run the same Compose file against that local image:
docker build -t foxhole:local .
FOXHOLE_IMAGE=foxhole FOXHOLE_IMAGE_TAG=local docker compose -f iac/compose/docker-compose.yml up -dBack up these host paths:
iac/compose/data/
iac/compose/config/
data/ contains the SQLite database and possible SQLite sidecar files. config/foxhole.env contains the bearer token, integration secrets, cookie settings, and any settings changed through the dashboard or API.
Create a backup from the repository root:
docker compose -f iac/compose/docker-compose.yml stop
mkdir -p backups
tar -C iac/compose -czf backups/foxhole-compose-$(date +%Y%m%d-%H%M%S).tgz data config
docker compose -f iac/compose/docker-compose.yml up -dRestore onto a fresh or stopped Compose deployment:
docker compose -f iac/compose/docker-compose.yml down
mkdir -p iac/compose
tar -C iac/compose -xzf backups/foxhole-compose-YYYYMMDD-HHMMSS.tgz
docker compose -f iac/compose/docker-compose.yml up -dIf FOXHOLE_DATABASE_PATH is changed from /app/data/foxhole.db, back up the host mount that contains the configured path instead of only iac/compose/data/.
Keep the Compose port bound to localhost and proxy only the unified Foxhole app:
127.0.0.1:8000 -> Foxhole dashboard/API
Caddy example:
foxhole.example.com {
reverse_proxy 127.0.0.1:8000
}Use these settings when the public URL is HTTPS:
FOXHOLE_SESSION_COOKIE_SECURE=true
FOXHOLE_SESSION_COOKIE_SAMESITE=laxDo not proxy or publish docker-socket-proxy, Redis, Celery worker, Celery beat, or Flower. They are internal runtime/debug services, not browser endpoints.
If you intentionally run a separate UI origin instead of the same-origin dashboard served by Foxhole, allow that UI origin explicitly and use secure cross-site cookies:
FOXHOLE_UI_ALLOWED_ORIGINS=["https://dashboard.example.com"]
FOXHOLE_SESSION_COOKIE_SECURE=true
FOXHOLE_SESSION_COOKIE_SAMESITE=noneThe socket proxy is disabled by default. Enable it only when Docker diagnostics are configured:
docker compose -f iac/compose/docker-compose.yml --profile docker up -dWhen enabling Docker diagnostics in config/foxhole.env, keep the proxy URL pointed at the internal service:
FOXHOLE_DOCKER_ENABLED=true
FOXHOLE_DOCKER_SOCKET_PROXY_URL=tcp://docker-socket-proxy:2375Do not set FOXHOLE_DOCKER_ENABLED=true without also starting the docker profile. The socket proxy URL is intentionally not part of the default one-container environment, so Docker remains incomplete until the proxy is explicitly enabled.
Stage 1 exposes read-only Docker API groups used by diagnostics: containers, events, images, info, networks, and version. It sets POST=0 and keeps mutation groups such as build, exec, secrets, services, swarm, tasks, and volumes disabled. The proxy is reachable only by services on the internal socket-proxy network.
The distributed Compose file keeps Redis/Celery mode available for advanced installs that want separate API, worker, and beat processes:
docker compose -f iac/compose/docker-compose.distributed.yml up -dThe file starts Redis, worker, and beat, and sets FOXHOLE_RUNTIME_MODE=distributed for the Foxhole service so scheduled checks run through Celery beat instead of the in-process scheduler.
Flower is not part of the default runtime. Start it only when debugging Celery:
docker compose -f iac/compose/docker-compose.distributed.yml --profile debug up flowerUse iac/compose/socket-proxy.stage2.yml only after write tools are protected by Foxhole confirmation tokens:
docker compose \
-f iac/compose/docker-compose.yml \
-f iac/compose/socket-proxy.stage2.yml \
--profile docker \
up -dThe override enables POST for narrowly scoped container remediations. It still leaves exec, image build, and volume mutation disabled.