-
Notifications
You must be signed in to change notification settings - Fork 0
Docker: fix fragile setup, adopt proven patterns from HolyClaude #9
Description
Context
Compared our Docker setup against HolyClaude, a battle-tested containerized Claude Code environment. Several of their patterns solve real bugs in our current setup.
Must Fix
1. Node version mismatch
Dockerfile uses node:24, Dockerfile.sandbox uses node:22-bookworm-slim. Standardize on node:24.
2. UID/GID remapping in sandbox entrypoint
The sandbox hardcodes USER gsd (UID 1000). Anyone whose host UID isn't 1000 gets permission errors on the mounted /workspace. Add an entrypoint script that remaps UID/GID via env vars (PUID/PGID), matching HolyClaude's approach.
3. Pre-create critical files before bind-mount
Docker creates missing bind-mount targets as directories. If a user mounts a config file path that doesn't exist yet, it becomes a directory and breaks things. The entrypoint should pre-create known file targets (e.g. config JSON) before they're needed.
4. Disconnected multi-stage Dockerfile
Stage 1 (builder) and Stage 2 (runtime) in the root Dockerfile share nothing — no COPY --from=builder. They're two independent images pretending to be a multi-stage build. Either split into two Dockerfiles or make stage 2 actually consume builder artifacts.
Pure Wins
5. Sentinel-based bootstrap (run first-boot setup once)
Add a sentinel file check in the entrypoint. First boot runs setup (copy default configs, init git identity, etc). Subsequent boots skip it, preserving user customizations.
SENTINEL="$HOME/.gsd/.bootstrapped"
if [ ! -f "$SENTINEL" ]; then
/usr/local/bin/bootstrap.sh
touch "$SENTINEL"
fi6. Two compose files (minimal + full)
Split docker-compose.yml into:
docker-compose.yaml— zero-config, just worksdocker-compose.full.yaml— every option documented inline
7. Proper entrypoint with signal handling
Replace bare ENTRYPOINT ["gsd"] with an entrypoint script that handles UID remapping, bootstrap, and execs into the main process with proper signal forwarding.
Out of Scope
- s6-overlay (overkill unless we add sidecar services)
- Multi-arch CI/CD (separate effort)
- Notification hooks (nice-to-have, not a Docker fix)