ERPNext gunicorn can exceed 180s + 15 retries (~6.5 min) after migrate. Use 360s start_period, 20 retries, and fall back to SERVICE_FQDN_FRONTEND when currentsite.txt is empty. Frontend gets the same Host fallback.
244 lines
8.2 KiB
YAML
244 lines
8.2 KiB
YAML
# ERPNext production stack for Coolify.
|
|
# Domain: assign in Coolify UI → service `frontend` → port 8080.
|
|
# Image: CUSTOM_IMAGE + CUSTOM_TAG=main (latest Jenkins build on Forgejo).
|
|
# Host must have the image before deploy — Jenkins preloads :main after each push.
|
|
# Manual once: bash scripts/coolify/sync-main-from-forgejo.sh
|
|
|
|
x-customizable-image: &customizable_image
|
|
image: ${CUSTOM_IMAGE:-git.aexoradao.com/epistemophiliac/erpnext}:${CUSTOM_TAG:-main}
|
|
pull_policy: ${PULL_POLICY:-if_not_present}
|
|
restart: ${RESTART_POLICY:-unless-stopped}
|
|
|
|
x-frappe-platform: &frappe_platform
|
|
platform: linux/amd64
|
|
|
|
x-sites-volume: &sites_volume
|
|
volumes:
|
|
- sites:/home/frappe/frappe-bench/sites
|
|
|
|
x-depends-on-configurator: &depends_on_configurator
|
|
depends_on:
|
|
configurator:
|
|
condition: service_completed_successfully
|
|
|
|
x-backend-defaults: &backend_defaults
|
|
<<: [*depends_on_configurator, *customizable_image, *frappe_platform, *sites_volume]
|
|
|
|
services:
|
|
db:
|
|
image: mariadb:11.8
|
|
restart: unless-stopped
|
|
command:
|
|
- --character-set-server=utf8mb4
|
|
- --collation-server=utf8mb4_unicode_ci
|
|
- --skip-character-set-client-handshake
|
|
- --skip-innodb-read-only-compressed
|
|
environment:
|
|
- 'MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-changeme}'
|
|
- 'MARIADB_AUTO_UPGRADE=1'
|
|
healthcheck:
|
|
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
|
|
start_period: 5s
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
volumes:
|
|
- db-data:/var/lib/mysql
|
|
|
|
redis-cache:
|
|
image: redis:8.6-alpine
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ['CMD', 'redis-cli', 'ping']
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
redis-queue:
|
|
image: redis:8.6-alpine
|
|
restart: unless-stopped
|
|
volumes:
|
|
- redis-queue-data:/data
|
|
healthcheck:
|
|
test: ['CMD', 'redis-cli', 'ping']
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
configurator:
|
|
<<: *backend_defaults
|
|
exclude_from_hc: true
|
|
restart: 'no'
|
|
entrypoint: ['bash', '-c']
|
|
command:
|
|
- >
|
|
ls -1 apps > sites/apps.txt;
|
|
bench set-config -g db_host $$DB_HOST;
|
|
bench set-config -gp db_port $$DB_PORT;
|
|
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
|
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
|
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
|
|
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
|
bench set-config -g chromium_path /usr/bin/chromium-headless-shell;
|
|
environment:
|
|
- 'DB_HOST=db'
|
|
- 'DB_PORT=3306'
|
|
- 'REDIS_CACHE=redis-cache:6379'
|
|
- 'REDIS_QUEUE=redis-queue:6379'
|
|
- 'SOCKETIO_PORT=9000'
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis-cache:
|
|
condition: service_healthy
|
|
redis-queue:
|
|
condition: service_healthy
|
|
|
|
create-site:
|
|
<<: *customizable_image
|
|
exclude_from_hc: true
|
|
restart: 'no'
|
|
platform: linux/amd64
|
|
entrypoint: ['bash', '-c']
|
|
command:
|
|
- >
|
|
export CI=1;
|
|
B=/usr/local/bin/bench;
|
|
SITE=$$SERVICE_FQDN_FRONTEND;
|
|
if [ -z "$$SITE" ]; then echo "[create-site] ERROR: SERVICE_FQDN_FRONTEND empty — assign domain to frontend:8080 in Coolify"; exit 1; fi;
|
|
wait-for-it -t 120 db:3306;
|
|
wait-for-it -t 120 redis-cache:6379;
|
|
wait-for-it -t 120 redis-queue:6379;
|
|
if [ -d "sites/$$SITE" ]; then echo "[create-site] exists"; $$B use "$$SITE"; else echo "[create-site] creating (10-20 min first time — verbose progress suppressed)"; INSTALL_ARGS=""; IFS=',' read -r -a apps <<< "$$INSTALL_APPS"; for app in "$${apps[@]}"; do INSTALL_ARGS="$$INSTALL_ARGS --install-app $$app"; done; set -o pipefail; $$B new-site "$$SITE" --mariadb-user-host-login-scope='%' --admin-password "$$ADMIN_PASSWORD" --db-root-password "$$DB_PASSWORD" $$INSTALL_ARGS --set-default 2>&1 | grep -vE '^Updating DocTypes for |^Creating Workspace|^Creating Desktop Icons|^Updating Dashboard for |^\* Installing |^Patching Existing|^rename_field:|^Thank you for installing|^Setting up Frappe HR'; fi
|
|
environment:
|
|
- SERVICE_FQDN_FRONTEND
|
|
- 'ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme}'
|
|
- 'DB_PASSWORD=${DB_PASSWORD:-changeme}'
|
|
- 'INSTALL_APPS=${INSTALL_APPS:-erpnext,payments,hrms,lending,lms}'
|
|
volumes:
|
|
- sites:/home/frappe/frappe-bench/sites
|
|
depends_on:
|
|
configurator:
|
|
condition: service_completed_successfully
|
|
db:
|
|
condition: service_healthy
|
|
|
|
migrator:
|
|
<<: *backend_defaults
|
|
exclude_from_hc: true
|
|
restart: 'no'
|
|
entrypoint: ['bash', '-c']
|
|
command:
|
|
- >
|
|
if [ "$$MIGRATE_SITES" != "true" ]; then echo "[migrator] disabled"; exit 0; fi;
|
|
if [ -z "$$(find sites -mindepth 2 -maxdepth 2 -name site_config.json 2>/dev/null)" ]; then echo "[migrator] no sites"; exit 0; fi;
|
|
echo "[migrator] migrating all sites";
|
|
set -o pipefail;
|
|
bench --site all migrate 2>&1 | grep -vE '^Updating DocTypes for |^Creating Workspace|^Creating Desktop Icons|^Updating Dashboard for ';
|
|
environment:
|
|
- 'MIGRATE_SITES=${MIGRATE_SITES:-true}'
|
|
depends_on:
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
|
|
backend:
|
|
<<: *backend_defaults
|
|
environment:
|
|
- SERVICE_FQDN_FRONTEND
|
|
- 'GUNICORN_THREADS=${GUNICORN_THREADS:-4}'
|
|
- 'GUNICORN_WORKERS=${GUNICORN_WORKERS:-2}'
|
|
- 'GUNICORN_TIMEOUT=${GUNICORN_TIMEOUT:-120}'
|
|
depends_on:
|
|
configurator:
|
|
condition: service_completed_successfully
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
migrator:
|
|
condition: service_completed_successfully
|
|
healthcheck:
|
|
test: ['CMD-SHELL', 'H=$$(tr -d "\r\n" < sites/currentsite.txt 2>/dev/null); [ -n "$$H" ] || H="$$SERVICE_FQDN_FRONTEND"; [ -z "$$H" ] && exit 1; curl -sf --max-time 8 -H "Host: $$H" http://127.0.0.1:8000/api/method/ping || exit 1']
|
|
interval: 15s
|
|
timeout: 10s
|
|
retries: 20
|
|
start_period: 360s
|
|
|
|
websocket:
|
|
<<: [*depends_on_configurator, *customizable_image, *frappe_platform, *sites_volume]
|
|
command:
|
|
- node
|
|
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
|
depends_on:
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
|
|
frontend:
|
|
<<: *customizable_image
|
|
platform: linux/amd64
|
|
entrypoint: ['bash', '-c']
|
|
command:
|
|
- export FRAPPE_SITE_NAME_HEADER=$$(tr -d '\r\n' < /home/frappe/frappe-bench/sites/currentsite.txt); exec nginx-entrypoint.sh
|
|
environment:
|
|
- SERVICE_URL_FRONTEND_8080
|
|
- SERVICE_FQDN_FRONTEND
|
|
- 'BACKEND=backend:8000'
|
|
- 'SOCKETIO=websocket:9000'
|
|
- 'UPSTREAM_REAL_IP_ADDRESS=${UPSTREAM_REAL_IP_ADDRESS:-127.0.0.1}'
|
|
- 'UPSTREAM_REAL_IP_HEADER=${UPSTREAM_REAL_IP_HEADER:-X-Forwarded-For}'
|
|
- 'UPSTREAM_REAL_IP_RECURSIVE=${UPSTREAM_REAL_IP_RECURSIVE:-off}'
|
|
- 'PROXY_READ_TIMEOUT=${PROXY_READ_TIMEOUT:-120}'
|
|
- 'CLIENT_MAX_BODY_SIZE=${CLIENT_MAX_BODY_SIZE:-50m}'
|
|
volumes:
|
|
- sites:/home/frappe/frappe-bench/sites
|
|
depends_on:
|
|
backend:
|
|
condition: service_healthy
|
|
websocket:
|
|
condition: service_started
|
|
healthcheck:
|
|
test: ['CMD-SHELL', 'H=$$(tr -d "\r\n" < sites/currentsite.txt 2>/dev/null); [ -n "$$H" ] || H="$$SERVICE_FQDN_FRONTEND"; [ -z "$$H" ] && exit 1; curl -sf --max-time 8 -H "Host: $$H" http://127.0.0.1:8080/api/method/ping || exit 1']
|
|
interval: 15s
|
|
timeout: 10s
|
|
retries: 20
|
|
start_period: 180s
|
|
|
|
queue-short:
|
|
<<: *backend_defaults
|
|
command:
|
|
- bench
|
|
- worker
|
|
- --queue
|
|
- short,default
|
|
depends_on:
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
migrator:
|
|
condition: service_completed_successfully
|
|
|
|
queue-long:
|
|
<<: *backend_defaults
|
|
command:
|
|
- bench
|
|
- worker
|
|
- --queue
|
|
- long,default,short
|
|
depends_on:
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
migrator:
|
|
condition: service_completed_successfully
|
|
|
|
scheduler:
|
|
<<: *backend_defaults
|
|
command:
|
|
- bench
|
|
- schedule
|
|
depends_on:
|
|
create-site:
|
|
condition: service_completed_successfully
|
|
migrator:
|
|
condition: service_completed_successfully
|
|
|
|
volumes:
|
|
sites:
|
|
db-data:
|
|
redis-queue-data:
|