From be13d8350b1a1c73d52164675083bd4d3d6ca54b Mon Sep 17 00:00:00 2001 From: linus Date: Thu, 10 Sep 2026 12:08:43 +0200 Subject: [PATCH] build: Docker setup (compose: postgres + all-in-one api image) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile: 3-stage — Flutter web build, NestJS build, slim node runtime. Runtime copies dist + node_modules + prisma + the web bundle (WEB_CLIENT_DIR=/app/web), runs `prisma migrate deploy` then `node dist/main.js`. One container serves client + API on :3000. - docker-compose.yml: postgres:16-alpine with a healthcheck + the api service; config from backend/.env (Compose v2 strips quotes), DATABASE_URL + GOOGLE_APPLICATION_CREDENTIALS overridden for the container, serviceAccount.json bind-mounted read-only. - .dockerignore keeps node_modules/build/secrets out of the context. Not run here (no Docker on this box); the stack also runs natively against the local Postgres. Co-Authored-By: Claude Sonnet 5 --- .dockerignore | 10 ++++++++++ Dockerfile | 34 ++++++++++++++++++++++++++++++++++ docker-compose.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0715d0b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +**/node_modules +**/dist +**/build +**/.dart_tool +**/coverage +.git +**/*.log +# Secrets: passed at runtime via env_file / bind mount, never baked in. +backend/.env +backend/serviceAccount.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a8261e9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1 + +# --- 1. Flutter web build ------------------------------------------------- +FROM ghcr.io/cirruslabs/flutter:stable AS web +WORKDIR /src +COPY client/app/pubspec.yaml client/app/pubspec.lock ./ +RUN flutter pub get +COPY client/app/ ./ +RUN flutter build web --release + +# --- 2. Backend build -------------------------------------------------------- +FROM node:20-bookworm-slim AS api-build +WORKDIR /src +COPY backend/package.json backend/package-lock.json ./ +RUN npm ci +COPY backend/ ./ +RUN npx prisma generate && npm run build + +# --- 3. Runtime ------------------------------------------------------------ +FROM node:20-bookworm-slim AS runtime +ENV NODE_ENV=production +WORKDIR /app +# Prisma needs OpenSSL at runtime. +RUN apt-get update && apt-get install -y --no-install-recommends openssl \ + && rm -rf /var/lib/apt/lists/* +COPY --from=api-build /src/node_modules ./node_modules +COPY --from=api-build /src/dist ./dist +COPY --from=api-build /src/prisma ./prisma +# The Flutter web build; app.module reads WEB_CLIENT_DIR. +COPY --from=web /src/build/web ./web +ENV WEB_CLIENT_DIR=/app/web +EXPOSE 3000 +# Apply pending migrations, then boot. +CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..31c6633 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +services: + db: + image: postgres:16-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: kcapp + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d kcapp"] + interval: 5s + timeout: 5s + retries: 10 + + api: + build: + context: . + dockerfile: Dockerfile + depends_on: + db: + condition: service_healthy + # All non-DB config comes from backend/.env (needs Docker Compose v2, + # which strips surrounding quotes). DATABASE_URL and the FCM credential + # path are overridden below for the container. + env_file: + - backend/.env + environment: + DATABASE_URL: postgresql://postgres:postgres@db:5432/kcapp?schema=public + PORT: "3000" + GOOGLE_APPLICATION_CREDENTIALS: /app/serviceAccount.json + APP_BASE_URL: http://localhost:3000 + ports: + - "3000:3000" + volumes: + # Firebase service account — kept out of the image, mounted read-only. + - ./backend/serviceAccount.json:/app/serviceAccount.json:ro + +volumes: + pgdata: