Environment Variables
All configurable environment variables live in compose.yaml under the dev service. The init script writes them into /config on every boot, so they take effect immediately.
ANTHROPIC_AUTH_TOKEN is the only value you must provide: without it, Claude Code won't start. Every other variable falls back to the default shown in its table.
How it works
compose.yaml init.sh /config/.bashrc
(dev service) → writes vars → /config/.zshrc
Edit compose.yaml, recreate the container, done.
Claude Code / API
| Env var | Default | What it does |
|---|---|---|
ANTHROPIC_BASE_URL | (empty) | Leave empty for Anthropic. Set to a third-party URL (DeepSeek, OpenRouter, etc.) to use a different provider |
ANTHROPIC_AUTH_TOKEN | (required) | Your API key. Get one at console.anthropic.com |
ANTHROPIC_MODEL | claude-opus-4-8 | Default model Claude Code uses for all tasks |
ANTHROPIC_DEFAULT_OPUS_MODEL | claude-opus-4-8 | Model used when Opus tier is selected |
ANTHROPIC_DEFAULT_SONNET_MODEL | claude-sonnet-4-6 | Model used when Sonnet tier is selected |
ANTHROPIC_DEFAULT_HAIKU_MODEL | claude-haiku-4-5 | Model for Haiku tier (lighter tasks, subagents) |
CLAUDE_CODE_SUBAGENT_MODEL | claude-haiku-4-5 | Model for background agents spawned by Claude Code |
CLAUDE_CODE_EFFORT_LEVEL | max | Reasoning depth: low, medium, high, or max |
Using a different provider
If ANTHROPIC_BASE_URL is left empty, Claude Code connects directly to Anthropic's API. To use a third-party provider, set the URL and adjust model names.
DeepSeek example:
ANTHROPIC_BASE_URL: https://api.deepseek.com/anthropic
ANTHROPIC_AUTH_TOKEN: sk-... # your DeepSeek key
ANTHROPIC_MODEL: deepseek-v4-pro[1m]
ANTHROPIC_DEFAULT_HAIKU_MODEL: deepseek-v4-flash
CLAUDE_CODE_SUBAGENT_MODEL: deepseek-v4-flash
OpenRouter example:
ANTHROPIC_BASE_URL: https://openrouter.ai/api/anthropic
ANTHROPIC_AUTH_TOKEN: sk-or-v1-... # your OpenRouter key
ANTHROPIC_MODEL: anthropic/claude-opus-4-8
How to apply changes
Edit compose.yaml, recreate the container:
docker compose up -d --force-recreate
Or, for a quick test without recreating:
nano ~/.zshrc
source ~/.zshrc
Git / GitHub
| Env var | What it does |
|---|---|
GIT_USER_NAME | Your name for commits (git config user.name) |
GIT_USER_EMAIL | Your email for commits (git config user.email) |
GITHUB_TOKEN | GitHub fine-grained PAT, used by both git (via gh credential helper) and the gh CLI |
Creating a GitHub token
Go to github.com/settings/tokens?type=beta:
- Repository access: "Only select repositories", pick which ones
- Permissions: Contents (read/write as needed), Metadata (read, auto-selected)
- Copy the
github_pat_...token intoGITHUB_TOKEN
The token can be revoked or scoped anytime. No account password stored: if the container is compromised, you only lose access to the repos you explicitly granted.
After setting these and recreating the container, both git clone/push/pull and gh CLI commands (gh pr, gh issue, etc.) use the token automatically.
Container settings
| Env var | Default | What it does |
|---|---|---|
CUSTOM_USER | abc | Custom username. Replaces the default abc user on boot |
PASSWORD | (required) | Login password for the ttyd web terminal |
SUDO_PASSWORD | (required) | Sudo password inside the container. Also used for SSH login |
PUID | 1000 | User ID for file permissions |
PGID | 1000 | Group ID for file permissions |
TZ | Europe/Rome | Timezone |
Session behavior
These variables are not set in compose.yaml. They are passed at connection time (by your SSH client or in the shell) to control what happens when you log in.
| Env var | Default | What it does |
|---|---|---|
NO_CLAUDE | (unset) | Set to 1 to skip Claude auto-launch and get a plain shell |
TMUX_AUTO | (unset) | Set to 1 to auto-attach a persistent tmux session on login |
TMUX_TIMEOUT | (unset) | Hours before a detached tmux session is auto-killed (-1 = never, 0 = on detach, N = after N hours) |
Claude auto-launch
Claude launches automatically on every login (both SSH and web terminal). You land in /workplace with Claude ready. Exiting Claude (/exit or Ctrl+D) returns you to a normal shell prompt.
To skip the auto-launch:
NO_CLAUDE=1 ssh abc@<server> -p 2222 # → plain shell, no Claude
Persistent sessions with tmux
Set TMUX_AUTO=1 on your SSH client (e.g. Termius environment variables) to wrap every session in tmux. When you disconnect, tmux keeps running. When you reconnect, you reattach to the same session with Claude still there.
# In Termius: set TMUX_AUTO=1 in the host's environment variables
# In a terminal:
TMUX_AUTO=1 ssh abc@<server> -p 2222 # → auto-attached to tmux "main"
Use TMUX_TIMEOUT to auto-clean detached sessions. For example, TMUX_TIMEOUT=2 kills sessions that have been detached for more than 2 hours. Run tmux-keep inside a session to override this and keep it alive indefinitely.
See Daily Workflow for full tmux usage.
Webhook Notifications
When Claude Code finishes responding and waits for input (60+ seconds idle), a webhook can fire if no one is connected to the container. This is useful for long-running tasks: start Claude inside tmux, disconnect, and get notified when Claude is ready for your next prompt.
The feature uses Claude Code's built-in idle_prompt Notification hook. No external daemon or polling is required.
| Env var | Default | What it does |
|---|---|---|
CLAUDE_WEBHOOK_URL | (empty) | Webhook endpoint URL. Leave empty to disable. Receives a JSON POST when Claude goes idle with no active connections |
CLAUDE_WEBHOOK_IDLE | 60 | Seconds of terminal silence before Claude is considered idle (minimum 60, matching Claude Code's hook timing) |
Webhook payload
{
"event": "claude_idle",
"timestamp": "2026-07-24T15:30:00Z",
"hostname": "claude-world",
"session_id": "35e6a910-...",
"last_output": "The fix has been applied to utils.py.\n\nAll tests pass.\n\n> What should I do next?"
}
| Field | Description |
|---|---|
event | Fixed value "claude_idle" |
timestamp | UTC ISO 8601 timestamp |
hostname | Container hostname |
session_id | Claude Code session ID that triggered the hook |
last_output | Last text message from Claude (up to 2000 characters) |
How it works
- You connect via SSH or web terminal, Claude auto-launches
- You give Claude a task, it starts processing
- You disconnect (tmux keeps Claude running if
TMUX_AUTO=1) - Claude finishes the task and waits for input
- After 60 seconds of idle, Claude Code fires the
idle_prompthook - The hook checks
whofor active connections - If no one is connected, it sends the webhook with Claude's last output
The webhook can point to any HTTP endpoint: Slack webhooks, Discord webhooks, Telegram bots, Zapier, n8n, ntfy, or a custom server.