Deployment
Both setups run one container, claude-discord, with a TTY (Claude Code is a terminal app), restart: unless-stopped, a 4 GB memory limit and 2 CPUs.
Docker Compose
Section titled “Docker Compose”docker-compose.yml builds the image from the local Dockerfile.
cp .env.example .env # then fill in the valuesdocker compose up -d --builddocker compose logs -f| Volume | Mount | Contents |
|---|---|---|
claude-home (named) |
/home/node/.claude |
Config, installed plugin, access.json, conversation history. |
./workspace (bind) |
/workspace |
The code the bot works on, visible on the host. |
The bind-mounted ./workspace must be writable by UID 1000 (the node user):
mkdir -p workspace && sudo chown 1000:1000 workspaceFrom a GitHub release (no build needed)
Section titled “From a GitHub release (no build needed)”Every release has a ready-made image attached, built by the Release image workflow (.github/workflows/release.yml).
- Open the repository’s Releases and download the image for your server:
claude-discord-<version>-arm64.tar.gz: Raspberry Pi and other ARM serversclaude-discord-<version>-amd64.tar.gz: x86 servers
- Portainer → Images → Import → upload the file. It loads as
claude-discord:latest(andclaude-discord:<version>). - Create the stack as in steps 2–4 below, or for an update, recreate the container (pull disabled) so it uses the new
claude-discord:latest.
Command line instead: docker load -i claude-discord-<version>-arm64.tar.gz.
To make a release, push a version tag, or run the workflow from the Actions tab with a tag name:
git tag v1.0.0 && git push origin v1.0.0The workflow builds arm64 (emulated, the slow part) and amd64 images, attaches them with SHA-256 checksums and the Portainer build-context tar, then publishes the release.
Portainer
Section titled “Portainer”docker-compose.portainer.yml uses a pre-built image, because Portainer’s stack editor cannot build from a local folder.
- Build the image (or import one from a release, see above). Create the tar from the repository root with
tar cf claude-discord-build.tar --exclude=node_modules --exclude=dist Dockerfile entrypoint.sh bridge. Then Images → Build a new image → Upload, chooseclaude-discord-build.tar, name itclaude-discord:latest. - Create the stack. Stacks → Add stack → Web editor, paste
docker-compose.portainer.yml. - Add variables. Under Environment variables, switch to Advanced mode and paste your filled-in
.env. Portainer saves them asstack.env, which the stack file reads. - Deploy the stack.
| Volume | Mount | Contents |
|---|---|---|
claude-home |
/home/node/.claude |
Config, plugin, access.json, conversation history. |
claude-workspace |
/workspace |
The code the bot works on. |
Both are named volumes here, so the workspace is not on the host filesystem directly. Use the container console or docker cp to get files out.
First start checklist
Section titled “First start checklist”- The bot is invited to the server with permission to read and send messages in the channels listed in
DISCORD_CHANNELS. - Message Content Intent is enabled for the bot in the Developer Portal.
- The logs show Claude starting with no
... is requirederror. - With
DISCORD_REQUIRE_MENTION=true, @mention the bot in an allowed channel to test it.