fix: make Docker container UID/GID configurable

Add UID and GID build arguments to Dockerfiles to allow matching the
container user to the host user. This fixes file permission issues when
mounting host directories as volumes.

Default remains 1001 for backward compatibility. To match host user:
  UID=$(id -u) GID=$(id -g) docker-compose build

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Tobias Weber
2026-01-12 16:11:57 +01:00
parent 299b838400
commit aa8caeaeb0
6 changed files with 51 additions and 5 deletions

View File

@@ -59,6 +59,11 @@ FROM node:22-slim AS server
ARG GIT_COMMIT_SHA=unknown ARG GIT_COMMIT_SHA=unknown
LABEL automaker.git.commit.sha="${GIT_COMMIT_SHA}" 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) # 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 \ RUN apt-get update && apt-get install -y --no-install-recommends \
git curl bash gosu ca-certificates openssh-client \ 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 RUN npm install -g @anthropic-ai/claude-code
# Create non-root user with home directory BEFORE installing Cursor CLI # Create non-root user with home directory BEFORE installing Cursor CLI
RUN groupadd -g 1001 automaker && \ # Uses UID/GID build args to match host user for mounted volume permissions
useradd -u 1001 -g automaker -m -d /home/automaker -s /bin/bash automaker && \ # 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/.local/bin && \
mkdir -p /home/automaker/.cursor && \ mkdir -p /home/automaker/.cursor && \
chown -R automaker:automaker /home/automaker && \ chown -R automaker:automaker /home/automaker && \

View File

@@ -27,9 +27,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# Install Claude CLI globally # Install Claude CLI globally
RUN npm install -g @anthropic-ai/claude-code RUN npm install -g @anthropic-ai/claude-code
# Create non-root user # Build arguments for user ID matching (allows matching host user for mounted volumes)
RUN groupadd -g 1001 automaker && \ # Override at build time: docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)
useradd -u 1001 -g automaker -m -d /home/automaker -s /bin/bash automaker && \ 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/.local/bin && \
mkdir -p /home/automaker/.cursor && \ mkdir -p /home/automaker/.cursor && \
chown -R automaker:automaker /home/automaker && \ chown -R automaker:automaker /home/automaker && \

View File

@@ -17,6 +17,11 @@ services:
build: build:
context: . context: .
dockerfile: Dockerfile.dev 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 container_name: automaker-dev-server-only
restart: unless-stopped restart: unless-stopped
ports: ports:

View File

@@ -18,6 +18,11 @@ services:
build: build:
context: . context: .
dockerfile: Dockerfile.dev 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 container_name: automaker-dev-server
restart: unless-stopped restart: unless-stopped
ports: ports:
@@ -94,6 +99,9 @@ services:
build: build:
context: . context: .
dockerfile: Dockerfile.dev dockerfile: Dockerfile.dev
args:
UID: ${UID:-1001}
GID: ${GID:-1001}
container_name: automaker-dev-ui container_name: automaker-dev-ui
restart: unless-stopped restart: unless-stopped
ports: ports:

View File

@@ -28,6 +28,11 @@ services:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
target: server 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 container_name: automaker-server
restart: unless-stopped restart: unless-stopped
ports: ports:

View File

@@ -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. **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) ## 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: 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` | | 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` | | 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. | | 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). |