Files
bricktrack/Dockerfile
T
unknownandClaude Opus 5 61b1249afd build: dockerfile, compose-stack, entrypoint und update-skript
Multi-Stage-Build mit standalone-Output, non-root Nutzer node (UID 1000)
und Healthcheck auf /api/health. Der Entrypoint prueft zuerst, ob /data
beschreibbar ist, und sagt bei einem Rechteproblem genau, was zu tun
ist; danach laufen die Migrationen und erst dann startet die App.

Der Migrations-Runner wird beim Build mit esbuild zu einer einzelnen
CJS-Datei gebuendelt. Damit braucht das Runtime-Image weder drizzle-kit
noch tsx - nur das native better-sqlite3, das ohnehin dabei ist.

update.sh sichert data und .env, holt den neuen Stand, baut, startet und
wartet auf den Healthcheck. Wird der Container nicht gesund, rollt es
Code und Container auf den vorherigen Commit zurueck und nennt den Pfad
der Sicherung.

.gitattributes erzwingt LF: eine entrypoint.sh mit CRLF scheitert im
Container mit "no such file or directory" auf der Shebang-Zeile.

Verifiziert: Image gebaut (358 MB), Container healthy, Migration laeuft,
Registrierung des ersten Nutzers funktioniert, sharp arbeitet unter musl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:09:03 +02:00

87 lines
3.1 KiB
Docker

# syntax=docker/dockerfile:1
# ---------------------------------------------------------------------------
# Bricktrack - Multi-Stage-Build
#
# Ergebnis ist ein schlankes Runtime-Image mit Next.js im Standalone-Modus,
# das als Nutzer "node" laeuft. Alle Daten liegen ausserhalb des Images unter
# /data, damit ein Update nichts anfasst, was dir gehoert.
# ---------------------------------------------------------------------------
FROM node:24-alpine AS base
ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
ENV NEXT_TELEMETRY_DISABLED=1
RUN corepack enable
# --- Abhaengigkeiten -------------------------------------------------------
# better-sqlite3 und sharp bringen Prebuilds mit; die Build-Werkzeuge sind der
# Rueckfallweg, falls fuer die Plattform keines existiert.
FROM base AS deps
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
# --- Build -----------------------------------------------------------------
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Platzhalter nur fuer den Build: src/env.ts validiert beim Import, und die
# echten Werte kommen zur Laufzeit aus der .env. Keiner dieser Werte landet im
# Image-Zustand, weil keine Seite vorgerendert wird.
ENV NODE_ENV=production
ENV BASE_URL=http://localhost:3000
ENV BETTER_AUTH_SECRET=build-platzhalter-0123456789-abcdefghij
ENV DATA_DIR=/tmp/build-data
RUN pnpm build
# Der Migrations-Runner wird zu einer einzelnen Datei gebuendelt. So braucht
# das Runtime-Image kein drizzle-kit und kein tsx - nur das native
# better-sqlite3, das ohnehin dabei ist. Das Buendel ist CommonJS, weil
# mitgebuendelte CJS-Abhaengigkeiten sonst am require() der Node-Builtins
# scheitern.
RUN pnpm exec esbuild scripts/migrate.ts \
--bundle \
--platform=node \
--format=cjs \
--target=node22 \
--external:better-sqlite3 \
--outfile=dist/migrate.cjs
# --- Laufzeit --------------------------------------------------------------
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
ENV DATA_DIR=/data
ENV MIGRATIONS_DIR=/app/drizzle
# Standalone bringt seine eigenen node_modules mit - kein pnpm install noetig.
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
COPY --from=builder --chown=node:node /app/public ./public
COPY --from=builder --chown=node:node /app/drizzle ./drizzle
COPY --from=builder --chown=node:node /app/dist/migrate.cjs ./migrate.cjs
COPY --chown=node:node docker/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Anlaufstelle fuer Datenbank und Uploads. Wird per Bind-Mount ueberdeckt;
# das Anlegen hier sorgt nur dafuer, dass der Pfad auch ohne Mount existiert.
RUN mkdir -p /data/db /data/files && chown -R node:node /data
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD wget -q --spider http://127.0.0.1:3000/api/health || exit 1
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["node", "server.js"]