Files
family-planner/Dockerfile

89 lines
3.9 KiB
Docker
Raw Normal View History

# ─────────────────────────────────────────────────────────────────────────────
# Stage 1 — Build client
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS client-builder
WORKDIR /build
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy workspace manifests first for better layer caching
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* ./
COPY apps/client/package.json ./apps/client/
COPY apps/server/package.json ./apps/server/
COPY tsconfig.base.json ./
# Install all workspace deps
RUN pnpm install --frozen-lockfile
# Copy client source and build
COPY apps/client ./apps/client/
RUN pnpm --filter client build
# ─────────────────────────────────────────────────────────────────────────────
# Stage 2 — Build server
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS server-builder
WORKDIR /build
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* ./
COPY apps/server/package.json ./apps/server/
COPY apps/client/package.json ./apps/client/
COPY tsconfig.base.json ./
# Install only server production deps + rebuild native modules for target arch
RUN pnpm install --frozen-lockfile
COPY apps/server ./apps/server/
RUN pnpm --filter server build
# ─────────────────────────────────────────────────────────────────────────────
# Stage 3 — Production runtime
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS runtime
# Install tini for proper PID 1 signal handling
RUN apk add --no-cache tini su-exec
WORKDIR /app
# No native deps — runtime only needs node_modules for pure-JS packages (express, cors, etc.)
# Copy them directly from the builder stage instead of re-installing
COPY --from=server-builder /build/node_modules ./node_modules
COPY --from=server-builder /build/apps/server/node_modules ./apps/server/node_modules 2>/dev/null || true
# Copy compiled server output (includes dist/db/migrations/*.js compiled by tsc)
COPY --from=server-builder /build/apps/server/dist ./apps/server/dist
# Copy built client into the path the server expects
COPY --from=client-builder /build/apps/client/dist ./apps/client/dist
# ── Runtime configuration ─────────────────────────────────────────────────
ENV NODE_ENV=production \
PORT=3001 \
DATA_DIR=/data \
PHOTOS_DIR=/photos \
PUID=99 \
PGID=100 \
TZ=UTC \
NODE_NO_WARNINGS=1
# /data — persistent: SQLite database
# /photos — bind-mount: read-only user photo library
VOLUME ["/data"]
EXPOSE 3001
# Entrypoint: fix ownership then drop to PUID/PGID
COPY docker-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]
CMD ["node", "apps/server/dist/index.js"]