From 5ff7b7a3ed8499a724438996aab8e735466e41a8 Mon Sep 17 00:00:00 2001 From: Stiftung Development Date: Mon, 15 Sep 2025 23:21:05 +0200 Subject: [PATCH] Fix production deployment: use pre-built images instead of local builds + handle git conflicts --- .github/workflows/ci-cd.yml | 6 + deploy-production/docker-compose.prod.yml | 231 +++++++++------------- 2 files changed, 94 insertions(+), 143 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 54f34ca..b01d1d2 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -195,9 +195,15 @@ jobs: script: | cd /opt/stiftung + # Stash any local changes to avoid conflicts + git stash push -m "Auto-stash before deployment $(date)" + # Pull latest changes using Personal Access Token git pull https://$DEPLOY_TOKEN@github.com/remmerinio/stiftung-management-system.git main + # Backup current compose.yml + cp compose.yml compose.yml.backup + # Copy production docker compose file to the active compose.yml cp deploy-production/docker-compose.prod.yml compose.yml diff --git a/deploy-production/docker-compose.prod.yml b/deploy-production/docker-compose.prod.yml index 2700c1f..1be8311 100644 --- a/deploy-production/docker-compose.prod.yml +++ b/deploy-production/docker-compose.prod.yml @@ -1,194 +1,139 @@ +# Production Docker Compose Configuration +# This file is used for production deployment via GitHub Actions +# For local development, use: docker-compose -f compose.dev.yml up +# +# IMPORTANT: This configuration requires ALL environment variables to be +# provided via the production server's .env file. No fallback values are +# included for security reasons. + services: db: - image: postgres:15-alpine + image: postgres:16-alpine restart: unless-stopped - environme beat: - build: - context: ../app - dockerfile: Dockerfile + environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - - postgres_data:/var/lib/postgresql/data - - ../app/backups:/backups - deploy: - resources: - limits: - memory: 1G - cpus: '1.0' + - dbdata:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] - interval: 30s - timeout: 10s + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s retries: 5 redis: image: redis:7-alpine restart: unless-stopped - command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru - volumes: - - redis_data:/data - deploy: - resources: - limits: - memory: 512M - cpus: '0.5' web: - image: ghcr.io/remmerinio/stiftung-management-system:main + image: ghcr.io/remmerinio/stiftung-management-system:latest restart: unless-stopped - environment: - - DEBUG=False - - ALLOWED_HOSTS=${ALLOWED_HOSTS} - - SECRET_KEY=${SECRET_KEY} - - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - - REDIS_URL=redis://redis:6379/0 - - CELERY_BROKER_URL=redis://redis:6379/0 - - CELERY_RESULT_BACKEND=redis://redis:6379/0 - volumes: - - ../app/media:/app/media - - ../app/static:/app/static - - ../app/backups:/app/backups depends_on: db: condition: service_healthy redis: condition: service_started - deploy: - resources: - limits: - memory: 1G - cpus: '1.0' + environment: + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY} + - DJANGO_DEBUG=${DJANGO_DEBUG} + - DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS} + - LANGUAGE_CODE=${LANGUAGE_CODE} + - TIME_ZONE=${TIME_ZONE} + - REDIS_URL=${REDIS_URL} + - PAPERLESS_API_URL=${PAPERLESS_API_URL} + - PAPERLESS_API_TOKEN=${PAPERLESS_API_TOKEN} ports: - - "127.0.0.1:8000:8000" + - "8081:8000" + volumes: + - media_files:/app/media + command: ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"] worker: - image: ghcr.io/remmerinio/stiftung-management-system:main + image: ghcr.io/remmerinio/stiftung-management-system:latest restart: unless-stopped - command: celery -A core worker -l info --concurrency=2 environment: - - DEBUG=False - - SECRET_KEY=${SECRET_KEY} - - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - - REDIS_URL=redis://redis:6379/0 - - CELERY_BROKER_URL=redis://redis:6379/0 - - CELERY_RESULT_BACKEND=redis://redis:6379/0 - volumes: - - ./app/media:/app/media - - ./backups:/app/backups + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY} + - DJANGO_DEBUG=${DJANGO_DEBUG} + - REDIS_URL=${REDIS_URL} depends_on: - db: - condition: service_healthy - redis: - condition: service_started - deploy: - resources: - limits: - memory: 512M - cpus: '0.5' + - redis + - db + command: ["celery", "-A", "core", "worker", "-l", "info"] beat: - image: ghcr.io/remmerinio/stiftung-management-system:main + image: ghcr.io/remmerinio/stiftung-management-system:latest restart: unless-stopped - command: celery -A core beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler environment: - - DEBUG=False - - SECRET_KEY=${SECRET_KEY} - - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - - REDIS_URL=redis://redis:6379/0 - - CELERY_BROKER_URL=redis://redis:6379/0 - - CELERY_RESULT_BACKEND=redis://redis:6379/0 - volumes: - - ./app/media:/app/media - - ./backups:/app/backups + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY} + - DJANGO_DEBUG=${DJANGO_DEBUG} + - REDIS_URL=${REDIS_URL} depends_on: - db: - condition: service_healthy - redis: - condition: service_started - deploy: - resources: - limits: - memory: 256M - cpus: '0.25' + - redis + - db + command: ["celery", "-A", "core", "beat", "-l", "info"] grampsweb: image: ghcr.io/gramps-project/grampsweb:latest - restart: unless-stopped - environment: - GRAMPSWEB_TREE: ${GRAMPSWEB_TREE:-Stiftung} - GRAMPSWEB_SECRET_KEY: ${GRAMPSWEB_SECRET_KEY} - GRAMPSWEB_ADMIN_EMAIL: ${GRAMPSWEB_ADMIN_EMAIL} - GRAMPSWEB_ADMIN_PASSWORD: ${GRAMPSWEB_ADMIN_PASSWORD} - volumes: - - gramps_users:/app/users - - gramps_index:/app/indexdir - - gramps_thumb_cache:/app/thumbnail_cache - - gramps_cache:/app/cache - - gramps_secret:/app/secret - - ./media:/app/media ports: - - "127.0.0.1:5000:5000" - deploy: - resources: - limits: - memory: 512M - cpus: '0.5' + - "8090:80" + environment: + - GRAMPSWEB_SECRET_KEY=${GRAMPSWEB_SECRET_KEY} + - GRAMPSWEB_ADMIN_EMAIL=${GRAMPSWEB_ADMIN_EMAIL} + - GRAMPSWEB_ADMIN_PASSWORD=${GRAMPSWEB_ADMIN_PASSWORD} + volumes: + - gramps_data:/app/data paperless: image: ghcr.io/paperless-ngx/paperless-ngx:latest - restart: unless-stopped - depends_on: - - db - - redis ports: - - "127.0.0.1:8080:8000" - healthcheck: - test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"] - interval: 30s - timeout: 10s - retries: 5 + - "8080:8000" + environment: + - PAPERLESS_REDIS=redis://redis:6379 + - PAPERLESS_DBHOST=db + - PAPERLESS_DBPORT=5432 + - PAPERLESS_DBNAME=${POSTGRES_DB} + - PAPERLESS_DBUSER=${POSTGRES_USER} + - PAPERLESS_DBPASS=${POSTGRES_PASSWORD} + - PAPERLESS_SECRET_KEY=${PAPERLESS_SECRET_KEY} + - PAPERLESS_URL=https://vhtv-stiftung.de + - PAPERLESS_ALLOWED_HOSTS=vhtv-stiftung.de,localhost + - PAPERLESS_CORS_ALLOWED_HOSTS=https://vhtv-stiftung.de + - PAPERLESS_FORCE_SCRIPT_NAME=/paperless + - PAPERLESS_STATIC_URL=/paperless/static/ + - PAPERLESS_LOGIN_REDIRECT_URL=/paperless/ + - PAPERLESS_LOGOUT_REDIRECT_URL=/paperless/ + - PAPERLESS_ADMIN_USER=${PAPERLESS_ADMIN_USER} + - PAPERLESS_ADMIN_PASSWORD=${PAPERLESS_ADMIN_PASSWORD} + - PAPERLESS_ADMIN_MAIL=${PAPERLESS_ADMIN_MAIL} volumes: - paperless_data:/usr/src/paperless/data - paperless_media:/usr/src/paperless/media - paperless_export:/usr/src/paperless/export - paperless_consume:/usr/src/paperless/consume - environment: - PAPERLESS_REDIS: redis://redis:6379 - PAPERLESS_DBHOST: db - PAPERLESS_DBNAME: ${PAPERLESS_DB:-paperless} - PAPERLESS_DBUSER: ${PAPERLESS_USER:-paperless} - PAPERLESS_DBPASS: ${PAPERLESS_PASSWORD:-paperless} - PAPERLESS_ADMIN_USER: ${PAPERLESS_ADMIN_USER:-admin} - PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD:-admin} - PAPERLESS_ADMIN_MAIL: ${PAPERLESS_ADMIN_MAIL:-admin@localhost} - PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY} - PAPERLESS_URL: https://vhtv-stiftung.de/paperless - PAPERLESS_ALLOWED_HOSTS: vhtv-stiftung.de,www.vhtv-stiftung.de - PAPERLESS_CORS_ALLOWED_HOSTS: https://vhtv-stiftung.de,https://www.vhtv-stiftung.de - PAPERLESS_TRUSTED_PROXIES: 172.16.0.0/12,10.0.0.0/8,192.168.0.0/16 - PAPERLESS_FORCE_SCRIPT_NAME: /paperless - PAPERLESS_STATIC_URL: /paperless/static/ - deploy: - resources: - limits: - memory: 2G - cpus: '1.0' + depends_on: + - db + - redis volumes: - postgres_data: - redis_data: - gramps_users: - gramps_index: - gramps_thumb_cache: - gramps_cache: - gramps_secret: + dbdata: + gramps_data: paperless_data: paperless_media: paperless_export: paperless_consume: - -networks: - default: - driver: bridge