Consolidate Docker volumes into single /app data mount#24
Consolidate Docker volumes into single /app data mount#24bclermont wants to merge 2 commits intonextlevelbuilder:mainfrom
Conversation
Move binary, entrypoint, and migrations out of /app to system paths (/usr/local/bin, /usr/share/goclaw) so /app contains only persistent data. This enables a single Docker volume mount for all state instead of the previous 4-5 separate volumes per deployment mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| environment: | ||
| - GOCLAW_MODE=managed | ||
| - GOCLAW_POSTGRES_DSN=postgres://${POSTGRES_USER:-goclaw}:${POSTGRES_PASSWORD:-goclaw}@postgres:5432/${POSTGRES_DB:-goclaw}?sslmode=disable | ||
| volumes: |
There was a problem hiding this comment.
so, should there be a volume for /app ?
There was a problem hiding this comment.
Yes — the base docker-compose.yml declares goclaw-data:/app, so all overlays (managed, tailscale, etc.) inherit it. The overlays no longer need their own volume declarations since everything lives under /app.
| environment: | ||
| - GOCLAW_TSNET_HOSTNAME=${GOCLAW_TSNET_HOSTNAME:-goclaw-gateway} | ||
| - GOCLAW_TSNET_AUTH_KEY=${GOCLAW_TSNET_AUTH_KEY} | ||
| volumes: |
There was a problem hiding this comment.
should there be a volume for /app ?
There was a problem hiding this comment.
Same as above — the base compose provides goclaw-data:/app which covers /app/tsnet-state too. No separate volume needed.
Dockerfile
Outdated
|
|
||
| # Create data directories (owned by goclaw user) | ||
| # Create data directories (owned by goclaw user) — /app is purely persistent data | ||
| RUN mkdir -p /app/workspace /app/data /app/sessions /app/skills /app/tsnet-state /app/.goclaw \ |
There was a problem hiding this comment.
that should be in the docker-entrypoint.sh, no? to have that set permissions right on any volumes
There was a problem hiding this comment.
Fixed. Moved mkdir -p and chown to docker-entrypoint.sh so it runs at container start on the actual mounted volume. The entrypoint now starts as root, creates directories, sets ownership, then drops to the goclaw user via su-exec before executing the application.
| - goclaw-data:/app/data | ||
| - goclaw-sessions:/app/sessions | ||
| - goclaw-skills:/app/skills | ||
| - goclaw-data:/app |
There was a problem hiding this comment.
that won't overwrite config.json ?
There was a problem hiding this comment.
maybe GOCLAW_CONFIG should be set to something like /etc/config.json and have that readonly volume mapped elsewhere
There was a problem hiding this comment.
Fixed. Config is now bind-mounted to /etc/goclaw/config.json (outside the /app volume), so there's no conflict. The Dockerfile default GOCLAW_CONFIG now points to /etc/goclaw/config.json.
- Move mkdir/chown from Dockerfile to docker-entrypoint.sh so permissions are set correctly on externally-created volumes - Entrypoint runs as root for volume init, then drops to goclaw user via su-exec before executing the application - Move GOCLAW_CONFIG to /etc/goclaw/config.json so the standalone config bind mount doesn't conflict with the /app volume - Add cap_add for CHOWN/SETUID/SETGID (needed by entrypoint init) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
goclaw), entrypoint script, and migrations out of/appto system paths (/usr/local/bin/,/usr/share/goclaw/) so the entire/appdirectory contains only persistent datagoclaw-data:/appmountMotivation
Previously, the binary, entrypoint script, and SQL migration files lived inside
/appalongside data directories (workspace, sessions, skills, etc.). This forced each deployment mode to declare and manage multiple separate Docker volumes:goclaw-data,goclaw-workspace,goclaw-sessions,goclaw-skills(4 volumes)goclaw-data,goclaw-skills,goclaw-workspace(3 volumes)tsnet-state(1 more volume)This made deployments harder to manage, backup, and migrate. By relocating non-data artifacts to standard system paths,
/appbecomes a pure data directory that can be served by a single volume mount, drastically simplifying Docker deployments.Changes
Dockerfile/usr/local/bin/goclaw, migrations →/usr/share/goclaw/migrations/, entrypoint →/usr/local/bin/docker-entrypoint.shdocker-entrypoint.sh/app/goclaw→goclaw(now on$PATH)docker-compose.ymlgoclaw-data:/appvolume, removedgoclaw-workspacedocker-compose.standalone.ymlgoclaw-workspace,goclaw-sessions,goclaw-skillsvolumesdocker-compose.managed.ymlgoclaw-skills,goclaw-workspacevolumes from goclaw servicedocker-compose.tailscale.ymltsnet-statevolume (covered by/appmount)docker-compose.upgrade.ymlNo Go code changes — all env var paths (
GOCLAW_WORKSPACE=/app/workspace,GOCLAW_DATA_DIR=/app/data, etc.) remain the same, they're just subdirectories of the single mounted volume now.Test plan
docker build -t goclaw-test .builds successfullydocker compose -f docker-compose.yml -f docker-compose.managed.yml configvalidatesdocker compose -f docker-compose.yml -f docker-compose.standalone.yml configvalidates:18790/healthgoclaw-datavolume🤖 Generated with Claude Code