diff --git a/Dockerfile b/Dockerfile index c32b1764..a0eaa752 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,6 +59,11 @@ FROM node:22-slim AS server ARG GIT_COMMIT_SHA=unknown LABEL automaker.git.commit.sha="${GIT_COMMIT_SHA}" +# Build arguments for user ID matching (allows matching host user for mounted volumes) +# Override at build time: docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) ... +ARG UID=1001 +ARG GID=1001 + # Install git, curl, bash (for terminal), gosu (for user switching), and GitHub CLI (pinned version, multi-arch) RUN apt-get update && apt-get install -y --no-install-recommends \ git curl bash gosu ca-certificates openssh-client \ @@ -79,8 +84,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN npm install -g @anthropic-ai/claude-code # Create non-root user with home directory BEFORE installing Cursor CLI -RUN groupadd -g 1001 automaker && \ - useradd -u 1001 -g automaker -m -d /home/automaker -s /bin/bash automaker && \ +# Uses UID/GID build args to match host user for mounted volume permissions +# Use -o flag to allow non-unique IDs (GID 1000 may already exist as 'node' group) +RUN groupadd -o -g ${GID} automaker && \ + useradd -o -u ${UID} -g automaker -m -d /home/automaker -s /bin/bash automaker && \ mkdir -p /home/automaker/.local/bin && \ mkdir -p /home/automaker/.cursor && \ chown -R automaker:automaker /home/automaker && \ diff --git a/Dockerfile.dev b/Dockerfile.dev index 87ac6bf6..1acd7742 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -27,9 +27,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Install Claude CLI globally RUN npm install -g @anthropic-ai/claude-code -# Create non-root user -RUN groupadd -g 1001 automaker && \ - useradd -u 1001 -g automaker -m -d /home/automaker -s /bin/bash automaker && \ +# Build arguments for user ID matching (allows matching host user for mounted volumes) +# Override at build time: docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) +ARG UID=1001 +ARG GID=1001 + +# Create non-root user with configurable UID/GID +# Use -o flag to allow non-unique IDs (GID 1000 may already exist as 'node' group) +RUN groupadd -o -g ${GID} automaker && \ + useradd -o -u ${UID} -g automaker -m -d /home/automaker -s /bin/bash automaker && \ mkdir -p /home/automaker/.local/bin && \ mkdir -p /home/automaker/.cursor && \ chown -R automaker:automaker /home/automaker && \ diff --git a/docker-compose.dev-server.yml b/docker-compose.dev-server.yml index 7765dcb5..9de27928 100644 --- a/docker-compose.dev-server.yml +++ b/docker-compose.dev-server.yml @@ -17,6 +17,11 @@ services: build: context: . dockerfile: Dockerfile.dev + args: + # Match container user to host user for mounted volume permissions + # Override with: UID=$(id -u) GID=$(id -g) docker-compose build + UID: ${UID:-1001} + GID: ${GID:-1001} container_name: automaker-dev-server-only restart: unless-stopped ports: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 28ef08da..ff83ea05 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -18,6 +18,11 @@ services: build: context: . dockerfile: Dockerfile.dev + args: + # Match container user to host user for mounted volume permissions + # Override with: UID=$(id -u) GID=$(id -g) docker-compose build + UID: ${UID:-1001} + GID: ${GID:-1001} container_name: automaker-dev-server restart: unless-stopped ports: @@ -94,6 +99,9 @@ services: build: context: . dockerfile: Dockerfile.dev + args: + UID: ${UID:-1001} + GID: ${GID:-1001} container_name: automaker-dev-ui restart: unless-stopped ports: diff --git a/docker-compose.yml b/docker-compose.yml index 227450ad..5e5d3513 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,11 @@ services: context: . dockerfile: Dockerfile target: server + args: + # Match container user to host user for mounted volume permissions + # Override with: UID=$(id -u) GID=$(id -g) docker-compose build + UID: ${UID:-1001} + GID: ${GID:-1001} container_name: automaker-server restart: unless-stopped ports: diff --git a/docs/docker-isolation.md b/docs/docker-isolation.md index eb8fe7e1..df07daf2 100644 --- a/docs/docker-isolation.md +++ b/docs/docker-isolation.md @@ -57,6 +57,20 @@ docker-compose -f docker-compose.yml -f docker-compose.project.yml up -d **Tip**: Use `:ro` (read-only) when possible for extra safety. +### Fixing File Permission Issues + +When mounting host directories, files created by the container may be owned by UID 1001 (the default container user), causing permission mismatches with your host user. To fix this, rebuild the image with your host UID/GID: + +```bash +# Rebuild with your user's UID/GID +UID=$(id -u) GID=$(id -g) docker-compose build + +# Then start normally +docker-compose up -d +``` + +This creates the container user with the same UID/GID as your host user, so files in mounted volumes have correct ownership. + ## CLI Authentication (macOS) On macOS, OAuth tokens are stored in Keychain (Claude) and SQLite (Cursor). Use these scripts to extract and pass them to the container: @@ -117,3 +131,4 @@ volumes: | Can't access web UI | Verify container is running with `docker ps \| grep automaker` | | Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` | | Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. | +| File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). |