🌍 English | Español | Français | Italiano | Português | Deutsch | Русский | हिन्दी | 中文 | 日本語 | 한국어
HolyClaude
Stop configuring. Start building.
One command. Full AI development workstation. Claude Code, web UI, headless browser, 8 AI CLIs, 50+ dev tools — containerized and ready.
You were going to spend 2 hours setting this up manually. Or you could just docker compose up.
Works with your existing Claude Code subscription. Max/Pro plan, API key — whatever you have, it just works.
[!TIP]
Stop maintaining. Start building.
> A hosted AI workstation. Always-on Linux box.
> holycode.cloud
What is this?
You know the drill. You want Claude Code. But you also want it in a browser. With a headless browser for screenshots and testing. With Playwright configured. With every AI CLI. With TypeScript, Python, deployment tools, database clients, GitHub CLI.
v1.6.7 updates the bundled AI CLIs and developer tools using official upstream releases. Debian Chromium stays at 155.0.8059.39 for AMD64 and ARM64. The Docker client remains an opt-in Full-derived recipe; stock images still ship without it. See the deferred replacements for integrations that still need an equivalent official release.
[!WARNING]
Dependency scans still run. Accepted upstream vulnerabilities remain listed with their installed versions, raw severity and available fixes; acceptance does not mean fixed. Retained modified third-party artifacts are identified separately from official releases. HolyClaude source checks, artifact integrity and runtime tests still block delivery. See the dependency policy and the release's scan evidence before deploying.
Release-sensitive facts are also published in contracts/product-facts.json. The release workflow checks that contract against the Dockerfile and Compose files before building images.
So you start installing things. One by one. Then Chromium won't launch because Docker's shared memory is 64MB. Then Xvfb isn't configured. Then the UID inside the container doesn't match your host and everything is permission denied. Then you realize Claude Code's installer hangs when WORKDIR is root-owned. Then SQLite locks on your NAS mount. Then—
HolyClaude is the container I built after solving every single one of those problems.
I've been running this daily on my own server for weeks. Every bug has been hit, diagnosed, and fixed. Every edge case has been handled. Every "why doesn't this work in Docker" has been answered.
You pull it. You run it. You open your browser. You build.
:credit_card: Use Your Existing Subscription
This runs the real Claude Code CLI. Not a wrapper. Not a proxy. Not a knock-off.
Your existing Anthropic account works directly:
- Claude Max/Pro plan — authenticate through the web UI (OAuth), same as desktop Claude Code
- Anthropic API key — set it through the web UI, same billing as always
- No extra cost — HolyClaude is free and open source. You only pay Anthropic for what you use, like you already do.
HolyClaude operates no credential relay. Bundled tools read credentials from container files, bind mounts, or environment variables, then contact the providers you configure directly.
Table of Contents
| | Section | |---|---| | :zap: | Quick Start | | :computer: | Platform Support | | :star2: | Why HolyClaude | | :credit_card: | Subscription & Authentication | | :package: | Image Variants | | :whale: | Docker Compose — Quick | | :whale2: | Docker Compose — Full | | :wrench: | Environment Variables | | :rocket: | What's Inside | | :robot: | AI CLI Providers | | :mag: | Desloppify | | :llama: | Using Ollama | | :building_construction: | Architecture | | :file_folder: | Project Structure | | :floppy_disk: | Data & Persistence | | :lock: | Permissions | | :shield: | Remote Access & Exposure | | :bell: | Notifications | | :arrows_counterclockwise: | Upgrading | | :construction: | Troubleshooting | | :warning: | Known Issues | | :hammer_and_wrench: | Building Locally | | :bar_chart: | Alternatives | | :rocket: | Roadmap | | :trophy: | Built with HolyClaude | | :handshake: | Contributing | | :heart: | Support | | :scroll: | Third-Party Software | | :page_facing_up: | License |
:zap: Quick Start
1. Create a folder for HolyClaude:
mkdir holyclaude && cd holyclaude
2. Create a docker-compose.yaml file. Copy one of the templates below:
- Quick template — minimal, zero config, just works
- Full template — all options, fully documented
docker compose up -d
4. Open the web UI:
http://localhost:3001
5. Create a CloudCLI account (takes 10 seconds), sign in with your Anthropic account, and you're live.
No .env files. No pre-configuration. No reading 40 pages of docs before you can start. It just runs.
Want to reach it from outside your network? Don't port-forward it. See Remote Access & Exposure — use Tailscale or Cloudflare Tunnel instead.
:computer: Platform Support
| Platform | Status | Notes |
|----------|--------|-------|
| Linux (amd64) | ✅ Fully supported | Native performance, recommended |
| Linux (arm64) | ✅ Fully supported | Raspberry Pi 4+, Oracle Cloud, AWS Graviton |
| macOS (Docker Desktop) | ✅ Fully supported | Apple Silicon & Intel via Docker Desktop |
| Windows (WSL2 + Docker Desktop) | ✅ Fully supported | Requires WSL2 backend |
| Synology / QNAP NAS | ✅ Fully supported | Use CHOKIDAR_USEPOLLING=true for SMB mounts |
| Kubernetes | 🔜 Coming soon | Helm chart planned |
:star2: Why HolyClaude
I built this because I was tired of re-doing the same setup every time. Installing Claude Code, wiring up a web UI, configuring Chromium in Docker, fixing permission issues, debugging process supervision. Every time.
So I made a container that does all of it. And then I hit every possible bug so you don't have to.
| | HolyClaude | Doing it yourself |
|---|---|---|
| Setup | 30 seconds | 1-2 hours (if it goes well) |
| Claude Code | Pre-installed, pre-configured, ready | Install, configure, debug installer hanging, fix WORKDIR |
| Web UI | CloudCLI included with plugins | Find a web UI, install it, configure it, wire it to Claude |
| Headless browser | Chromium + Xvfb + Playwright, configured | Install Chromium, install Xvfb, configure display :99, fix shm, fix sandbox, fix seccomp... |
| AI CLIs | 8 providers, one container | Install each one separately across 3 package managers |
| Dev tools | 50+ tools, ready | apt-get install / npm i -g / pip install for the next hour |
| Process management | s6-overlay (auto-restart, graceful shutdown) | Write your own supervisord config or hope Docker restart works |
| Persistence | Bind-mounted tool config and workspace survive rebuilds | Figure out Docker volumes, debug "why is this a directory not a file" |
| Updates | docker compose pull && docker compose up -d | Update 50 tools manually, pray nothing breaks |
| Multi-arch | AMD64 + ARM64 | Pray your Dockerfile builds on ARM |
The last row of every manual setup is "works on my machine." HolyClaude works on every machine.
:credit_card: Subscription & Authentication
HolyClaude runs the official Claude Code CLI from Anthropic. Your existing account works out of the box.
What works:
| Authentication method | How | Cost | |----------------------|-----|------| | Claude Max/Pro plan (subscription) | Sign in through CloudCLI web UI — same OAuth flow as desktop | Your existing subscription, no extra charge | | Anthropic API key | Paste your API key in the web UI | Pay-per-use, same Anthropic billing |
What doesn't work:
| | Why | |---|---| | OpenAI API key for Claude | Different company, different API. OpenAI keys work with the Codex CLI (also pre-installed) |
ChatGPT Plus/Pro subscribers: Your subscription works with the Codex CLI. Runcodex login --device-authinside the container to authenticate with your ChatGPT account. If you need Codex's browser callback flow from your host, expose port1455in the full compose template below.
Other AI CLIs included:
| CLI | What you need |
|-----|--------------|
| Gemini CLI | Google AI API key (GEMINI_API_KEY) |
| OpenAI Codex | OpenAI API key (OPENAI_API_KEY) or ChatGPT Plus/Pro subscription (codex login --device-auth) |
| Cursor | Cursor API key (CURSOR_API_KEY) |
| TaskMaster AI | Uses your AI provider keys (Anthropic, OpenAI, etc.) |
| Junie | JetBrains account (JetBrains AI subscription) |
| OpenCode | Configure via opencode TUI (OpenRouter and other providers) |
| Pi Coding Agent | Configure through pi (supports multiple providers) |
HolyClaude is free and open source. You only pay your AI providers for usage, same as you already do. HolyClaude does not relay provider credentials. Bundled tools read them from container files, bind mounts, or environment variables and contact providers directly.
:package: Image Variants
Two flavors. Same quality. Pick your weight class.
| Tag | What you get | Best for | Docker Hub compressed size |
|-----|-------------|----------|----------------------------|
| latest | Everything pre-installed — every tool, every library, every CLI | Most users. Zero wait time. Claude never has to stop and install something. | Check by architecture |
| slim | Core tools only — Claude installs extras on-demand | Smaller VPS, limited disk, metered bandwidth | Check by architecture |
| X.Y.Z | Full image, pinned version | Production stability — you control when to update | Same as latest for that release |
| X.Y.Z-slim | Slim image, pinned version | Production + small footprint | Same as slim for that release |
# Full — batteries included (recommended)
docker pull coderluii/holyclaude
Slim — lean and mean
docker pull coderluii/holyclaude:slim
latest is always the full image. Slim users: don't worry — when you ask Claude to do something that needs a missing tool, it installs it in seconds. You get the same capabilities, just with a smaller initial download.
> Download size varies by release and architecture; check the matching tag on Docker Hub. It reports compressed transfer size. Docker, Synology Container Manager, and NAS filesystems can show a larger unpacked size after layers are extracted. Use slim when disk space or bandwidth matters more than having every tool ready on first boot.
:whale: Docker Compose — Quick
The "I just want it running" template. Copy this entire block into a docker-compose.yaml file:
# ==============================================================================
HolyClaude — Quick Start
Just run: docker compose up -d
Then open: http://localhost:3001
==============================================================================
services:
holyclaude:
image: coderluii/holyclaude:latest # Full image (use :slim for smaller download)
container_name: holyclaude
hostname: holyclaude
restart: unless-stopped
shm_size: 2g # Retained browser default for this release
network_mode: bridge
cap_add:
- SYS_ADMIN # Current browser profile for this release; hardening is separate
- SYS_PTRACE # Debugging-related capability
security_opt:
- seccomp=unconfined # Current browser profile for this release; hardening is separate
ports:
- "127.0.0.1:3001:3001" # CloudCLI web UI, localhost only
volumes:
#
# ./data/claude — Your settings, credentials, API keys, and Claude's memory.
# This is what survives container rebuilds.
# NEVER delete this folder — your auth lives here.
#
- ./data/claude:/home/claude/.claude
#
# ./workspace — Your code. All projects go here.
# Bind-mounted so you can access files from your host.
#
- ./workspace:/workspace
environment:
- TZ=UTC # Your timezone (e.g., America/New_York, Europe/London)
Then:
docker compose up -d
Open http://localhost:3001. Create a CloudCLI account. Sign in with your Anthropic account. Build something.
That's the whole setup. You're done.
Why these browser caps? This release keeps HolyClaude's current browser container profile in place. Chromium does not universally requireSYS_ADMIN,SYS_PTRACE, orseccomp=unconfined, andSYS_PTRACEis debugging-related. Keep the web UI bound to127.0.0.1unless you put a real private tunnel or access layer in front of it. Treat any hardening pass as a separate change.
Whyshm_size: 2g? Docker gives containers 64MB of shared memory by default. HolyClaude keeps 2GB as the retained browser default for this release because Chromium uses/dev/shmheavily for tab rendering. At 64MB, tabs crash randomly. For heavy browser use (many tabs, complex pages), increase to 4GB.
:whale2: Docker Compose — Full
Same image, every knob exposed. Copy this entire block into a docker-compose.yaml file:
# ==============================================================================
HolyClaude — Full Configuration
All options documented inline.
Detailed docs: https://github.com/CoderLuii/HolyClaude/blob/master/docs/configuration.md
==============================================================================
services:
holyclaude:
image: coderluii/holyclaude:latest # Full image (use :slim for smaller download)
container_name: holyclaude
hostname: holyclaude
restart: unless-stopped
shm_size: 2g # Chromium shared memory — increase to 4g for heavy browser use
network_mode: bridge
cap_add:
- SYS_ADMIN # Current browser profile for this release; hardening is separate
- SYS_PTRACE # Debugging-related capability (strace, lsof)
security_opt:
- seccomp=unconfined # Current browser profile for this release; hardening is separate
ports:
#
# CloudCLI web UI — this is the only port you need.
# Override the host-side port from .env if 3001 is already in use.
#
- "127.0.0.1:${HOLYCLAUDE_HOST_PORT:-3001}:3001"
#
# Dev server ports — uncomment as needed.
# These let you access dev servers running inside the container from your host browser.
#
# - "127.0.0.1:3000:3000" # Next.js / Express
# - "127.0.0.1:4321:4321" # Astro
# - "127.0.0.1:5173:5173" # Vite
# - "127.0.0.1:8787:8787" # Wrangler (Cloudflare Workers)
# - "127.0.0.1:9229:9229" # Node.js debugger
# - "127.0.0.1:1455:1455" # Codex auth callback port
# - "127.0.0.1:2222:22" # Optional SSH, localhost/VPN only
# - "127.0.0.1:60000-60010:60000-60010/udp" # Optional Mosh UDP range
volumes:
#
# PERSISTENT DATA
#
# ./data/claude — Settings, credentials, API keys, Claude's memory file.
# Survives container rebuilds. NEVER delete this folder.
# Override the host path from .env if you want it elsewhere.
#
- ${HOLYCLAUDE_HOST_CLAUDE_DIR:-./data/claude}:/home/claude/.claude
#
# ./workspace — Your code and projects. Everything you build goes here.
# Accessible from your host machine.
# Override the host path from .env if you want a different root.
#
- ${HOLYCLAUDE_HOST_WORKSPACE_DIR:-./workspace}:/workspace
#
# OPTIONAL SSH/MOSH REMOTE SHELL
# Keep authorized_keys outside .claude and /workspace. Mount it read-only.
#
# - ./data/ssh/authorized_keys:/run/holyclaude-ssh/authorized_keys:ro
# - holyclaude-ssh:/var/lib/holyclaude-ssh
environment:
#
# TIMEZONE
# Full list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
#
- TZ=UTC
#
# PERFORMANCE
# Node.js heap memory limit in MB. Increase if you work on large monorepos
# and hit out-of-memory errors. 4096 (4GB) is a solid default.
#
- NODE_OPTIONS=--max-old-space-size=4096
#
# USER MAPPING
# Docker-style UID/GID remapping. Run id -u and id -g on your host.
# Rootless Podman users should use docker-compose.podman-rootless.yaml instead.
#
- PUID=1000
- PGID=1000
#
# SMB/CIFS NETWORK MOUNTS
# Only enable these if your volumes are on a NAS, Samba share, or CIFS mount.
# They enable polling-based file watching since network mounts don't support inotify.
# Leave commented out for local storage — polling uses more CPU.
#
# - CHOKIDAR_USEPOLLING=1
# - WATCHFILES_FORCE_POLLING=true
#
# NOTIFICATIONS (optional)
# Get notified when Claude finishes a task or hits an error.
# Uses Apprise — supports 100+ services. Also requires creating a flag file
# inside the container: touch ~/.claude/notify-on
#
# - NOTIFY_DISCORD=discord://webhook_id/webhook_token
# - NOTIFY_TELEGRAM=tgram://bot_token/chat_id
# - NOTIFY_PUSHOVER=pover://user_key@app_token
# - NOTIFY_SLACK=slack://token_a/token_b/token_c
# - NOTIFY_EMAIL=mailto://user:[email protected][email protected]
# - NOTIFY_GOTIFY=gotify://hostname/token
# - NOTIFY_URLS= # catch-all: comma-separated Apprise URLs
#
# AI PROVIDER KEYS (optional)
# Claude Code can authenticate via web UI (OAuth) or ANTHROPIC_API_KEY.
# Set these if you want to use additional AI CLIs or API-based auth.
#
# - GEMINI_API_KEY=your_key
# - OPENAI_API_KEY=your_key
# - CURSOR_API_KEY=your_key
#
# WEB UI BASE PATH (optional)
# Use only when a reverse proxy mounts HolyClaude below a path.
# Example: Tailscale Serve --set-path=/holyclaude
# Do not add a trailing slash.
#
# - HOLYCLAUDE_BASE_PATH=/holyclaude
#
# CODEX PERMISSION MODES (optional)
# CloudCLI Codex chat uses this only when a request omits permissionMode.
# The current browser client sends permissionMode explicitly.
# Raw codex CLI reads HOLYCLAUDE_CODEX_CLI_PERMISSION_MODE only when first creating ~/.codex/config.toml.
# Valid values: default, acceptEdits, bypassPermissions. Recommended: acceptEdits.
# bypassPermissions gives full access with no approval. Use it only for trusted local workspaces.
#
# - HOLYCLAUDE_CODEX_CHAT_PERMISSION_MODE=acceptEdits
# - HOLYCLAUDE_CODEX_CLI_PERMISSION_MODE=acceptEdits
#
# SSH/MOSH REMOTE SHELL (optional, disabled by default)
# Keep this behind localhost, VPN, Tailscale, or a firewall.
#
# - HOLYCLAUDE_SSH_ENABLE=false
# - HOLYCLAUDE_SSH_AUTHORIZED_KEYS=/run/holyclaude-ssh/authorized_keys
# - HOLYCLAUDE_SSH_HOST_KEYS_DIR=/var/lib/holyclaude-ssh/host_keys
# - HOLYCLAUDE_MOSH_ENABLE=false
# - HOLYCLAUDE_MOSH_UDP_START=60000
# - HOLYCLAUDE_MOSH_UDP_END=60010
volumes:
holyclaude-ssh:
Then:
docker compose up -d
If you want to change the host-side port or bind-mount paths without editing compose, copy .env.example to .env and set:
HOLYCLAUDE_HOST_PORT=3003
HOLYCLAUDE_HOST_CLAUDE_DIR=./data/claude
HOLYCLAUDE_HOST_WORKSPACE_DIR=./workspace
These values are read by Docker Compose on the host. They are not container environment variables.
What each section controls:
| Section | What it does | When to change it |
|---------|-------------|-------------------|
| Timezone | Container clock | Always — set to your local TZ |
| Performance | Node.js memory ceiling | Only if you hit OOM errors on large projects |
| User mapping | File permissions between container and host | Docker users: match id -u and id -g. Rootless Podman users: use the Podman compose file below. |
| SMB/CIFS | File watcher polling mode | Only if your volumes live on a NAS or network share |
| Notifications | Push alerts via Apprise (Discord, Telegram, Slack, Email, 100+ services) | If you want to walk away and know when your AI agents are done |
| AI providers | API keys for Gemini, Codex, Cursor, Junie, OpenCode | If you want to use AI CLIs other than Claude |
Every single environment variable is optional. The container runs perfectly with just TZ=UTC. Everything else has sensible defaults or is handled through the web UI.
:wrench: Environment Variables
The complete reference. Every variable, what it defaults to, what it does.
| Variable | Default | What it does |
|----------|---------|--------------|
| TZ | UTC | Container timezone |
| PUID | 1000 | Container user ID for Docker-style UID/GID remapping |
| PGID | 1000 | Container group ID for Docker-style UID/GID remapping |
| NODE_OPTIONS | Full Compose: --max-old-space-size=4096 | Optional Node.js heap limit. The quick Compose file and raw image do not set a heap-size default. |
| HOLYCLAUDE_BASE_PATH | (unset) | Optional web UI subpath such as /holyclaude for Tailscale Serve --set-path or reverse proxies |
| GIT_USER_NAME | HolyClaude User | Git commit author, seeded only when the persisted global config has no value |
| GIT_USER_EMAIL | [email protected] | Git commit email, seeded only when the persisted global config has no value |
| CHOKIDAR_USEPOLLING | (unset) | Set to 1 for SMB/CIFS — enables polling file watchers |
| WATCHFILES_FORCE_POLLING | (unset) | Set to true for SMB/CIFS — enables Python polling |
| NOTIFY_DISCORD | (unset) | Discord webhook URL for notifications |
| NOTIFY_TELEGRAM | (unset) | Telegram bot URL for notifications |
| NOTIFY_PUSHOVER | (unset) | Pushover URL for notifications |
| NOTIFY_SLACK | (unset) | Slack webhook URL for notifications |
| NOTIFY_EMAIL | (unset) | Email (SMTP) URL for notifications |
| NOTIFY_GOTIFY | (unset) | Gotify URL for notifications |
| NOTIFY_URLS | (unset) | Catch-all — comma-separated Apprise URLs |
| ANTHROPIC_API_KEY | (unset) | Anthropic API key (alternative to web UI OAuth) |
| ANTHROPIC_AUTH_TOKEN | (unset) | Anthropic auth token (alternative to API key, or set to ollama for Ollama) |
| ANTHROPIC_BASE_URL | (unset) | Custom Anthropic API endpoint (proxies, private deployments, or Ollama's Anthropic-compatible API) |
| CLAUDE_CODE_USE_BEDROCK | (unset) | Set to 1 to use Amazon Bedrock backend |
| CLAUDE_CODE_USE_VERTEX | (unset) | Set to 1 to use Google Vertex AI backend |
| GEMINI_API_KEY | (unset) | Google Gemini API key |
| OPENAI_API_KEY | (unset) | OpenAI API key (for Codex CLI, or use codex login --device-auth for ChatGPT subscription) |
| CURSOR_API_KEY | (unset) | Cursor API key |
| HOLYCLAUDE_CODEX_CHAT_PERMISSION_MODE | acceptEdits fallback | CloudCLI Codex chat fallback when a request omits permissionMode. The current browser client sends an explicit value. Valid: default, acceptEdits, bypassPermissions |
| HOLYCLAUDE_CODEX_CLI_PERMISSION_MODE | default | Raw codex CLI first-boot mode for new ~/.codex/config.toml only. Valid: default, acceptEdits, bypassPermissions |
| HOLYCLAUDE_DESLOPPIFY_SETUP | off | Optional Desloppify global skill setup. Valid: off, all, claude, codex, gemini, opencode, or comma-separated subsets |
| HOLYCLAUDE_SSH_ENABLE | false | Enables the optional sshd service when a safe authorized_keys mount is present |
| HOLYCLAUDE_SSH_AUTHORIZED_KEYS | /run/holyclaude-ssh/authorized_keys | Read-only public-key file for SSH login as claude; must not live under .claude or /workspace |
| HOLYCLAUDE_SSH_HOST_KEYS_DIR | /var/lib/holyclaude-ssh/host_keys | Root-owned SSH host key directory; mount /var/lib/holyclaude-ssh to keep fingerprints stable after recreate |
| HOLYCLAUDE_MOSH_ENABLE | false | Allows mosh-server for SSH sessions when UDP ports are mapped |
| HOLYCLAUDE_MOSH_UDP_START | 60000 | First UDP port Mosh may use |
| HOLYCLAUDE_MOSH_UDP_END | 60010 | Last UDP port Mosh may use |
:rocket: What's Inside
This is not a minimal container. This is an entire development workstation.
Both variants (full + slim)
Node.js 26 + npm global packages
| Package | What it's for |
|---------|---------------|
| typescript, tsx | TypeScript compilation and execution |
| pnpm | Fast, disk-efficient package manager |
| vite, esbuild | Lightning-fast build tools |
| eslint, prettier | Code quality and formatting |
| serve, nodemon | Static file server, auto-restart dev server |
| concurrently | Run multiple scripts in parallel |
| dotenv-cli | Load env vars from .env files |
Python 3 packages
| Package | What it's for |
|---------|---------------|
| requests, httpx | HTTP clients |
| beautifulsoup4, lxml | Web scraping and HTML parsing |
| Pillow | Image processing (pre-compiled — no waiting) |
| pandas, numpy | Data manipulation (pre-compiled — seriously, you don't want to pip install these at runtime) |
| openpyxl | Read/write Excel files |
| python-docx | Read/write Word documents |
| jinja2, markdown | Templating and markdown rendering |
| pyyaml, python-dotenv | Config file parsing |
| rich, click, tqdm | Beautiful CLIs and progress bars |
| desloppify, bandit, tree-sitter | Code-quality scans, Python security checks, parser-backed code analysis |
| playwright | Browser automation (Python and Node 1.64.0 are baked into both images) |
| pytest, pytest-asyncio, flake8 | Python tests, async tests, and linting |
| aiomqtt, aiohttp | Async MQTT and HTTP clients |
System tools
| Tool | What it's for |
|------|---------------|
| git, gh | Version control + GitHub CLI (PRs, issues, releases from the terminal) |
| ripgrep (rg), fd, fzf | Blazing-fast search — Claude uses these constantly |
| bat, tree, jq | Better cat (syntax highlighting), directory trees, JSON processing |
| curl, wget | HTTP downloads |
| nano, shellcheck | Terminal editing and Bash linting |
| yq, dig | Mike Farah's YAML processor and DNS queries |
| atuin | Shell history client; installed without account setup or shell integration |
| tmux | Terminal multiplexer — run things in the background |
| htop, lsof, strace | Process monitoring and debugging |
| imagemagick | Image conversion (convert, identify, mogrify) |
| chromium | Headless browser — screenshots and Playwright; /usr/bin/chromium stays the supported wrapper |
| psql, redis-cli, sqlite3, mysql, mysqldump | Database clients; MySQL-compatible commands use Debian's MariaDB client |
| openssh-client, openssh-server, mosh | SSH out, and optional key-only SSH/Mosh access into the container |
AI CLIs — core providers in both variants
| CLI | Command | Version | What it's for |
|-----|---------|---------|---------------|
| Claude Code | claude | 2.1.300 | The main event — you're running inside this |
| Gemini CLI | gemini | 0.64.0 | Google's AI coding agent |
| OpenAI Codex | codex | 0.162.1 | OpenAI's coding agent |
| Cursor | cursor | 2026.09.15-d2fe57e | Cursor's AI agent |
| TaskMaster AI | task-master | 0.43.1 | Task planning and orchestration |
Five AI CLIs ship in both full and slim. The full image adds Junie, OpenCode, and Pi below, for eight AI CLIs total.
Full image only (additional packages)
The full image includes everything above, plus:
Additional AI CLIs
| CLI | Command | Version | What it's for |
|-----|---------|---------|---------------|
| Junie | junie | 3659.4 | JetBrains' AI coding agent |
| OpenCode | opencode | 1.19.0 | Open source AI agent (OpenRouter and other providers) |
| Pi Coding Agent | pi | 0.85.1 | Minimal agent harness (multiple providers) |
Additional npm packages — deployment, ORMs, performance
| Package | What it's for |
|---------|---------------|
| wrangler, @cloudflare/next-on-pages | Cloudflare Workers deployment |
| vercel | Vercel deployment |
| netlify-cli | Netlify deployment |
| az | Azure CLI for cloud deployment and management |
| prisma, drizzle-kit | The two most popular Node.js ORMs |
| pm2 | Production process manager |
| eas-cli | Expo / React Native builds |
| lighthouse, @lhci/cli | Performance auditing (full image only; Chromium is already there) |
| sharp-cli | Image processing CLI |
| json-server, http-server | Mock REST APIs, static file serving |
| @marp-team/marp-cli | Markdown to presentation slides |
Additional Python packages — PDFs, data viz, web frameworks
| Package | What it's for |
|---------|---------------|
| reportlab, weasyprint, cairosvg, fpdf2, PyMuPDF, img2pdf | PDF generation, reading, conversion, and merging libraries. |
| xlsxwriter, xlrd | Excel formats beyond what openpyxl covers |
| matplotlib, seaborn | Data visualization and charts |
| python-pptx | PowerPoint generation |
| fastapi, uvicorn | Python web framework |
Additional system packages — media, documents
| Package | What it's for |
|---------|---------------|
| pandoc | Convert between any document format (markdown, HTML, PDF, docx, epub...) |
| ffmpeg | Video and audio processing (extract, convert, transcode) |
| libvips-dev | High-performance image processing library |
Slim users: Missing a package? Ask Claude. It installs npm/pip packages in seconds. System packages (pandoc, ffmpeg) take 1-2 minutes. You get the same capabilities — the full image just has zero wait time.
:robot: AI CLI Providers
The full image ships eight AI CLIs. The slim image ships the five core CLIs.
| Provider | Command | How to authenticate | Subscription works? |
|----------|---------|--------------------|--------------------|
| Claude Code | claude | CloudCLI web UI (OAuth) | Yes — Max/Pro plan or API key |
| Gemini CLI | gemini | GEMINI_API_KEY env var | API key (pay-per-use) |
| OpenAI Codex | codex | OPENAI_API_KEY or codex login --device-auth | Yes — ChatGPT Plus/Pro/Team/Enterprise or API key |
| Cursor | cursor | CURSOR_API_KEY env var | API key |
| TaskMaster AI | task-master | Uses existing AI provider keys | Works with configured keys |
| Junie | junie | JetBrains AI subscription | JetBrains account required, full image only |
| OpenCode | opencode | Configure via TUI | OpenRouter and other providers, full image only |
| Pi Coding Agent | pi | Configure through Pi | Supports multiple providers, full image only |
Claude Code is the primary CLI. The others are there because sometimes you want a second opinion, or a specific model's strengths, or you're comparing outputs. Having all of them one Tab away is the whole point.
OpenCode is the full-image path for OpenRouter and multi-provider model routing. Configure it inside opencode; free model availability depends on OpenRouter and provider account limits, not HolyClaude.
:mag: Desloppify
Desloppify ships in both images as the desloppify command. It is passive by default. HolyClaude does not run scans, create .desloppify/, edit .gitignore, or change your mounted workspace unless you run Desloppify yourself.
Use it inside a project:
desloppify scan --path .
desloppify next
After you use scans in a project, add .desloppify/ to that project's .gitignore.
Optional global skill setup is controlled by HOLYCLAUDE_DESLOPPIFY_SETUP.
| Value | Behavior |
|-------|----------|
| off | Default. No global skill setup. The CLI is still installed. |
| claude | Runs desloppify setup --interface claude at container start. |
| codex | Runs desloppify setup --interface codex at container start. |
| gemini | Runs desloppify setup --interface gemini at container start. |
| all | Expands to claude,codex,gemini. |
| claude,codex | Any comma-separated subset of claude, codex, and gemini. |
| opencode | Full image only. Sets up the OpenCode-native global skill path. |
OpenCode can also discover Claude-compatible skills from ~/.claude/skills, so all does not include opencode. Do not combine claude and opencode in automatic setup; HolyClaude warns and skips opencode to avoid duplicate skill discovery. If OPENCODE_CONFIG_DIR is set, HolyClaude also skips automatic OpenCode setup because Desloppify writes to the standard ~/.config/opencode path.
Desloppify also supports these manual upstream targets if you want to configure them yourself: cursor, copilot, windsurf, qwen, amp, rovodev, droid, and hermes.
:llama: Using Ollama
HolyClaude works with Ollama as an alternative to an Anthropic subscription. The supported setup path is ANTHROPIC_AUTH_TOKEN=ollama plus ANTHROPIC_BASE_URL=.
See the full setup guide: docs/ollama.md
:building_construction: Architecture
graph TB
subgraph Docker Container
EP["entrypoint.sh"] --> CP["Persist Git and gh config\n(every boot)"]
CP --> BS["bootstrap.sh\n(first boot only)"]
EP --> S6["s6-overlay\n(PID 1)"]
S6 --> CC["CloudCLI\n(:3001)"]
S6 --> XV["Xvfb\n(:99)"]
S6 -. optional .-> SSH["sshd\n(:22)"]
CC --> CLAUDE["Claude Code CLI"]
CLAUDE --> TOOLS["Dev Tools\n(Node, Python, Git...)"]
CLAUDE --> CHROME["Chromium\n(headless)"]
XV -.-> CHROME
end
subgraph Host
DATA["./data/claude"] -.->|bind mount| HOME["~/.claude"]
WS["./workspace"] -.->|bind mount| WORK["/workspace"]
end
USER["Browser"] -->|":3001"| CC
style S6 fill:#2d3748,color:#fff
style CC fill:#6366f1,color:#fff
style CLAUDE fill:#f59e0b,color:#000
How the pieces fit together
- Container starts —
entrypoint.shruns as root for Docker and remaps UID/GID there. In rootless Podman keep-id mode, it skips root-only repairs because the container is already running as the target user. In both paths it restores the saved Claude Code session and prepares persistent Git and GitHub CLI configuration before checking if this is a first boot.
- Every boot — HolyClaude links global Git config, XDG Git config, and GitHub CLI config into the existing
.claudemount. Missing identity values are seeded without replacing manual changes.
- First boot only —
bootstrap.shcopies default settings and the memory template, then creates.holyclaude-bootstrapped. Your customizations are safe from that point on.
- s6-overlay takes over as PID 1 — This isn't supervisord. It's s6-overlay, purpose-built for Docker. Supervises CloudCLI, Xvfb, Claude session sync, and optional
sshdwhen you explicitly enable it. Auto-restarts on crash. Forwards signals. Reaps zombies. Shuts down gracefully.
- CloudCLI serves the web UI — Port 3001. Browser-based interface to Claude Code with project management, multiple sessions, and plugins (project stats + web terminal included).
- Xvfb provides a compatibility display — Xvfb stays available at
:99for tools that use a headed display. Modern headless Chromium, Playwright, and Lighthouse do not universally require Xvfb.
runuser instead of su.
:file_folder: Project Structure
holyclaude/
├── .github/ # CI/CD workflows, issue & PR templates
│ ├── FUNDING.yml # Sponsor/donation links
│ ├── ISSUE_TEMPLATE/ # Bug report, feature request, package request
│ ├── pull_request_template.md
│ ├── SECURITY.md # Security policy
│ └── workflows/ # Docker build & push automation
├── assets/ # Logo and banner images
├── config/ # Claude Code configuration
│ ├── claude-memory-full.md
│ ├── claude-memory-slim.md
│ └── settings.json
├── docs/ # Extended documentation
│ ├── architecture.md
│ ├── CHANGELOG.md
│ ├── configuration.md
│ ├── dockerhub-description.md
│ ├── ollama.md
│ └── troubleshooting.md
├── scripts/ # Container lifecycle scripts
│ ├── bootstrap.sh # First-run setup
│ ├── entrypoint.sh # Container entrypoint
│ └── notify.py # Notification helper (Apprise)
├── s6-overlay/ # Process supervision (s6-rc services)
├── Dockerfile # Multi-stage build
├── docker-compose.yaml # Quick start (minimal config)
├── docker-compose.full.yaml # Full config (all options)
├── docker-compose.podman-rootless.yaml # Rootless Podman keep-id profile
├── LICENSE
└── README.md
:floppy_disk: Data & Persistence
| What | Where (container) | Where (host) | Survives rebuild? |
|------|-------------------|-------------|-------------------|
| Claude settings and persisted tool config | /home/claude/.claude | ./data/claude | Yes |
| Claude Code session (OAuth, onboarding) | /home/claude/.claude.json | ./data/claude/.claude.json.persist | Yes |
| Git global and XDG configuration | /home/claude/.gitconfig, /home/claude/.config/git | ./data/claude/.gitconfig, ./data/claude/.config/git | Yes |
| GitHub CLI configuration and authentication | /home/claude/.config/gh | ./data/claude/.config/gh | Yes |
| Bash aliases | /home/claude/.bash_aliases | ./data/claude/.bash_aliases | Yes |
| Your code and projects | /workspace | ./workspace | Yes |
| CloudCLI account | /home/claude/.cloudcli | (container only by default — see below) | No (opt-in available) |
HolyClaude restores the saved Claude Code session before startup can create a fresh default file. That keeps container rebuilds and recreates from replacing a real OAuth/API session with onboarding state.
What survives docker compose down && docker compose up:
- File-based Anthropic authentication and Claude Code API-key settings
- Claude Code settings, memory (
CLAUDE.md), and OAuth session (no re-login) - All your code in
./workspace - Git global configuration, aliases, and XDG Git settings
- GitHub CLI configuration and authentication
- Bash aliases saved in
~/.bash_aliases - Codex and Gemini file-based config and authentication stored below
/home/claude/.claude(since v1.1.7). Cursor uses.claude/.cursoronly when HolyClaude creates the managed alias; an existing real directory or custom link remains user-managed, so inspect and migrate it before relying on.claudepersistence. Environment-provided keys remain in your Compose or host environment.
What you'll redo (10 seconds):
- CloudCLI web account — quick signup, that's it (unless you opt into persistence below)
Re-triggering first-boot setup:
# Delete the sentinel file — NOT the whole folder
rm ./data/claude/.holyclaude-bootstrapped
docker compose restart holyclaude
Protect./data/claude/. It contains saved Claude Code sessions and may now contain GitHub credentials in.config/gh/hosts.yml. Do not commit it, share it broadly, or place it in an unencrypted backup. Delete only the specific state you intend to reset.
Persisting the CloudCLI account (optional, local storage only)
By default, the CloudCLI account database (~/.cloudcli) is container-local and gets wiped on rebuild. Re-creating the account takes 10 seconds, so most people leave it as-is.
CloudCLI is a single-user service. Remote access shares the same CloudCLI account, /workspace, and mounted credential context; it does not create separate users or tenant isolation.
If you want it to survive rebuilds, add a named Docker volume to your compose file:
services:
holyclaude:
volumes:
- ./data/claude:/home/claude/.claude
- ./workspace:/workspace
- cloudcli-data:/home/claude/.cloudcli # add this line
volumes:
cloudcli-data: # and this block
HolyClaude prepares this directory before CloudCLI starts. Fresh Docker volumes inherit the image's claude:claude ownership. Existing volumes are repaired to the configured PUID/PGID during normal root-starting Docker startup. If the mount is read-only or cannot be repaired, startup stops with the exact path and UID/GID instead of letting CloudCLI repeat unable to open database file.
For rootless Podman, use docker-compose.podman-rootless.yaml. Its keep-id mapping and :Z labels let the existing host user write the mounted state without a privileged ownership repair. :Z handles SELinux labeling; :U rewrites host ownership and is not the default.
Keep CloudCLI's SQLite database off network shares (NAS, SMB/CIFS, NFS). Network filesystem locking can cause database is locked errors. Use a named volume backed by the default local driver without remote mount options, or a local-disk bind mount. A volume name alone does not guarantee local storage; remote drivers and NFS/CIFS options still place the database on a network share.
A bind mount to a local SSD path is fine too, just keep it off any network share.
:lock: Permissions
Claude Code runs in acceptEdits mode by default. This is the shipped setting:
| Action | Allowed? | |--------|----------| | Read files | Yes | | Edit / create files | Yes | | Run shell commands | Depends on Claude Code's current permission prompt | | Install packages | Depends on Claude Code's current permission prompt |
Want full bypass? (power users)
This is how I personally run it. Edit ./data/claude/settings.json on your host:
{
"permissions": {
"defaultMode": "bypassPermissions"
}
}
Bypass mode means Claude executes commands without confirmation. It is powerful, but it can also run destructive commands quickly. Keep the shipped acceptEdits default unless you trust the workspace and every prompt you run.
Codex Permission Modes
HolyClaude also ships configurable near-parity permission modes for Codex, with separate controls for CloudCLI Codex chat and the raw codex CLI.
| Setting | Applies to | Default | When it is read |
|---------|------------|---------|-----------------|
| HOLYCLAUDE_CODEX_CHAT_PERMISSION_MODE | CloudCLI Codex requests that omit permissionMode | acceptEdits fallback | Runtime container config, used only when the request has no explicit value |
| HOLYCLAUDE_CODEX_CLI_PERMISSION_MODE | Raw codex CLI config at ~/.codex/config.toml | default | First boot only, when the file does not already exist |
Valid values for both are default, acceptEdits, and bypassPermissions. acceptEdits is the fallback recommended for trusted self-hosted workspaces. The current CloudCLI browser client sends an explicit permissionMode, and that request value wins. HOLYCLAUDE_CODEX_CHAT_PERMISSION_MODE is only the fallback for direct WebSocket/API clients or custom frontends that omit it. For the raw codex CLI, default is the first-boot default; the setting only seeds a new ~/.codex/config.toml, existing configs are not overwritten, and the generated value persists until you edit that file yourself.
bypassPermissions maps Codex to full access with no approval. Inside Docker, that still runs within the container and mounted volumes, but it can read and change anything reachable through those mounts, especially /workspace and persisted config under /home/claude. Use it only for trusted local workspaces, and don't expose CloudCLI directly to the public internet.
:shield: Remote Access & Expos
... (README truncated for length)