Run FoxxyCode as foxxycode http inside a minimal scratch image. The default image ships the embedded web UI (ui build tag), OpenAI-compatible REST (http), scheduler (scheduler), and long-term memory (memory).
Related files:
Dockerfile- multi-stage build (Node UI bundle, Go binary,scratchruntime)docker-compose.yml- runghcr.io/hijera/foxxycode-agent(defaultdocker compose)docker-compose.dev.yml- build from source, publish port 12345, volumes.dockerignore- keeps context small; never commitconfig.yamlwith secretsexamples/httpserver/docker.sh- automated smoke test
Published images: foxxycode-agent on GHCR (ghcr.io/hijera/foxxycode-agent). CI builds multi-arch manifests (linux/amd64, linux/arm64) on SemVer tags and pushes floating aliases (latest, MAJOR.MINOR, MAJOR) when appropriate - see .github/workflows/docker-build-push.yaml.
On Apple Silicon or arm64 Linux hosts, pull the image as usual; Docker selects arm64 automatically. To pin a platform explicitly:
docker pull --platform linux/arm64 ghcr.io/hijera/foxxycode-agent:latest
docker pull --platform linux/amd64 ghcr.io/hijera/foxxycode-agent:latestGeneral build instructions without Docker - docs/build.md.
- Docker with Compose V2 (
docker compose, not only legacydocker-compose) - A
config.yamlyou mount read-only into the container (start fromconfig.example.yaml). Do not commit secrets. - For the web UI, a browser on the machine that can reach the published host port (default 12345)
Compose is the recommended way to run the published GHCR image or a locally built one. Both files define a single service named foxxycode - no database or Redis sidecar. The process is foxxycode http bound to 0.0.0.0:12345 inside the container; Compose maps that port to the host.
| File | When to use |
|---|---|
docker-compose.yml |
Pull a release from GHCR (docker compose pull) - day-to-day and production-like runs without building on the host |
docker-compose.dev.yml |
build: from the repo Dockerfile - hacking on FoxxyCode, reproducing CI, or running examples/httpserver/docker.sh |
Compose V2 merges an optional docker-compose.override.yml in the same directory (git-ignored by convention) so you can pin an image tag, change ports, or add environment without editing the tracked file.
| Setting | Default compose | Dev compose |
|---|---|---|
| Image | ${FOXXYCODE_IMAGE:-ghcr.io/hijera/foxxycode-agent:latest} |
foxxycode-agent:${FOXXYCODE_VERSION:-dev} (built locally) |
| Command | Image CMD: http -H 0.0.0.0 -P 12345, overridable with FOXXYCODE_COMMAND |
Same |
| Published port | ${FOXXYCODE_HTTP_PORT:-12345}:12345 |
Same |
| Working dir | /workspace (FOXXYCODE_CWD) |
Same |
Healthcheck - foxxycode --version every 5s (container "healthy" once the binary responds; HTTP readiness is separate - use curl /v1/models after up).
Logging - json-file driver with max-size: 100k per file to avoid unbounded log growth on long-lived hosts.
Bind mounts (override host paths with env vars before up):
| Host (default) | Container | Variable |
|---|---|---|
./config.yaml |
/home/user/.foxxycode.yaml (read-only) |
FOXXYCODE_CONFIG |
./workspace |
/workspace |
FOXXYCODE_CWD |
./foxxycode_home |
/home/user/.foxxycode |
FOXXYCODE_HOME |
Fixed inside the container (set by compose environment):
| Variable | Value | Role |
|---|---|---|
FOXXYCODE_CONFIG |
/home/user/.foxxycode.yaml |
Points the loader at the mounted file (not $FOXXYCODE_HOME/config.yaml) |
FOXXYCODE_HOME |
/home/user/.foxxycode |
Sessions, skills, scheduler store |
FOXXYCODE_CWD |
/workspace |
Default cwd for new sessions and tool paths |
Provider keys can be injected from the host shell (optional, empty if unset):
OPENAI_API_KEY,ANTHROPIC_API_KEY,DEEPSEEK_API_KEY
Prefer mounting secrets via config or your orchestrator; do not commit real keys.
Published image (from repo root or any directory where you keep config.yaml, workspace/, foxxycode_home/):
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f foxxycode
curl -sS http://127.0.0.1:12345/v1/models | head
docker compose restart foxxycode
docker compose downPin a GHCR tag without editing YAML:
export FOXXYCODE_IMAGE=ghcr.io/hijera/foxxycode-agent:0.2.0
docker compose pull
docker compose up -dChange only the host port:
export FOXXYCODE_HTTP_PORT=8080
docker compose up -d
# UI: http://127.0.0.1:8080/Build from source (dev compose file):
docker compose -f docker-compose.dev.yml build foxxycode
docker compose -f docker-compose.dev.yml up -d --build
docker compose -f docker-compose.dev.yml logs -f foxxycode
docker compose -f docker-compose.dev.yml downOptional build args on the dev file:
export FOXXYCODE_VERSION="$(git describe --tags --dirty 2>/dev/null || echo dev)"
export FOXXYCODE_BUILD_TAGS="http,scheduler,ui,memory,gateway"
docker compose -f docker-compose.dev.yml build foxxycodeFOXXYCODE_BUILD_TAGS must stay comma-separated with no spaces, matching go build -tags=. The dev file defaults to http,scheduler,ui,memory,gateway (matching the Dockerfile BUILD_TAGS default) so the built image can run the messenger gateway; drop gateway to trim it.
Both compose files run foxxycode http by default. Override the subcommand with FOXXYCODE_COMMAND (shell-split into args) to run any other mode - for the messenger gateway:
# Build from source: the dev image already includes the `gateway` tag.
export TELEGRAM_BOT_TOKEN="<bot-token>" # or leave in $FOXXYCODE_HOME/.env
export FOXXYCODE_COMMAND="gateway --cwd /workspace"
docker compose -f docker-compose.dev.yml up -d --build
docker compose -f docker-compose.dev.yml logs -f foxxycode # expect: "telegram bot connected"Notes:
- The published GHCR image is built without the
gatewaytag (CIdocker-build-push.yamlsetsBUILD_TAGS=http,scheduler,ui,memory), sodocker-compose.ymlwithFOXXYCODE_COMMAND=gatewayneeds an image built with it - use the dev file or a customFOXXYCODE_IMAGE. - The bot token is read from
TELEGRAM_BOT_TOKEN(passed through by both compose files) or$FOXXYCODE_HOME/.env; keep it out of git. - If your
gateways.telegram.proxypoints at a host-local proxy (e.g.socks5://127.0.0.1:7890), it is unreachable from inside the container - usehost.docker.internalor addnetwork_mode: hostin adocker-compose.override.yml. - Gateway mode uses no inbound port (Telegram long-polling); the mapped
12345is simply unused.
Create docker-compose.override.yml next to docker-compose.yml (Compose loads it automatically):
services:
foxxycode:
image: ghcr.io/hijera/foxxycode-agent:0.2.0
ports:
- "8080:12345"
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY}Use overrides for machine-specific ports, pinned tags, or extra environment - keep secrets out of git.
export FOXXYCODE_IMAGE=ghcr.io/hijera/foxxycode-agent:NEW_TAG
docker compose pull
docker compose up -dSessions and config on the host (./foxxycode_home, mounted config.yaml) survive image swaps. If a new binary fails health checks, inspect docker compose logs foxxycode and roll back FOXXYCODE_IMAGE to the previous tag.
From a checkout of this repository (or any folder where you keep config.yaml, workspace/, and foxxycode_home/):
1. Prepare config and directories
cp config.example.yaml config.yaml
mkdir -p workspace foxxycode_homeEdit config.yaml: configure at least one entry under providers and models, and set agent.model to a listed model id. You can leave api_key empty and pass OPENAI_API_KEY (or NAME_API_KEY for provider name) through compose environment instead.
Optional: set httpserver.host to 0.0.0.0 and httpserver.port to 12345 in YAML. The container CMD already runs foxxycode http -H 0.0.0.0 -P 12345, so flags apply even if httpserver is omitted from the file.
2. Start FoxxyCode (published image from GHCR):
docker compose pull
docker compose up -d3. Connect with the bundled UI
Open in a browser on the host (same machine as Docker, unless you forwarded the port):
http://127.0.0.1:12345/
What you get:
| URL | Purpose |
|---|---|
/ |
Embedded SPA (chat composer, sessions, tools in transcript) |
/#/settings |
Live config.yaml editor (GET/PUT /foxxycode/config) |
/docs/ |
Swagger UI |
/v1/models |
Model and mode list (also used by the SPA model picker) |
Typical first-time flow in the UI:
- Confirm the page loads (static assets from
go:embed). - In the composer toolbar, select a backend model (rows with
owned_byother thanfoxxycodecome from your YAMLmodelslist). - Switch agent vs plan mode if needed (session operating profiles).
- Type a message and send. The UI calls
POST /v1/responseswithstream: trueand shows streaming assistant output. - Files created or edited by tools land under the mounted workspace (host
./workspace→ container/workspace,FOXXYCODE_CWD).
Sessions and skills state persist under ./foxxycode_home on the host (FOXXYCODE_HOME in the container).
4. Sanity check (optional)
curl -sS http://127.0.0.1:12345/v1/models | head
docker compose logs -f foxxycodeSecurity: foxxycode http has no application-level auth. Treat port 12345 like any admin API - bind to localhost, use a firewall, or put a reverse proxy with TLS and authentication in front for remote access.
For a local Dockerfile build, use docker-compose.dev.yml - see Docker Compose above.
Dockerfile ARG BUILD_TAGS defaults to http,scheduler,ui,memory,gateway (comma-separated, same meaning as go build -tags=).
http-foxxycode httpand REST gateway (see docs/http-api.md).ui- embedded SPA on/(needshttp).scheduler- scheduler subsystem (docs/scheduler.md).memory- long-term memory copilot and session memory REST (external/memory/README.md); toggle runtime behavior viamemory.enabled.gateway- messenger gateway mode (foxxycode gateway, see docs/gateway.md); reachable by overriding the container command. Note the published GHCR image is built without this tag.
To build an image without memory or the embedded UI, override BUILD_TAGS (for example http,scheduler,ui or http,scheduler) via docker compose args or docker build --build-arg.
Volume and environment details for Compose are in Docker Compose. On a bare-metal install without FOXXYCODE_CONFIG, the loader prefers $FOXXYCODE_HOME/config.yaml (see docs/config.md).
ui-builder(Node) - runsnpm ciandnpm run build:gounderexternal/ui, producing the static bundle copied into the Go tree forgo:embedwhenuiis inBUILD_TAGS.build(Go) -CGO_ENABLED=0,GOOS/GOARCHfrom BuildKitTARGETOS/TARGETARCH(CI buildslinux/amd64andlinux/arm64),go build -tags="$BUILD_TAGS"with-trimpathand-ldflags "-s -w -X ...Version=...", writes/out/foxxycode, copiesca-certificates.crtfor HTTPS clients.scratch- only the binary and CA bundle;ENTRYPOINT/bin/foxxycode, defaultCMDhttp -H 0.0.0.0 -P 12345.
./examples/httpserver/docker.shThe script builds a temporary config.yaml, brings up foxxycode with docker-compose.dev.yml, waits for /v1/models, then runs examples/httpserver/http_smoke_gateway.py.