Zero-touch startup · Persistent state · Multi-agent ready · Fully automated
A production-ready Docker setup for running Claude Code agents that communicate through Telegram and Discord. Deploy one agent or a fleet — each with its own identity, channels, and workspace.
The problem: Claude Code is an interactive CLI tool designed for local use. Running it headlessly in a container requires bypassing onboarding wizards, configuring auth tokens, installing channel plugins, and managing state across restarts.
This solution: A single Docker image that handles all of that automatically. Configure via environment variables, deploy with docker compose up -d, and your agent is live on Telegram and Discord.
| Feature | Description |
|---|---|
| Zero-touch startup | No interactive prompts — onboarding, trust dialogs, and permission screens all pre-configured |
| Telegram integration | Communicate with your agent via Telegram bot (auto-installed plugin) |
| Discord integration | Communicate with your agent via Discord bot (auto-installed plugin) |
| Persistent state | Docker volumes preserve plugins, pairings, sessions, and workspace across restarts |
| Auto plugin install | Telegram and Discord plugins installed automatically on first boot |
| Multi-agent ready | Deploy multiple agents from the same image — just change the identity file |
| Configurable | Agent name, model, resources — all via environment variables |
| Secure by default | Tokens in .env files with 600 permissions, .gitignore for secrets |
┌─────────────┐ ┌──────────────┐
│ Telegram │ │ Discord │
│ Bot API │ │ Gateway │
└──────┬──────┘ └──────┬───────┘
│ │
│ ┌──────────────┐ │
└────┤ MCP Servers ├───┘
│ (bun) │
└──────┬───────┘
│
┌────────▼────────┐
│ Claude Code │
│ (tmux) │
│ │
│ ┌───────────┐ │
│ │ CLAUDE.md │ │ ← Agent identity
│ │ (system │ │
│ │ prompt) │ │
│ └───────────┘ │
└────────┬────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ .claude │ │workspace│ │ .env │
│ volume │ │ volume │ │ (host) │
└─────────┘ └─────────┘ └─────────┘
plugins code & tokens &
pairings projects config
settings
- Docker 20.10+
- Docker Compose 2.0+
- A Claude Pro, Max, Team, or Enterprise subscription
git clone https://github.com/naorbrig/claude-code-agent-docker.git
cd claude-code-agent-dockerOn your local machine (requires Claude Code installed):
claude setup-tokenSign in via browser, copy the long-lived token.
cp .env.example .envEdit .env with your OAuth token and (optionally) bot tokens:
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-your-token-here
AGENT_NAME=my-agent
AGENT_MODEL=sonnet
TELEGRAM_BOT_TOKEN=123456789:AAH... # optional
DISCORD_BOT_TOKEN=MTIz... # optionalEdit CLAUDE.md — this is your agent's system prompt. Define its personality, skills, and rules.
docker compose up -dDM your Telegram or Discord bot. You'll receive a pairing code. Approve it:
# View the agent's screen
docker exec my-agent tmux capture-pane -t my-agent -p
# Approve Telegram pairing
docker exec my-agent tmux send-keys -t my-agent '/telegram:access pair <code>' Enter
# Approve Discord pairing
docker exec my-agent tmux send-keys -t my-agent '/discord:access pair <code>' EnterYour agent is now live. Send it messages on Telegram or Discord.
| Variable | Required | Default | Description |
|---|---|---|---|
CLAUDE_CODE_OAUTH_TOKEN |
Yes | — | OAuth token from claude setup-token |
AGENT_NAME |
No | claude |
Agent display name (used for tmux session, git, and --name) |
AGENT_MODEL |
No | sonnet |
Model: opus, sonnet, or haiku |
TELEGRAM_BOT_TOKEN |
No | — | Telegram bot token from @BotFather |
DISCORD_BOT_TOKEN |
No | — | Discord bot token from Developer Portal |
GITHUB_TOKEN |
No | — | GitHub PAT for git operations inside the container |
MEMORY_LIMIT |
No | 8g |
Container memory limit |
CPU_LIMIT |
No | 8 |
Container CPU limit |
MEMORY_RESERVATION |
No | 4g |
Container memory reservation |
Deploy a fleet of agents from the same image — each with a different identity and channels.
agents/
├── alice/
│ ├── .env # AGENT_NAME=alice, unique tokens
│ └── CLAUDE.md # Alice's personality
├── bob/
│ ├── .env # AGENT_NAME=bob, unique tokens
│ └── CLAUDE.md # Bob's personality
└── shared/
├── Dockerfile
├── entrypoint.sh
└── docker-compose.yml
services:
alice:
build: .
container_name: alice
init: true
env_file: agents/alice/.env
volumes:
- ./agents/alice/CLAUDE.md:/home/node/CLAUDE.md:ro
- alice-claude:/home/node/.claude
- alice-workspace:/home/node/workspace
restart: unless-stopped
bob:
build: .
container_name: bob
init: true
env_file: agents/bob/.env
volumes:
- ./agents/bob/CLAUDE.md:/home/node/CLAUDE.md:ro
- bob-claude:/home/node/.claude
- bob-workspace:/home/node/workspace
restart: unless-stopped
volumes:
alice-claude:
alice-workspace:
bob-claude:
bob-workspace:- Message @BotFather on Telegram →
/newbot - Copy the bot token to your
.envfile - Deploy the container
- DM your bot → receive a pairing code → approve it
- Create an application at Discord Developer Portal
- Go to Bot tab → copy the token to your
.env - Enable Privileged Gateway Intents: Message Content, Server Members, Presence
- Invite the bot to your server:
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=274877975552&integration_type=0&scope=bot+applications.commandsTip: Your client ID is the base64-decoded first segment of the bot token.
- DM the bot → receive a pairing code → approve it
- Add server channels:
docker exec <container> tmux send-keys -t <agent> '/discord:access group add <channel_id>' Enter
By default, both plugins ignore messages from other bots. If you're running multiple agents that need to talk to each other, patch the plugin on each container:
docker exec <container> sed -i \
's/if (msg.author.bot) return/\/\/ if (msg.author.bot) return/' \
/home/node/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/discord/server.tsdocker exec <container> sed -i \
's/if (msg.from?.is_bot) return/\/\/ if (msg.from?.is_bot) return/' \
/home/node/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/telegram/server.tsRestart the container after patching.
Note: This patch resets on plugin updates — reapply after
claude plugin update.
# View agent output
docker exec <container> tmux capture-pane -t <agent-name> -p
# Attach to agent session (interactive)
docker exec -it <container> tmux attach -t <agent-name>
# Detach with: Ctrl+B, then D
# Send a Claude Code command
docker exec <container> tmux send-keys -t <agent-name> '/compact' Enter
docker exec <container> tmux send-keys -t <agent-name> '/model opus' Enter
# Restart agent
docker restart <container>
# View container logs
docker logs <container> --tail 30
# Check resource usage
docker stats <container> --no-stream-
Dockerfile installs Claude Code + Bun on
node:22-bookworm, pre-configures~/.claude.jsonandsettings.local.jsonto skip all interactive prompts -
entrypoint.sh runs on every container start:
- Patches
~/.claude.jsonto skip onboarding, trust dialog, and bypass permissions prompt - Installs Telegram/Discord plugins if not already present
- Writes bot tokens from environment variables to plugin
.envfiles (permissions600) - Launches Claude Code inside a tmux session with
--channelsflags
- Patches
-
Docker volumes persist
~/.claude/(plugins, pairings, settings) and~/workspace/(code, projects) across container restarts and rebuilds
| Problem | Solution |
|---|---|
| "plugin not installed" in Claude Code header | Restart the container — plugins install on boot |
| Bot not responding to DMs | Check TELEGRAM_BOT_TOKEN / DISCORD_BOT_TOKEN in .env |
| Trust dialog appears on restart | Run: docker exec <c> python3 -c "..." to patch .claude.json (see entrypoint.sh) |
| Discord bot offline in member list | Enable Privileged Gateway Intents in Discord Developer Portal |
| Pairing code not appearing | Verify the MCP server is running: docker exec <c> ps aux | grep bun |
| Agent not responding in Discord server | Add the channel: /discord:access group add <channel_id> |
| Bots can't talk to each other | Apply the bot-to-bot patch |
| Component | Minimum | Recommended |
|---|---|---|
| Docker | 20.10+ | 27.0+ |
| Docker Compose | 2.0+ | 2.29+ |
| RAM | 4 GB | 8 GB per agent |
| CPU | 2 cores | 4+ cores per agent |
| Disk | 5 GB | 20 GB per agent |
| OS | Any with Docker | Ubuntu 22.04+ / macOS |
- OAuth tokens and bot tokens are stored in
.envfiles (git-ignored) and written to plugin directories with600permissions - The container runs as the unprivileged
nodeuser (UID 1000) --permission-mode bypassPermissionsallows the agent to run commands without approval — only use in trusted/sandboxed environments- See SECURITY.md for vulnerability reporting
Contributions are welcome! See CONTRIBUTING.md for guidelines.
MIT — use it however you want.
Built for running AI agent teams in production.