← Discover MCPs and Agents
H
MCPAI & MLGitHub

HolyClaude

AI coding workstation: Claude Code + web UI + 8 AI CLIs + headless browser + 50+ tools

Links

README

From the repo.

🌍 English | EspaΓ±ol | FranΓ§ais | Italiano | PortuguΓͺs | Deutsch | Русский | ΰ€Ήΰ€Ώΰ€¨ΰ₯ΰ€¦ΰ₯€ | δΈ­ζ–‡ | ζ—₯本θͺž | ν•œκ΅­μ–΄

HolyClaude HolyClaude

HolyClaude Banner

License: MIT Docker Pulls Full Image Slim Image
GitHub Stars Twitter Follow PayPal Buy Me A Coffee Website GitHub Release Issues Contributors

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.

In v1.5.5, Git global configuration and GitHub CLI authentication survive container replacement through the existing ./data/claude mount. Node moves to 26.5.1, GitHub CLI to 2.97.0, npm to 11.19.0, Vite to 8.2.0, Wrangler to 4.116.0, OpenCode to 1.18.10, and Python Markdown to 3.10.3. CloudCLI stays at the verified 1.36.3 baseline with a refreshed production dependency tree. Playwright remains aligned at 1.61.0 for Node and Python, with no runtime browser download.

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.

↑ back to top


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

↑ back to top


: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:

3. Pull and start:

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.

↑ back to top


:computer: Platform Support

PlatformStatusNotes
Linux (amd64)βœ… Fully supportedNative performance, recommended
Linux (arm64)βœ… Fully supportedRaspberry Pi 4+, Oracle Cloud, AWS Graviton
macOS (Docker Desktop)βœ… Fully supportedApple Silicon & Intel via Docker Desktop
Windows (WSL2 + Docker Desktop)βœ… Fully supportedRequires WSL2 backend
Synology / QNAP NASβœ… Fully supportedUse CHOKIDAR_USEPOLLING=true for SMB mounts
KubernetesπŸ”œ Coming soonHelm chart planned

↑ back to top


: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.

HolyClaudeDoing it yourself
Setup30 seconds1-2 hours (if it goes well)
Claude CodePre-installed, pre-configured, readyInstall, configure, debug installer hanging, fix WORKDIR
Web UICloudCLI included with pluginsFind a web UI, install it, configure it, wire it to Claude
Headless browserChromium + Xvfb + Playwright, configuredInstall Chromium, install Xvfb, configure display :99, fix shm, fix sandbox, fix seccomp...
AI CLIs8 providers, one containerInstall each one separately across 3 package managers
Dev tools50+ tools, readyapt-get install / npm i -g / pip install for the next hour
Process managements6-overlay (auto-restart, graceful shutdown)Write your own supervisord config or hope Docker restart works
PersistenceBind-mounted tool config and workspace survive rebuildsFigure out Docker volumes, debug "why is this a directory not a file"
Updatesdocker compose pull && docker compose up -dUpdate 50 tools manually, pray nothing breaks
Multi-archAMD64 + ARM64Pray your Dockerfile builds on ARM

The last row of every manual setup is "works on my machine." HolyClaude works on every machine.

↑ back to top


:credit_card: Subscription & Authentication

HolyClaude runs the official Claude Code CLI from Anthropic. Your existing account works out of the box.

What works:

Authentication methodHowCost
Claude Max/Pro plan (subscription)Sign in through CloudCLI web UI β€” same OAuth flow as desktopYour existing subscription, no extra charge
Anthropic API keyPaste your API key in the web UIPay-per-use, same Anthropic billing

What doesn't work:

Why
OpenAI API key for ClaudeDifferent company, different API. OpenAI keys work with the Codex CLI (also pre-installed)

ChatGPT Plus/Pro subscribers: Your subscription works with the Codex CLI. Run codex login --device-auth inside the container to authenticate with your ChatGPT account. If you need Codex's browser callback flow from your host, expose port 1455 in the full compose template below.

Other AI CLIs included:

CLIWhat you need
Gemini CLIGoogle AI API key (GEMINI_API_KEY)
OpenAI CodexOpenAI API key (OPENAI_API_KEY) or ChatGPT Plus/Pro subscription (codex login --device-auth)
CursorCursor API key (CURSOR_API_KEY)
TaskMaster AIUses your AI provider keys (Anthropic, OpenAI, etc.)
JunieJetBrains account (JetBrains AI subscription)
OpenCodeConfigure via opencode TUI (OpenRouter and other providers)
Pi Coding AgentConfigure 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.

↑ back to top


:package: Image Variants

Two flavors. Same quality. Pick your weight class.

TagWhat you getBest forDocker Hub compressed size
latestEverything pre-installed β€” every tool, every library, every CLIMost users. Zero wait time. Claude never has to stop and install something.~4.1 GB
slimCore tools only β€” Claude installs extras on-demandSmaller VPS, limited disk, metered bandwidth~2.4 GB
X.Y.ZFull image, pinned versionProduction stability β€” you control when to updateSame as latest for that release
X.Y.Z-slimSlim image, pinned versionProduction + small footprintSame 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.

Docker Hub reports compressed transfer size. Docker, Synology Container Manager, and NAS filesystems can show a larger unpacked size after layers are extracted. That is expected; use slim when disk space or bandwidth matters more than having every tool ready on first boot.

↑ back to top


: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 require SYS_ADMIN, SYS_PTRACE, or seccomp=unconfined, and SYS_PTRACE is debugging-related. Keep the web UI bound to 127.0.0.1 unless you put a real private tunnel or access layer in front of it. Treat any hardening pass as a separate change.

Why shm_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/shm heavily for tab rendering. At 64MB, tabs crash randomly. For heavy browser use (many tabs, complex pages), increase to 4GB.

↑ back to top


: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:pass@gmail.com?to=you@gmail.com
      # - 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:

SectionWhat it doesWhen to change it
TimezoneContainer clockAlways β€” set to your local TZ
PerformanceNode.js memory ceilingOnly if you hit OOM errors on large projects
User mappingFile permissions between container and hostDocker users: match id -u and id -g. Rootless Podman users: use the Podman compose file below.
SMB/CIFSFile watcher polling modeOnly if your volumes live on a NAS or network share
NotificationsPush alerts via Apprise (Discord, Telegram, Slack, Email, 100+ services)If you want to walk away and know when your AI agents are done
AI providersAPI keys for Gemini, Codex, Cursor, Junie, OpenCodeIf 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.

↑ back to top


:wrench: Environment Variables

The complete reference. Every variable, what it defaults to, what it does.

VariableDefaultWhat it does
TZUTCContainer timezone
PUID1000Container user ID for Docker-style UID/GID remapping
PGID1000Container group ID for Docker-style UID/GID remapping
NODE_OPTIONSFull Compose: --max-old-space-size=4096Optional 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_NAMEHolyClaude UserGit commit author, seeded only when the persisted global config has no value
GIT_USER_EMAILnoreply@holyclaude.localGit 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_MODEacceptEdits fallbackCloudCLI Codex chat fallback when a request omits permissionMode. The current browser client sends an explicit value. Valid: default, acceptEdits, bypassPermissions
HOLYCLAUDE_CODEX_CLI_PERMISSION_MODEdefaultRaw codex CLI first-boot mode for new ~/.codex/config.toml only. Valid: default, acceptEdits, bypassPermissions
HOLYCLAUDE_DESLOPPIFY_SETUPoffOptional Desloppify global skill setup. Valid: off, all, claude, codex, gemini, opencode, or comma-separated subsets
HOLYCLAUDE_SSH_ENABLEfalseEnables the optional sshd service when a safe authorized_keys mount is present
HOLYCLAUDE_SSH_AUTHORIZED_KEYS/run/holyclaude-ssh/authorized_keysRead-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_keysRoot-owned SSH host key directory; mount /var/lib/holyclaude-ssh to keep fingerprints stable after recreate
HOLYCLAUDE_MOSH_ENABLEfalseAllows mosh-server for SSH sessions when UDP ports are mapped
HOLYCLAUDE_MOSH_UDP_START60000First UDP port Mosh may use
HOLYCLAUDE_MOSH_UDP_END60010Last UDP port Mosh may use

↑ back to top


: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
PackageWhat it's for
typescript, tsxTypeScript compilation and execution
pnpmFast, disk-efficient package manager
vite, esbuildLightning-fast build tools
eslint, prettierCode quality and formatting
serve, nodemonStatic file server, auto-restart dev server
concurrentlyRun multiple scripts in parallel
dotenv-cliLoad env vars from .env files
Python 3 packages
PackageWhat it's for
requests, httpxHTTP clients
beautifulsoup4, lxmlWeb scraping and HTML parsing
PillowImage processing (pre-compiled β€” no waiting)
pandas, numpyData manipulation (pre-compiled β€” seriously, you don't want to pip install these at runtime)
openpyxlRead/write Excel files
python-docxRead/write Word documents
jinja2, markdownTemplating and markdown rendering
pyyaml, python-dotenvConfig file parsing
rich, click, tqdmBeautiful CLIs and progress bars
desloppify, bandit, tree-sitterCode-quality scans, Python security checks, parser-backed code analysis
playwrightBrowser automation (Playwright 1.61.0, baked at build time)
System tools
ToolWhat it's for
git, ghVersion control + GitHub CLI (PRs, issues, releases from the terminal)
ripgrep (rg), fd, fzfBlazing-fast search β€” Claude uses these constantly
bat, tree, jqBetter cat (syntax highlighting), directory trees, JSON processing
curl, wgetHTTP downloads
tmuxTerminal multiplexer β€” run things in the background
htop, lsof, straceProcess monitoring and debugging
imagemagickImage conversion (convert, identify, mogrify)
chromiumHeadless browser β€” screenshots and Playwright; /usr/bin/chromium stays the supported wrapper
psql, redis-cli, sqlite3Talk to databases directly
openssh-client, openssh-server, moshSSH out, and optional key-only SSH/Mosh access into the container
AI CLIs β€” core providers in both variants
CLICommandWhat it's for
Claude CodeclaudeThe main event β€” you're running inside this
Gemini CLIgeminiGoogle's AI coding agent
OpenAI CodexcodexOpenAI's coding agent
CursorcursorCursor's AI agent
TaskMaster AItask-masterTask 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
CLICommandWhat it's for
JuniejunieJetBrains' AI coding agent
OpenCodeopencodeOpen source AI agent (OpenRouter and other providers)
Pi Coding AgentpiMinimal agent harness (multiple providers)
Additional npm packages β€” deployment, ORMs, performance
PackageWhat it's for
wrangler, @cloudflare/next-on-pagesCloudflare Workers deployment
vercelVercel deployment
netlify-cliNetlify deployment; the optional local Go/Rust functions proxy is omitted
azAzure CLI for cloud deployment and management
prisma, drizzle-kitThe two most popular Node.js ORMs
pm2Production process manager
eas-cliExpo / React Native builds
lighthouse, @lhci/cliPerformance auditing (full image only; Chromium is already there)
sharp-cliImage processing CLI
json-server, http-serverMock REST APIs, static file serving
@marp-team/marp-cliMarkdown to presentation slides
Additional Python packages β€” PDFs, data viz, web frameworks
PackageWhat it's for
reportlab, weasyprint, cairosvg, fpdf2, PyMuPDF, img2pdfPDF generation, reading, conversion, and merging libraries.
xlsxwriter, xlrdExcel formats beyond what openpyxl covers
matplotlib, seabornData visualization and charts
python-pptxPowerPoint generation
fastapi, uvicornPython web framework
Additional system packages β€” media, documents
PackageWhat it's for
pandocConvert between any document format (markdown, HTML, PDF, docx, epub...)
ffmpegVideo and audio processing (extract, convert, transcode)
libvips-devHigh-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.

↑ back to top


:robot: AI CLI Providers

The full image ships eight AI CLIs. The slim image ships the five core CLIs.

ProviderCommandHow to authenticateSubscription works?
Claude CodeclaudeCloudCLI web UI (OAuth)Yes β€” Max/Pro plan or API key
Gemini CLIgeminiGEMINI_API_KEY env varAPI key (pay-per-use)
OpenAI CodexcodexOPENAI_API_KEY or codex login --device-authYes β€” ChatGPT Plus/Pro/Team/Enterprise or API key
CursorcursorCURSOR_API_KEY env varAPI key
TaskMaster AItask-masterUses existing AI provider keysWorks with configured keys
JuniejunieJetBrains AI subscriptionJetBrains account required, full image only
OpenCodeopencodeConfigure via TUIOpenRouter and other providers, full image only
Pi Coding AgentpiConfigure through PiSupports 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.

↑ back to top


: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.

ValueBehavior
offDefault. No global skill setup. The CLI is still installed.
claudeRuns desloppify setup --interface claude at container start.
codexRuns desloppify setup --interface codex at container start.
geminiRuns desloppify setup --interface gemini at container start.
allExpands to claude,codex,gemini.
claude,codexAny comma-separated subset of claude, codex, and gemini.
opencodeFull 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.

↑ back to top


: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=<your Ollama endpoint>.

See the full setup guide: docs/ollama.md

↑ back to top


: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

  1. Container starts β€” entrypoint.sh runs 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.

  2. Every boot β€” HolyClaude links global Git config, XDG Git config, and GitHub CLI config into the existing .claude mount. Missing identity values are seeded without replacing manual changes.

  3. First boot only β€” bootstrap.sh copies default settings and the memory template, then creates .holyclaude-bootstrapped. Your customizations are safe from that point on.

  4. 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 sshd when you explicitly enable it. Auto-restarts on crash. Forwards signals. Reaps zombies. Shuts down gracefully.

  5. 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).

  6. Xvfb provides a compatibility display β€” Xvfb stays available at :99 for tools that use a headed display. Modern headless Chromium, Playwright, and Lighthouse do not universally require Xvfb.

See docs/architecture.md for the full technical deep-dive β€” including why we chose s6 over supervisord, why plugins are baked into the image, and why runuser instead of su.

↑ back to top


: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

↑ back to top


:floppy_disk: Data & Persistence

WhatWhere (container)Where (host)Survives rebuild?
Claude settings and persisted tool config/home/claude/.claude./data/claudeYes
Claude Code session (OAuth, onboarding)/home/claude/.claude.json./data/claude/.claude.json.persistYes
Git global and XDG configuration/home/claude/.gitconfig, /home/claude/.config/git./data/claude/.gitconfig, ./data/claude/.config/gitYes
GitHub CLI configuration and authentication/home/claude/.config/gh./data/claude/.config/ghYes
Your code and projects/workspace./workspaceYes
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
  • Codex, Gemini, and Cursor file-based config and authentication stored below /home/claude/.claude (since v1.1.7). 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.

Do NOT bind-mount ./data/cloudcli on a network share (NAS, SMB/CIFS, NFS). CloudCLI stores its account in SQLite, and SQLite's file locking breaks on network mounts. You'll hit database is locked errors constantly. Named volumes live on the Docker engine's local filesystem, which is why this works β€” bind mounts pointing at a NAS will not.

A bind mount to a local SSD path is fine too, just keep it off any network share.

↑ back to top


:lock: Permissions

Claude Code runs in acceptEdits mode by default. This is the shipped setting:

ActionAllowed?
Read filesYes
Edit / create filesYes
Run shell commandsDepends on Claude Code's current permission prompt
Install packagesDepends 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.

SettingApplies toDefaultWhen it is read
HOLYCLAUDE_CODEX_CHAT_PERMISSION_MODECloudCLI Codex requests that omit permissionModeacceptEdits fallbackRuntime container config, used only when the request has no explicit value
HOLYCLAUDE_CODEX_CLI_PERMISSION_MODERaw codex CLI config at ~/.codex/config.tomldefaultFirst 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.

↑ back to top


:shield: Remote Access & Exposure

HolyClaude binds CloudCLI to 127.0.0.1:3001 by default. That keeps the web UI on the Docker host only, which is right for a laptop or a home server you reach over SSH.

The moment you want to reach it from outside your network, read this section.

Don't port-forward it to the public internet

I'll say it flat out: do not punch a hole in your router and expose 3001 to the open internet. Not even with a password. Here's why:

  • CloudCLI exposes a full shell through the web terminal plugin
  • It can run arbitrary code, install packages, and read/write your mounted /workspace
  • It holds your Anthropic OAuth tokens and API keys
  • Basic auth / app-level passwords get brute-forced, credential-stuffed, and scraped out of logs
  • One leaked password = someone else has a paid Claude Code instance running on your box with your money

A password is a speed bump, not a door. Treat HolyClaude like you'd treat an SSH session: never on the open internet without a proper tunnel in front of it.

Use a proper tunnel instead

These are the two options I actually recommend:

OptionWho it's forWhy
TailscalePersonal use, small teamsWireGuard mesh VPN. Install Tailscale on your server + your laptop/phone, and you reach http://holyclaude:3001 from anywhere as if you were on the LAN. No ports opened, no DNS, no certs. Free for personal use.
Cloudflare TunnelSharing with others, public hostnameCloudflare proxies the connection, so port 3001 stays closed. You get a real domain with HTTPS, and you can put Cloudflare Access (Google/GitHub SSO) in front of it. Free tier covers most personal use.

Both avoid forwarding HolyClaude's application port through your router. Tailscale provides encrypted mesh transport and identity controls. Cloudflare Tunnel provides encrypted transport to Cloudflare's edge; configure Cloudflare Access separately when you need identity-based access controls and Access audit logs.

Tailscale subpath mounts

If you mount HolyClaude below a path, set the same path inside the container:

environment:
  - HOLYCLAUDE_BASE_PATH=/holyclaude

Then publish that path with Tailscale Serve:

sudo tailscale serve --bg --https=443 --set-path=/holyclaude http://127.0.0.1:3001

Use no trailing slash in HOLYCLAUDE_BASE_PATH. If you serve HolyClaude at the hostname root, leave this unset.

Optional SSH and Mosh

HolyClaude also ships sshd and Mosh for people who want a direct terminal from a phone, laptop, NAS, or VPN connection. It is off by default. Nothing listens on port 22 unless you enable it.

This is a remote shell into the same container that holds your /workspace and Claude state. Treat it like SSH to your dev box:

  • key-only login as claude
  • no password auth
  • no root login
  • no authorized_keys under .claude or /workspace
  • no public internet port forwarding

Safe setup shape:

services:
  holyclaude:
    ports:
      - "127.0.0.1:2222:22"
      - "127.0.0.1:60000-60010:60000-60010/udp"
    volumes:
      - ./data/ssh/authorized_keys:/run/holyclaude-ssh/authorized_keys:ro
      - holyclaude-ssh:/var/lib/holyclaude-ssh
    environment:
      - HOLYCLAUDE_SSH_ENABLE=true
      - HOLYCLAUDE_MOSH_ENABLE=true

volumes:
  holyclaude-ssh:

Prepare the public key file on the host:

mkdir -p data/ssh
cp ~/.ssh/id_ed25519.pub data/ssh/authorized_keys

Connect over SSH:

ssh -p 2222 claude@127.0.0.1

Connect over Mosh when UDP is reachable:

mosh --ssh="ssh -p 2222" -p 60000:60010 claude@127.0.0.1

Bind those ports to localhost, a VPN interface, or a firewall-restricted address. If you change 127.0.0.1 to 0.0.0.0, you are taking responsibility for that exposure.

If you insist on exposing it directly (please don't)

If you absolutely have to skip the tunnel (self-hosting tutorial, isolated lab network, whatever), at the very minimum:

  1. Put a reverse proxy in front of it (Caddy, nginx, Traefik) with real TLS
  2. Add IP allowlisting at the firewall or proxy level β€” only your known IPs
  3. Keep the shipped acceptEdits default unless you have a clear reason to use bypassPermissions
  4. Rotate your Anthropic credentials if anything looks off
  5. Run behind Cloudflare Access or similar SSO, not basic auth

Even with all that, the risk surface is huge. Use Tailscale or Cloudflare Tunnel. They're free, they take five minutes to set up, and they're what I personally use.

↑ back to top


:bell: Notifications

Walk away from your computer and know when a task is done. Claude Code hooks, raw CLI hooks for Codex and Gemini CLI, and CloudCLI Codex chat completion/failure events use the same Apprise setup. Apprise supports 100+ services including Discord, Telegram, Slack, Email, Pushover, Gotify, and more.

To enable:

  1. Add one or more NOTIFY_* variables to your compose environment:
    - NOTIFY_DISCORD=discord://webhook_id/webhook_token
    - NOTIFY_TELEGRAM=tgram://bot_token/chat_id
    
  2. Inside the container: touch ~/.claude/notify-on

Telegram uses Apprise's tgram:// scheme. Legacy Telegram values are still accepted, but new setups should use tgram://.

Test the setup without sending a message:

docker compose exec holyclaude /usr/local/bin/notify.py test --dry-run --debug

See configuration docs for all supported variables and URL formats.

To disable: rm ~/.claude/notify-on

Events that trigger notifications:

EventWhat happened
stopClaude Code hooks, raw CLI hooks for Codex and Gemini CLI, or a CloudCLI Codex chat run finished
errorA Claude Code hook, raw CLI hook, or CloudCLI Codex chat run failed

Completely silent when not configured. No NOTIFY_* vars set? No flag file? Zero network calls. Zero log spam. Zero overhead.

↑ back to top


:arrows_counterclockwise: Upgrading

Upgrading from a version before v1.5.5? If the current container holds git config --global or gh auth state that is not already in ./data/claude, migrate it before recreating the container. Follow Git and GitHub CLI recovery. Removing the old container first can discard that state.

# Pull the latest image
docker compose pull

# Recreate the container with the new image
docker compose up -d

Your data persists in ./data/claude and ./workspace β€” upgrading only replaces the container, not your files.

Do not update CloudCLI inside the container with cloudcli update or npm install -g @cloudcli-ai/cloudcli@latest. HolyClaude ships a patched CloudCLI runtime, so the supported update path is the Docker image:

docker compose pull && docker compose up -d

To pin a specific version instead of latest:

image: coderluii/holyclaude:1.5.5   # instead of :latest

↑ back to top


:construction: Troubleshooting

CloudCLI shows wrong default directory

CloudCLI opens to /home/claude instead of /workspace.

Cause: A custom or modified CloudCLI service script did not set WORKSPACES_ROOT=/workspace before launching CloudCLI.

Fix: Already handled in HolyClaude. The s6 run script uses with-contenv, exports WORKSPACES_ROOT=/workspace, then starts CloudCLI as the claude user.

CloudCLI says "Failed to browse filesystem"

CloudCLI cannot open ~, /workspace, or a broad NAS mount such as /volume2/docker:/workspace.

Cause: HolyClaude 1.3.3 could remove CloudCLI's expandWorkspacePath helper while disabling CloudCLI's in-container self-update path.

Fix: Update to HolyClaude 1.3.4 or newer:

docker compose pull && docker compose up -d

Broad NAS mounts work, but /workspace is the CloudCLI boundary. Anything readable under that mount is visible to authenticated CloudCLI users. Use a narrower project mount when you do not want the whole NAS folder tree exposed inside CloudCLI.

CloudCLI Web Terminal shows black squares or missing characters

The Web Terminal output is hard to read. Box drawing, emoji, CJK text, or CLI banners may show as black squares or broken characters.

Cause: Older images built the CloudCLI Web Terminal plugin with string-based PTY output handling and a narrow xterm.js font stack. If a multibyte character was split between PTY chunks, or if WebGL picked a weak fallback font, terminal output could render as replacement blocks.

Fix: Update to HolyClaude 1.3.5 or newer:

docker compose pull && docker compose up -d

HolyClaude now patches the baked Web Terminal plugin before it is built. If your browser still shows black squares after updating, open browser DevTools on the CloudCLI page, run this once, then reopen the Web Terminal:

localStorage.setItem('web-terminal-disable-webgl', 'true')
SQLite "database is locked"

Cause: SQLite databases on SMB/CIFS network mounts. CIFS doesn't support the file-level locking SQLite requires.

Fix: Don't store SQLite databases on network shares. HolyClaude keeps .cloudcli in container-local storage for exactly this reason. If you have your own SQLite databases in /workspace on a NAS, move them to a local path.

Chromium crashes / blank pages / tab failures

Cause: Usually insufficient shared memory. Docker defaults to 64MB. If the process exits immediately with SIGTRAP or exit 133, treat that as a separate browser/runtime failure instead of a shared-memory symptom.

Fix: Ensure shm_size: 2g in your compose file. For heavy browser use (many tabs, complex pages), increase to 4g. If you still get an immediate SIGTRAP, re-check the browser build path before only raising shm.

File watchers not detecting changes (hot reload broken)

Cause: SMB/CIFS network mounts don't support inotify.

Fix: Enable polling in your compose environment:

- CHOKIDAR_USEPOLLING=1
- WATCHFILES_FORCE_POLLING=true

Note: Polling uses more CPU than inotify. Only enable on network mounts.

Permission denied errors

Cause: Container UID/GID doesn't match host file ownership.

Fix for Docker:

# On your host machine
id -u  # β†’ this is your PUID
id -g  # β†’ this is your PGID

Set them in your compose file:

- PUID=1000
- PGID=1000

Rootless Podman on SELinux: PUID/PGID changes the container user, but rootless Podman still maps container IDs through your subordinate UID/GID range unless you use keep-id. Use the Podman profile instead:

mkdir -p data/claude data/cloudcli workspace
podman compose -f docker-compose.podman-rootless.yaml up -d

That file uses userns_mode: "keep-id:uid=1000,gid=1000" and :Z labels. Do not add :U to /workspace; it rewrites host ownership and can make host editing worse.

Docker creates .claude.json as a directory

Cause: If a bind-mount target file doesn't exist before container start, Docker helpfully creates it as a directory. Thanks, Docker.

Fix: Already handled β€” entrypoint.sh restores the saved session file first, or creates a safe default file when no saved session exists.

See docs/troubleshooting.md for the complete guide including all SMB/CIFS gotchas and the full history of bugs we encountered and fixed.

↑ back to top


:warning: Known Issues

These are not HolyClaude bugs β€” they're upstream issues or intentional trade-offs.

IssueWhyWorkaround
"Continue in Shell" button brokenCloudCLI upstream bug (race condition in terminal init)Use the Web Terminal plugin instead (pre-installed)
Cursor CLI "Command timeout"No API key configured β€” cosmetic only, doesn't affect anythingSet CURSOR_API_KEY or ignore
CloudCLI account lost on rebuild.cloudcli is container-local by defaultAdd the documented local named volume, or re-create the account (~10 seconds)
Web push notifications "not supported"Browser limitation in CloudCLI, standard behaviorUse Apprise notifications instead (see Notifications)

↑ back to top


:hammer_and_wrench: Building Locally

Want to build the image yourself instead of pulling from Docker Hub? Go for it:

git clone https://github.com/CoderLuii/HolyClaude.git
cd holyclaude

# Build full image
docker build -t holyclaude .

# Build slim image
docker build --build-arg VARIANT=slim -t holyclaude:slim .

# Build for ARM (Apple Silicon, Raspberry Pi, AWS Graviton)
docker buildx build --platform linux/arm64 -t holyclaude .

This source release vendors the patched CloudCLI package under vendor/artifacts/, so docker build installs that bundled @cloudcli-ai/cloudcli tarball instead of downloading a moved package from npm.

Then use image: holyclaude instead of image: coderluii/holyclaude:latest in your compose file.

↑ back to top


:bar_chart: Alternatives

How does HolyClaude compare to other approaches?

ApproachWeb UIMulti-AIPre-configured toolsHeadless browserOne command setupPersistence
HolyClaudeCloudCLI8 CLIs50+ toolsChromium + Xvfb + Playwrightdocker compose upBind mounts
Claude Code (bare metal)NoNoInstall yourselfInstall yourselfMulti-step installManual
Claude Code + oh-my-openagentNoYes (multi-model)SomeNonpm installManual
DIY Docker + Claude CodeMaybeMaybeWhatever you addIf you configure itIf you write the DockerfileIf you set up volumes
Cursor IDEBuilt-inCursor onlyIDE-bundledNoDownload appApp data

HolyClaude isn't competing with coding agents β€” it's the infrastructure layer that makes them all work better. It's the container you run them inside.

↑ back to top


:rocket: Roadmap

What's coming next:

StatusFeature
πŸ”œARM-native builds β€” optimized native ARM64 images, not just emulated
πŸ”œVS Code tunnel integration β€” built-in VS Code Server or tunnel for connecting from VS Code desktop
πŸ”œNotification routing β€” different notification destinations per event type (errors to Telegram, completions to Discord)

Have an idea? Start a discussion or request a feature.

↑ back to top


:trophy: Built with HolyClaude

Using HolyClaude to build something? We'd love to see it.

Open an issue with the showcase label or submit a PR to add your project here:

Be the first to add your project here.

↑ back to top


:handshake: Contributing

Contributions welcome. This project was born from real daily usage, and it gets better when more people use it and find edge cases.

  1. Fork it
  2. Branch it (git checkout -b feature/something)
  3. Commit it
  4. Push it
  5. PR it

Bugs, feature requests, questions: open an issue.

Get in touch

ChannelUse for
GitHub DiscussionsQuestions, show your setup, ideas
IssuesBug reports, feature & package requests
Security AdvisoriesVulnerability reports (private)

Want a tool added?

Use the πŸ“¦ Package Request issue template. Include the package name, install method, and which variant (full/slim) it should target.

↑ back to top


:heart: Support

HolyClaude is free, open source, and maintained by one developer who uses it every day.

If it saved you time, here's how you can help:

  • Star this repo β€” it's the single biggest thing you can do for visibility
  • Share it β€” tell a friend, post it, tweet it
  • Open issues β€” bug reports and feature requests make HolyClaude better for everyone
  • Contribute β€” PRs are always welcome

PayPal Buy Me A Coffee

↑ back to top


:scroll: Third-Party Software

The HolyClaude Docker image includes third-party software, each under its own license. Notable components:

ComponentLicenseSource
CloudCLIAGPL-3.0-or-laterCloudCLI, npm package
DesloppifyOSNL-0.2peteromallet/desloppify
s6-overlayISCjust-containers/s6-overlay
Node.jsMITnodejs/node

See THIRD-PARTY-NOTICES for full details including modification notices. HolyClaude's own source code is MIT licensed.

↑ back to top


:page_facing_up: License

MIT β€” see LICENSE. Use it however you want.

↑ back to top



Built by CoderLuii Β· holyclaude.coderluii.dev

This container is what I use every day. If it saves you even half the setup time it saved me, a star would be nice.

Collected info

  • β˜… 2,471 stars
  • βŽ‡ 260 forks
  • Language: JavaScript
  • Source updated: 8/4/2026

Config for your environment

Replace {MCP_ENDPOINT_URL} with this MCP’s endpoint URL (from its repo or docs above). No API key β€” you connect directly.

Tool

OS

Config file: ~/.cursor/mcp.json

{
  "mcpServers": {
    "mcp-server": {
      "url": "{MCP_ENDPOINT_URL}"
    }
  }
}

Paste into mcpServers in the config file. Restart Cursor after saving.

If this MCP is also published on mcpchannel.ai, you can subscribe from Browse and use the gateway config there instead.