fix(docker): make quick-start compose run as non-root against a writable /data#141
Merged
Merged
Conversation
…ble /data The documented `docker compose -f docker/docker-compose.yml up -d` quick-start crash-looped on first start. The image runs as non-root uid 1654 (app), but Docker creates the named volume's `/data` mount point as root:root 0755 (there is no `/data` in the image to inherit ownership from). The app cannot create the SQLite database there, so DatabaseInitializer/SchemaMigrator fails with `SQLite Error 14: 'unable to open database file'` and the container exits. Fix, compose-only (no image rebuild required): - Add a one-shot `init-data` service that runs as root (`user: "0:0"`) and chowns `/data` to 1654:1654 before the server starts. The server now gates on it via `depends_on: condition: service_completed_successfully`. The app itself still runs as non-root uid 1654, and data persists in the named volume. - Point the image at `ghcr.io/netclaw-dev/skillserver:0.4.0` instead of the local-only `skillserver:latest` tag, so a fresh clone can actually pull and `docker compose up` out of the box. - Set an explicit project `name: skillserver` so the stack no longer inherits the ambiguous `docker` project name from its parent directory. Verified from a clean state: reaches healthy, runs as uid 1654 (non-root), and the SQLite DB survives a `down` (without -v) + `up` restart.
Signed-off-by: Aaron Stannard <[email protected]>
Aaronontheweb
enabled auto-merge (squash)
July 22, 2026 02:13
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the Docker quick-start, which crash-looped on first start (issue #140).
docker/docker-compose.ymlmounts a named volume at/data, but the published image runs as non-rootuid=1654 (app)(.NET SDK container publish; there is no Dockerfile). Docker creates the named volume's/datamount point asroot:root 0755— there is no/datain the image for it to inherit ownership from — so the app cannot create the SQLite database.DatabaseInitializer/SchemaMigratorfails withSQLite Error 14: 'unable to open database file'and the container exits.Root cause
uid=1654 (app)(Entrypoint=["dotnet","/app/SkillServer.dll"],User=1654).root:rootbecause/datadoes not exist in the image.Directory.CreateDirectory/ SQLite open inDatabaseInitializercan't write inside a root-owned directory → the documenteddocker compose ... uppath fails out of the box.Fix (compose-only — no image rebuild required)
init-dataone-shot service: runs as root (user: "0:0"),chown -R 1654:1654 /data, then exits. It uses the same image (Ubuntu 24.04 base, shipssh+chown).skill-servergates on it viadepends_on: { init-data: { condition: service_completed_successfully } }. The server itself still runs as non-root uid 1654, and data persists in the named volume.skillserver:latesttoghcr.io/netclaw-dev/skillserver:0.4.0, so a fresh clone can actuallydocker compose upand pull the image. (Pinned for a reproducible quick-start; happy to floatlatestif preferred.)name: skillserverso the stack no longer inherits the ambiguousdockerproject name from its parent directory.The README quick-start command is unchanged:
docker compose -f docker/docker-compose.yml up -d.Verification
From a clean state (
docker compose -f docker/docker-compose.yml down -v), againstghcr.io/netclaw-dev/skillserver:0.4.0:up -d→ init runs and exits 0, server starts:Health check:
Still non-root (uid 1654),
/datanow writable, DB created:Logs — migrations apply cleanly, no SQLite/permission exception:
Persistence across restart —
down(WITHOUT-v) thenup -d; the SQLite DB is byte-identical (same inode + sha256) and migrations report "already applied":Closes #140