feat(project): add Docker Compose with flexible storage and DB modes

Bundle the app + Postgres into a compose stack on top of the existing image.

- app: builds the image, publishes ${APP_PORT:-42776}, reads .env, pins
  STATIC_DIR so SPA serving can't be disabled by an empty value
- db: postgres:14-alpine under the "with-db" profile; toggle it off via
  COMPOSE_PROFILES to point the app at a Postgres on the host instead
  (host.docker.internal), with depends_on required:false so it stays optional

Storage and the DB data dir each default to a named volume but can be bind
mounted to a host folder via FILES_DIR / THUMBS_DIR / IMPORT_DIR / DB_DIR.
Add PUID/PGID (via user:) so bind-mounted folders are writable by the
non-root container.

Run the container as a dedicated non-root user "tanabata" with uid/gid 42776,
reusing the project's signature number (also the default port). Document every
variable in .env.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 11:26:25 +03:00
parent 69650b6464
commit fce71bb946
4 changed files with 175 additions and 15 deletions
+6 -6
View File
@@ -58,18 +58,18 @@ FROM alpine:3.21 AS runtime
RUN apk add --no-cache ffmpeg ca-certificates tzdata
# Run as an unprivileged user.
RUN addgroup -S app && adduser -S -G app -u 10001 app
RUN addgroup -S -g 42776 tanabata && adduser -S -G tanabata -u 42776 tanabata
WORKDIR /app
# The built SPA, served by the Go binary (matches STATIC_DIR below).
COPY --from=frontend --chown=app:app /src/frontend/build /app/static
COPY --from=frontend --chown=tanabata:tanabata /src/frontend/build /app/static
# The server binary.
COPY --from=backend --chown=app:app /out/server /app/server
COPY --from=backend --chown=tanabata:tanabata /out/server /app/server
# Data directories (overridable via FILES_PATH/THUMBS_CACHE_PATH/IMPORT_PATH).
# Created and owned by the app user so a fresh named volume inherits write access.
RUN mkdir -p /data/files /data/thumbs /data/import && chown -R app:app /data
# Created and owned by the tanabata user so a fresh named volume inherits write access.
RUN mkdir -p /data/files /data/thumbs /data/import && chown -R tanabata:tanabata /data
# Non-secret defaults mirroring .env.example. Secrets (JWT_SECRET, ADMIN_PASSWORD,
# DATABASE_URL) are intentionally NOT baked in — pass them at `docker run`.
@@ -81,7 +81,7 @@ ENV LISTEN_ADDR=:42776 \
EXPOSE 42776
VOLUME ["/data"]
USER app
USER tanabata
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget -qO- http://127.0.0.1:42776/health >/dev/null 2>&1 || exit 1