Docker image
The image is built from a two-stage Dockerfile.
Stages
Section titled “Stages”| Stage | Base | Purpose |
|---|---|---|
bun |
oven/bun:1.2 |
Only used to copy the bun binary out. |
bridge |
node:22-bookworm-slim |
Builds bridge/ (TypeScript → dist/) and drops dev and optional dependencies. Copied to /opt/bridge. |
| final | node:22-bookworm-slim |
Runtime: Node 22 for Claude Code and the bridge, Bun for the Discord plugin. |
Bun is needed because the Discord channel plugin’s MCP server runs on Bun. It is copied to /usr/local/bin/bun.
Build argument
Section titled “Build argument”| Argument | Default | Meaning |
|---|---|---|
CLAUDE_CODE_VERSION |
2.1.322 |
Version of @anthropic-ai/claude-code installed globally with npm. |
The version is pinned so rebuilding the image is reproducible. Keep it in step with @anthropic-ai/claude-agent-sdk in bridge/package.json (SDK 0.3.N goes with Claude Code 2.1.N): the bridge runs the globally installed claude, and the SDK’s own bundled binaries are left out of the image to save about 650 MB. Auto-update is turned off at runtime (DISABLE_AUTOUPDATER=1); to upgrade, rebuild with a new value:
docker build --build-arg CLAUDE_CODE_VERSION=<new version> -t claude-discord:latest .Installed packages
Section titled “Installed packages”Installed with apt-get (no recommends), then the apt lists are deleted to keep the image small.
| Group | Packages |
|---|---|
| Core | git, ca-certificates, curl, tini, jq, ripgrep |
| Admin | sudo, procps, less, nano, file |
| Media | ffmpeg |
| Python | python3, python3-pip, python3-venv |
| Build | build-essential |
| Archives / download | wget, unzip, zip, xz-utils |
| Remote | openssh-client |
| GitHub | gh (GitHub CLI, from the official cli.github.com apt repository, verified with its keyring) |
tini is PID 1 so signals are forwarded and zombie processes are reaped. jq is used by the entrypoint to edit JSON config. ripgrep is used by Claude Code for code search.
User and permissions
Section titled “User and permissions”- The container runs as the non-root user
node(UID 1000 in the Node base image). Claude Code refusesbypassPermissionsmode when running as root, so a non-root user is required. nodehas passwordless sudo (/etc/sudoers.d/node, mode0440) so the bot can install extra packages withsudo apt-get install. See Security before relying on this./workspaceand/home/node/.claudeare created and owned bynode.
Environment and entrypoint
Section titled “Environment and entrypoint”| Setting | Value |
|---|---|
WORKDIR |
/workspace |
DISABLE_AUTOUPDATER |
1 |
CLAUDE_CONFIG_DIR |
/home/node/.claude |
ENTRYPOINT |
/usr/bin/tini -- /usr/local/bin/entrypoint.sh |
The entrypoint script is described in Entrypoint.
Building
Section titled “Building”With Docker:
docker build -t claude-discord:latest .For Portainer, the build context is shipped as claude-discord-build.tar (it contains Dockerfile, entrypoint.sh and the bridge sources). To recreate it after editing any of them:
tar cf claude-discord-build.tar --exclude=node_modules --exclude=dist Dockerfile entrypoint.sh bridgeThen upload it as described in Deployment.