From e95b9a61c95b95ccdae2b5622bb477e451139bf4 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Sun, 22 Feb 2026 04:10:38 +0000 Subject: [PATCH] feat: add Docker Compose setup with Nginx, PostgreSQL, MinIO Co-Authored-By: Claude Opus 4.6 --- apps/cms/Dockerfile | 13 ++++++ apps/web/Dockerfile | 26 ++++++++++++ docker/.env.example | 19 +++++++++ docker/docker-compose.yml | 89 +++++++++++++++++++++++++++++++++++++++ docker/nginx/nginx.conf | 58 +++++++++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 apps/cms/Dockerfile create mode 100644 apps/web/Dockerfile create mode 100644 docker/.env.example create mode 100644 docker/docker-compose.yml create mode 100644 docker/nginx/nginx.conf diff --git a/apps/cms/Dockerfile b/apps/cms/Dockerfile new file mode 100644 index 0000000..3765b7d --- /dev/null +++ b/apps/cms/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine AS base + +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY . . +RUN npm run build + +FROM node:20-alpine AS production +WORKDIR /app +COPY --from=base /app ./ +EXPOSE 1337 +CMD ["npm", "run", "start"] diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..e2c773d --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,26 @@ +FROM node:20-alpine AS base + +WORKDIR /app +COPY package.json package-lock.json* turbo.json ./ +COPY apps/web/package.json ./apps/web/ +COPY packages/shared/package.json ./packages/shared/ + +RUN npm ci + +COPY packages/shared/ ./packages/shared/ +COPY apps/web/ ./apps/web/ + +WORKDIR /app/apps/web +RUN npm run build + +FROM node:20-alpine AS production +WORKDIR /app/apps/web +COPY --from=base /app/apps/web/.next ./.next +COPY --from=base /app/apps/web/public ./public +COPY --from=base /app/apps/web/package.json ./ +COPY --from=base /app/apps/web/node_modules ./node_modules +COPY --from=base /app/node_modules /app/node_modules +COPY --from=base /app/packages /app/packages + +EXPOSE 3000 +CMD ["npm", "start"] diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..8eb3cc4 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,19 @@ +# Database +DATABASE_NAME=afterlife +DATABASE_USERNAME=afterlife +DATABASE_PASSWORD=change_me_in_production + +# Strapi +APP_KEYS=key1,key2,key3,key4 +API_TOKEN_SALT=change_me +ADMIN_JWT_SECRET=change_me +TRANSFER_TOKEN_SALT=change_me +JWT_SECRET=change_me +STRAPI_API_TOKEN=your_api_token_after_first_boot + +# MinIO +MINIO_ROOT_USER=afterlife +MINIO_ROOT_PASSWORD=change_me_in_production + +# Public URL (for frontend image/media URLs) +PUBLIC_STRAPI_URL=http://yourdomain.com diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..b44fa52 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,89 @@ +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + POSTGRES_DB: ${DATABASE_NAME:-afterlife} + POSTGRES_USER: ${DATABASE_USERNAME:-afterlife} + POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-afterlife} + ports: + - "5432:5432" + + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data --console-address ":9001" + volumes: + - minio_data:/data + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-afterlife} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-afterlife123} + ports: + - "9000:9000" + - "9001:9001" + + cms: + build: + context: ../apps/cms + dockerfile: Dockerfile + restart: unless-stopped + depends_on: + - postgres + - minio + environment: + HOST: 0.0.0.0 + PORT: 1337 + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_NAME: ${DATABASE_NAME:-afterlife} + DATABASE_USERNAME: ${DATABASE_USERNAME:-afterlife} + DATABASE_PASSWORD: ${DATABASE_PASSWORD:-afterlife} + APP_KEYS: ${APP_KEYS} + API_TOKEN_SALT: ${API_TOKEN_SALT} + ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET} + TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT} + JWT_SECRET: ${JWT_SECRET} + ports: + - "1337:1337" + + web: + build: + context: ../ + dockerfile: apps/web/Dockerfile + restart: unless-stopped + depends_on: + - cms + environment: + STRAPI_URL: http://cms:1337 + STRAPI_API_TOKEN: ${STRAPI_API_TOKEN} + NEXT_PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL:-http://localhost:1337} + ports: + - "3000:3000" + + nginx: + image: nginx:alpine + restart: unless-stopped + depends_on: + - web + - cms + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - certbot_certs:/etc/letsencrypt:ro + - certbot_www:/var/www/certbot:ro + + certbot: + image: certbot/certbot + volumes: + - certbot_certs:/etc/letsencrypt + - certbot_www:/var/www/certbot + +volumes: + postgres_data: + minio_data: + certbot_certs: + certbot_www: diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..19f6946 --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,58 @@ +events { + worker_connections 1024; +} + +http { + upstream web { + server web:3000; + } + + upstream cms { + server cms:1337; + } + + server { + listen 80; + server_name _; + + client_max_body_size 100M; + + location / { + proxy_pass http://web; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + 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 /api/ { + proxy_pass http://cms; + proxy_http_version 1.1; + 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 /admin { + proxy_pass http://cms; + proxy_http_version 1.1; + 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 /uploads/ { + proxy_pass http://cms; + proxy_set_header Host $host; + } + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + } +}