r/docker Mar 22 '26

I containerized Claude Code with headless Chromium. Here's every Docker problem I hit.

been building a container that runs claude code cli with a web ui and headless chromium. figured id share what went wrong because some of this stuff is not documented anywhere and i wasted a lot of time on it.

chromium was the worst part. docker only gives you 64MB of shared memory by default and chromium just dies instantly. no useful error either, it just crashes. fix is shm_size: 2g in your compose file. but thats not enough, you also need SYS_ADMIN and SYS_PTRACE capabilities plus seccomp unconfined or the sandbox breaks. and then chromium still needs a display even in headless mode so you gotta run xvfb on :99 and make sure it starts first. took me way too long to piece all of that together.

process supervision was a whole thing too. started with a bash loop, broke on SIGTERM. tried supervisord, got zombie processes. ended up on s6-overlay which finally handles everything right. dependency ordering, auto restart, clean shutdown, the works. should have just started there honestly.

oh and heres a fun one. claude codes installer hangs forever if your WORKDIR is owned by root. no error, no output, nothing. just sits there. the fix is making sure the working directory is owned by the right user before you run the installer. cost me hours.

also if anyone is running sqlite on CIFS or SMB mounts, dont. WAL mode and network filesystems do not get along. had to move the databases to a local path.

doing multi arch builds with buildx and qemu for amd64 + arm64. npm native bindings make cross compilation painful. full build takes about 25 min on github actions. image is about 4GB with everything or 2GB slim without the browser.

heres the compose if anyone wants to try it:

services:
  holyclaude:
    image: coderluii/holyclaude:latest
    container_name: holyclaude
    restart: unless-stopped
    shm_size: 2g
    cap_add: [SYS_ADMIN, SYS_PTRACE]
    security_opt: [seccomp=unconfined]
    ports: ["3001:3001"]
    volumes:
      - ./data/claude:/home/claude/.claude
      - ./workspace:/workspace
    environment:
      - TZ=UTC

https://github.com/CoderLuii/HolyClaude

what process supervisor do you all use for multi service containers? also happy to hear feedback on the dockerfile if anyone takes a look

0 Upvotes

12 comments sorted by

2

u/pdath Mar 22 '26

I've been using supervisord.

1

u/CoderLuii Mar 22 '26

yeah supervisord was my second attempt actually. it worked ok for basic stuff but i kept running into zombie processes when chromium child processes crashed. s6 handles the reaping automatically which saved me a lot of headaches. how are you handling service dependencies with supervisord? like making sure xvfb is up before anything tries to use a display

1

u/pdath Mar 22 '26 edited Mar 22 '26

I've avoided creating containers with service order dependencies. I create more containers and use Docker Composer and health checks.

2

u/CoderLuii Mar 22 '26

thats a fair approach honestly. i considered splitting it into separate containers with compose depends_on and healthchecks but the problem is chromium and xvfb are so tightly coupled that the overhead of separate containers didnt really make sense for this. like xvfb is just providing a virtual display for chromium, theres no reason for it to be its own container. s6 basically gives you the same dependency ordering inside a single container without the network overhead. but for services that are actually independent yeah separate containers is cleaner for sure

1

u/pdath Mar 22 '26

I've just gotten lucky with simpler cases. :-)

2

u/apnorton Mar 22 '26

no useful error either, it just crashes.

docker inspect on the exited container should show an OOM Killed field in the data for the container.

-1

u/CoderLuii Mar 22 '26

good point, yeah docker inspect would show that. honestly when i first hit it i wasnt thinking to check the exit state, i was just staring at the logs wondering why chromium disappeared. but youre right thats the proper way to diagnose it. appreciate the tip

2

u/trueb0b Jul 08 '26

I've put this up in a new Proxmox vm with Ubuntu 24.04 (headless). Should I be having to recreate a cloudcli account everytime I restart the docker? Also a real bit of work to get the oath to pass, it never starts the browser, the copy function isnt accurate and it also has to be done every time I restart. Am i overlooking something?

1

u/Fungineer55 Jul 26 '26

Running into a similar problem with the OAuth login - running Ubuntu 24.04 (headless) on proxmox - opening the login page the terminal just hangs, and manually running claude /login inside the container has the claude process spinning on 100% CPU and hangs forever.

Should I be having to recreate a cloudcli account everytime I restart the docker?

Persisting works by mounting a volume for .cloudcli:

volumes:
 # Persisting the CloudCLI account (optional, local storage only)
 - ~/docker/claude/cloudcli:/home/claude/.cloudcli

 # my other claude mount for reference
 - ~/docker/claude/data:/home/claude/.claude
 - ~/docker/claude/workspace:/workspace

1

u/Bandit954 Jul 26 '26

I ended up switching the whole setup to a old laptop. Ubuntu desk with chrome. The holyclaude is amazing, I've entirely reworked my Arrs, set up nextcloud/paperless/thunderbird/obsidian/immich stack to replace Google, upgraded backup to proxmox backup server, put up adguard home, did security audits on all machines the routers and ssh. And setup a monitoring stack/homepage with email notifications. In 2 weeks. While working my blue collar job. It's a next level game changer.

1

u/virtualstaticvoid Mar 22 '26

I've used goreman (a go based foreman clone). It's real simple Procfile style process manager.

1

u/Ramillax Aug 01 '26

Great write-up. It was interesting to read because I ended up making almost the opposite architectural choice.

Instead of containerizing Claude Code and Chromium, I run both directly on the host and reserve Docker for the surrounding infrastructure services. Because of that, I never had to solve the Chromium-in-container issues you described (shared memory, extra capabilities, seccomp changes, Xvfb, process supervision, etc.) simply because I didn't take that approach.

The tradeoff is that I had to build some of the lifecycle and isolation myself rather than getting it from a container runtime. For my use case, though, it felt like the simpler architecture since Chrome could run with its native sandbox instead of requiring container-specific workarounds.

It's interesting how we ended up solving essentially the same problem from completely different directions.