Skip to content

Commit e89d260

Browse files
committed
Fix media directory permission denied on bind mounts
The container runs as the non-root "chronicle" user, but when /app/data is a bind mount the host directory ownership (root:root) takes precedence over the Dockerfile's chown. Add a docker-entrypoint.sh that runs as root to fix ownership of /app/data, then drops to the chronicle user via su-exec before starting the server. https://claude.ai/code/session_0153nX7vQSjEFPpZTgZdDmu5
1 parent 6de4ab0 commit e89d260

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

Dockerfile

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /chronicle ./cmd/server
4242
# --- Stage 3: Runtime ---
4343
FROM alpine:3.20
4444

45-
# Install CA certificates for HTTPS calls (if needed) and timezone data.
46-
RUN apk add --no-cache ca-certificates tzdata
45+
# Install CA certificates for HTTPS calls (if needed), timezone data, and
46+
# su-exec for dropping privileges in the entrypoint.
47+
RUN apk add --no-cache ca-certificates tzdata su-exec
4748

4849
# Create non-root user for runtime security.
4950
RUN adduser -D -H -s /sbin/nologin chronicle
@@ -62,10 +63,16 @@ COPY --from=builder /src/db/migrations /app/db/migrations
6263
# Mount a volume at /app/data to persist media across container rebuilds.
6364
RUN mkdir -p /app/data/media && chown -R chronicle:chronicle /app/data
6465

66+
# Copy entrypoint script that fixes bind-mount permissions before dropping to
67+
# the non-root chronicle user.
68+
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
69+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
70+
6571
WORKDIR /app
6672

67-
# Run as non-root user.
68-
USER chronicle
73+
# Container starts as root so the entrypoint can fix bind-mount ownership,
74+
# then drops to the chronicle user via su-exec.
75+
ENTRYPOINT ["docker-entrypoint.sh"]
6976

7077
# The Go binary serves HTTP directly on this port.
7178
EXPOSE 8080

docker-entrypoint.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
# docker-entrypoint.sh -- fix bind-mount permissions, then drop to non-root.
3+
#
4+
# When /app/data is a bind mount, the host directory's ownership may not match
5+
# the container's "chronicle" user. This script runs as root to ensure the data
6+
# directory is writable, then exec's the server as the unprivileged user.
7+
8+
set -e
9+
10+
# Ensure the media subdirectory exists and is owned by chronicle.
11+
mkdir -p /app/data/media
12+
chown -R chronicle:chronicle /app/data
13+
14+
# Drop privileges and exec the main process.
15+
exec su-exec chronicle "$@"

0 commit comments

Comments
 (0)