Skip to content

Docker

Run agent-detective in containers for local development or production-style deployment on a single host. For a comparison with bare-metal and from-source installs, see installation.md.

  • Docker Engine 24+ with BuildKit (default on Docker Desktop)
  • Optional: Docker Compose v2

The image copies pnpm-workspace.yaml (workspace is packages/* plus the root app), pnpm-lock.yaml, and runs pnpm install --frozen-lockfile so the lockfile in git must match the Docker build context.

TargetPurpose
dev (default)Installs the monorepo with pnpm; use with bind mounts for src/ and packages/ so pnpm dev hot-reloads.
productionBuilds workspace packages, bundles the app with tsup, prunes devDependencies, runs node dist/index.js. Optional CLI agents via build-arg AGENTS: opencode from opencode-ai (OpenCode docs), claude from @anthropic-ai/claude-code. The Cursor Agent CLI (agent on PATH) is not installed in the default image — use the official install script on the host or extend the image (see cursor-agent.md).

The HTTP API is mounted under /api. Health is:

  • GET /api/health — JSON body includes "status" (ok | degraded | unhealthy).

Compose and the production image healthcheck use this path (not /health).

From the repository root:

Terminal window
docker compose build
docker compose up

This uses docker-compose.yml, target dev, and mounts ./src, ./packages, and ./config into the container. The app listens on port 3001.

  • CLI agents (OpenCode, Claude, etc.) are typically installed on the host and invoked from the container only if their binaries are on PATH inside the image. For a fully self-contained dev image, extend the Dockerfile or install agents in a custom image.
Terminal window
docker compose build --no-cache
Terminal window
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d

Environment variables (see also configuration.md and config/default.json):

VariableDescription
PORTHost port mapped to container 3001 (default 3001)
IMAGE_NAMEImage name/tag (default agent-detective:latest)
AGENTSBuild-arg when building: comma-separated agents to npm install -g in the image
AGENTDefault agent id (e.g. opencode)
LOG_LEVELAliased to OBSERVABILITY_LOG_LEVEL when the latter is unset (debug | info | warn | error)
AGENTS_*_MODELModel overrides per agent (see configuration.md)
AGENTS_RUNNER_*, OBSERVABILITY_REQUEST_LOGGER_EXCLUDE_PATHSSee configuration.md
REPO_CONTEXT_*, SUMMARY_MAX_OUTPUT_CHARS, JIRA_*As in configuration.md
JIRA_API_TOKEN / JIRA_EMAIL / JIRA_BASE_URLJira adapter when that plugin is listed in config

Jira secrets: pass JIRA_API_TOKEN, JIRA_EMAIL, and JIRA_BASE_URL in the environment (.env next to compose, CI variables, or your orchestrator). This repository’s compose file does not require Docker Swarm-style secret files.

TLS in front of the app: The nginx server / proxy_pass example (long timeouts, headers for the API) lives in a single place: deployment.md#reverse-proxy-nginx — set proxy_pass to the host port that maps to the container’s 3001 (or your PORT).

Terminal window
docker build --target production --build-arg AGENTS=opencode,claude -t agent-detective:latest .
docker run --rm -p 3001:3001 -v "$(pwd)/config:/app/config:ro" agent-detective:latest

The process cwd must be the app root (WORKDIR /app in the image) so workspace plugins resolve under packages/.

WorkflowWhenWhat
ci.ymlPR and push to mainLint, typecheck, test, build (Turbo).
docker.ymlPR and push to mainRuns the same checks, then builds the production image. Pushes to GHCR only on push to main; on PRs the image is built and loaded locally on the runner for a smoke test (no registry push).
release.ymlTag v*.*.*CI, multi-arch push to GHCR, Trivy scan, GitHub Release.

Image: ghcr.io/<owner>/<repo> (repository name lowercased by the registry).

Images are published to ghcr.io/<owner>/<repo> (for this upstream repo: ghcr.io/toniop99/agent-detective). Pushes to main produce latest (multi-arch linux/amd64 and linux/arm64) with AGENTS=opencode baked in. Version tags (for example stable, 1.x.x) come from the release workflow and may include additional CLIs; see .github/workflows/release.yml.

Terminal window
docker pull ghcr.io/toniop99/agent-detective:latest
docker run -d --name agent-detective -p 3001:3001 \
-v "$(pwd)/config:/app/config:ro" \
-e NODE_ENV=production \
ghcr.io/toniop99/agent-detective:latest

Optional: mount custom plugins and pass Jira or model overrides (same variables as docker-compose.prod.yml).

From a directory that contains config/ (copy config/default.json from the repo or your own files) and optionally plugins/:

Terminal window
docker compose -f docker-compose.ghcr.yml pull
docker compose -f docker-compose.ghcr.yml up -d

Override the image (for example a fork or a release tag):

Terminal window
export GHCR_IMAGE=ghcr.io/toniop99/agent-detective:stable
docker compose -f docker-compose.ghcr.yml up -d

agent-detective shells out to opencode; it does not store provider API keys in config/default.json. Pass the environment variables your provider needs via docker run -e / Compose environment, or follow OpenCode configuration for interactive setup (for example OpenCode Zen). The Node process inherits the container environment, so those variables reach the CLI.

Terminal window
docker run --rm ghcr.io/toniop99/agent-detective:latest bash -lc 'command -v opencode && opencode --version'
wget -qO- http://127.0.0.1:3001/api/health
wget -qO- http://127.0.0.1:3001/api/agent/list

Expect opencode in the agent list with available": true after a successful install.

With Docker installed:

Terminal window
docker build --target production -t agent-detective:local .
docker run --rm -p 3001:3001 -v "$(pwd)/config:/app/config:ro" agent-detective:local
# In another terminal:
wget -qO- http://127.0.0.1:3001/api/health
  • Plugins not loading in the image: Ensure config/*.json lists plugins that exist in the image (node_modules or mounted ./packages). Built plugins are loaded from packages/<name>/dist/index.js when the bare package import is unavailable.
  • pnpm install fails in Docker: The dev and builder stages use the same pnpm major as package.json packageManager via Corepack; keep them in sync when upgrading.
  • Permission errors on mounted volumes (Linux): Adjust host directory ownership or run dev compose with a user override matching your UID.