From b5ac041c32f1becd8592a0802a1316a8feb47527 Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 11 Sep 2026 15:28:01 +0200 Subject: [PATCH] feat: add Docker Compose deployment for backend/frontend/postgres Backend Dockerfile runs drizzle-kit migrate on start, frontend serves the built Vite app via nginx and proxies /api to the backend container. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A --- .gitignore | 3 +++ backend/.dockerignore | 4 ++++ backend/Dockerfile | 10 ++++++++++ docker-compose.yml | 39 +++++++++++++++++++++++++++++++++++++++ docs/deployment.md | 34 ++++++++++++++++++++++++++++++++++ frontend/.dockerignore | 4 ++++ frontend/Dockerfile | 11 +++++++++++ frontend/nginx.conf | 18 ++++++++++++++++++ 8 files changed, 123 insertions(+) create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 docs/deployment.md create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/.gitignore b/.gitignore index 1734ab4..c8033a0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ synapse/data/ synapse/pgdata/ !synapse/homeserver.yaml.sample +# talktime app stack runtime data (docker-compose.yml at repo root) +/pgdata/ + # misc .DS_Store *.pem diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..b1640b5 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +.env.local diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..3ea76af --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,10 @@ +FROM node:22-alpine +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +RUN npm run build + +ENV NODE_ENV=production +EXPOSE 4000 +CMD ["sh", "-c", "npx drizzle-kit migrate && node dist/server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b4cc716 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_USER: talktime + POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} + POSTGRES_DB: talktime + volumes: + - ./pgdata:/var/lib/postgresql/data + + backend: + build: ./backend + restart: unless-stopped + depends_on: + - db + environment: + DATABASE_URL: postgresql://talktime:${DB_PASSWORD:-changeme}@db:5432/talktime + JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set} + PORT: 4000 + APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8080} + SYNAPSE_BASE_URL: ${SYNAPSE_BASE_URL} + SYNAPSE_ADMIN_TOKEN: ${SYNAPSE_ADMIN_TOKEN} + SYNAPSE_SERVER_NAME: ${SYNAPSE_SERVER_NAME} + SMTP_HOST: ${SMTP_HOST:-} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_USER: ${SMTP_USER:-} + SMTP_PASSWORD: ${SMTP_PASSWORD:-} + MAIL_FROM: ${MAIL_FROM:-TalkTime } + ports: + - "4000:4000" + + frontend: + build: ./frontend + restart: unless-stopped + depends_on: + - backend + ports: + - "8080:80" diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..eebceb8 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,34 @@ +# Deployment + +## Lokal / Server mit Docker Compose + +Voraussetzung: Synapse läuft bereits (siehe `synapse/README.md`), Admin-Token liegt vor. + +```bash +export DB_PASSWORD= +export JWT_SECRET= +export APP_BASE_URL=http://localhost:8080 +export SYNAPSE_BASE_URL=http://:8008 +export SYNAPSE_ADMIN_TOKEN= +export SYNAPSE_SERVER_NAME=chat.talktime.local + +docker compose up -d --build +``` + +Startet: +- `db` — PostgreSQL 16, Daten in `./pgdata` +- `backend` — Fastify-API, migriert die DB beim Start automatisch (`drizzle-kit migrate`), Port 4000 +- `frontend` — gebautes Vite-Frontend hinter nginx, proxied `/api/*` zu `backend:4000`, Port 8080 + +App danach unter `http://localhost:8080` erreichbar. + +## Produktivbetrieb + +- Reverse Proxy (nginx/Caddy) terminiert TLS vor Port 8080 (frontend) und ggf. Synapse (8008/8448) — Ports nicht direkt exponieren. +- `.env`-Datei statt `export` nutzen (nicht committen), Secrets nie in Git. +- Backups: `pgdata/`-Volume (App-DB) und Synapse-eigene Postgres-DB regelmäßig sichern (`pg_dump`). +- Updates: `git pull && docker compose up -d --build`. + +## Environment-Variablen (Backend) + +Siehe `backend/.env.example` für die vollständige Liste (lokal ohne Docker) bzw. oben für Compose. diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..b1640b5 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +.env.local diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..8958887 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,11 @@ +FROM node:22-alpine AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:1.27-alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..87cab82 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location /api/ { + proxy_pass http://backend:4000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri /index.html; + } +}