From f2f7e6355de86aa6e65cb65f58d3a2793569071b Mon Sep 17 00:00:00 2001 From: epistemophiliac Date: Tue, 16 Jun 2026 20:34:44 -0400 Subject: [PATCH] Wire Coolify domain to SITE_NAME and document env template SERVICE_FQDN_FRONTEND from the frontend domain drives site creation and nginx headers; coolify.env.example adds CUSTOM_IMAGE/CUSTOM_TAG for Jenkins registry pulls. Co-authored-by: Cursor --- README.md | 4 +- coolify.env.example | 36 +++++++++++++ docker-compose.yml | 11 ++-- docs/COOLIFY_DEPLOY.md | 74 ++++++++++++++------------- example.env | 28 +++------- scripts/ci/validate-docker-compose.sh | 12 +++++ 6 files changed, 102 insertions(+), 63 deletions(-) create mode 100644 coolify.env.example diff --git a/README.md b/README.md index 822305e..8b1ea25 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ See [docs/JENKINS.md](docs/JENKINS.md). ## Coolify deploy (you configure) 1. Docker Compose from this git repo, file `docker-compose.yml` -2. Env vars from [`example.env`](example.env) — use `CUSTOM_TAG` from latest green Jenkins build -3. Domain on service **`frontend`**, port **`8080`** +2. Env vars from [`coolify.env.example`](coolify.env.example) — `CUSTOM_IMAGE`, `CUSTOM_TAG` from latest green Jenkins build (`dist/coolify-image.env`) +3. Domain on service **`frontend`**, port **`8080`** — Coolify sets `SERVICE_FQDN_FRONTEND`; compose uses it for `SITE_NAME` / `FRAPPE_SITE_NAME_HEADER` See [docs/COOLIFY_DEPLOY.md](docs/COOLIFY_DEPLOY.md). diff --git a/coolify.env.example b/coolify.env.example new file mode 100644 index 0000000..0fce5e4 --- /dev/null +++ b/coolify.env.example @@ -0,0 +1,36 @@ +# Paste into Coolify → erpnext service → Environment Variables +# After Jenkins green build, copy CUSTOM_IMAGE / CUSTOM_TAG from dist/coolify-image.env + +# --- Custom image (required — from Jenkins Forgejo registry) --- +CUSTOM_IMAGE=git.aexoradao.com/epistemophiliac/erpnext +CUSTOM_TAG=main-26933f3 +PULL_POLICY=always + +# --- Secrets (required — change before first deploy) --- +DB_PASSWORD=replace-with-strong-secret +ADMIN_PASSWORD=replace-with-strong-secret + +# --- Domain (automatic — do NOT set unless overriding) --- +# 1. In Coolify UI: add domain on service "frontend", port 8080 +# 2. Coolify sets SERVICE_FQDN_FRONTEND → compose uses it for SITE_NAME + nginx header +# 3. Deploy AFTER domain is assigned (first deploy creates the Frappe site) +# +# SITE_NAME= +# FRAPPE_SITE_NAME_HEADER= + +# --- Apps installed on first site creation only (order matters) --- +INSTALL_APPS=erpnext,payments,hrms,lending,lms + +# --- Redeploy behaviour --- +MIGRATE_SITES=true +RESTART_POLICY=unless-stopped + +# --- Optional tuning --- +GUNICORN_THREADS=4 +GUNICORN_WORKERS=2 +GUNICORN_TIMEOUT=120 +PROXY_READ_TIMEOUT=120 +CLIENT_MAX_BODY_SIZE=50m +UPSTREAM_REAL_IP_ADDRESS=127.0.0.1 +UPSTREAM_REAL_IP_HEADER=X-Forwarded-For +UPSTREAM_REAL_IP_RECURSIVE=off diff --git a/docker-compose.yml b/docker-compose.yml index 4cc4cab..29e64ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ # ERPNext production stack for Coolify. -# Based on https://github.com/frappe/frappe_docker (compose.yaml + mariadb + redis). -# Coolify: assign your domain to service `frontend` on port 8080. +# Domain: assign in Coolify UI → service `frontend` → port 8080. +# SITE_NAME + FRAPPE_SITE_NAME_HEADER use SERVICE_FQDN_FRONTEND automatically. +# Image: set CUSTOM_IMAGE / CUSTOM_TAG from Jenkins (dist/coolify-image.env). # No ports: — routing uses SERVICE_URL_FRONTEND_8080. x-customizable-image: &customizable_image @@ -99,12 +100,13 @@ services: - > B=/usr/local/bin/bench; SITE=$$SITE_NAME; + if [ -z "$$SITE" ]; then echo "[create-site] ERROR: SITE_NAME empty — assign domain to frontend:8080 in Coolify (SERVICE_FQDN_FRONTEND)"; 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"; INSTALL_ARGS=""; IFS=',' read -r -a apps <<< "$$INSTALL_APPS"; for app in "$${apps[@]}"; do INSTALL_ARGS="$$INSTALL_ARGS --install-app $$app"; done; $$B new-site "$$SITE" --mariadb-user-host-login-scope='%' --admin-password "$$ADMIN_PASSWORD" --db-root-password "$$DB_PASSWORD" $$INSTALL_ARGS --set-default; fi environment: - - 'SITE_NAME=${SITE_NAME:-erp.example.com}' + - 'SITE_NAME=${SITE_NAME:-${SERVICE_FQDN_FRONTEND}}' - 'ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme}' - 'DB_PASSWORD=${DB_PASSWORD:-changeme}' - 'INSTALL_APPS=${INSTALL_APPS:-erpnext,payments,hrms,lending,lms}' @@ -172,9 +174,10 @@ services: - nginx-entrypoint.sh environment: - SERVICE_URL_FRONTEND_8080 + - SERVICE_FQDN_FRONTEND - 'BACKEND=backend:8000' - 'SOCKETIO=websocket:9000' - - 'FRAPPE_SITE_NAME_HEADER=${FRAPPE_SITE_NAME_HEADER:-${SITE_NAME:-erp.example.com}}' + - 'FRAPPE_SITE_NAME_HEADER=${FRAPPE_SITE_NAME_HEADER:-${SERVICE_FQDN_FRONTEND}}' - '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}' diff --git a/docs/COOLIFY_DEPLOY.md b/docs/COOLIFY_DEPLOY.md index c2ff378..f653f59 100644 --- a/docs/COOLIFY_DEPLOY.md +++ b/docs/COOLIFY_DEPLOY.md @@ -3,11 +3,11 @@ ## Prerequisites - Coolify v4+ with Docker Compose support -- Jenkins green build published image to `git.aexoradao.com/epistemophiliac/erpnext` -- Server: **minimum 4 GB RAM**, **8 GB+** recommended (custom image + LMS frontend assets) -- Public domain (e.g. `erp.yourdomain.com`) +- Jenkins green build → image in Forgejo Packages +- **4 GB+ RAM** (8 GB+ recommended) +- Compose file: `docker-compose.yml` -## 1. Create the Coolify service (you do this) +## 1. Create the Coolify service | Setting | Value | |---------|--------| @@ -16,53 +16,55 @@ | Branch | `main` | | Compose file | `docker-compose.yml` | -## 2. Environment variables +## 2. Environment variables (Coolify UI) -From latest **green Jenkins build**, use `dist/coolify-image.env` or: +Copy from [`coolify.env.example`](../coolify.env.example). **Required before first deploy:** -| Variable | Required | Example | Notes | -|----------|----------|---------|-------| -| `CUSTOM_IMAGE` | yes | `git.aexoradao.com/epistemophiliac/erpnext` | Forgejo registry | -| `CUSTOM_TAG` | yes | `main-3eefb73` or `main` | Pin SHA for prod; `main` = latest CI | -| `PULL_POLICY` | yes | `always` | Pull from registry on deploy | -| `DB_PASSWORD` | yes | strong secret | MariaDB root | -| `SITE_NAME` | yes | `erp.yourdomain.com` | Must match domain | -| `ADMIN_PASSWORD` | yes | strong secret | Frappe login | -| `FRAPPE_SITE_NAME_HEADER` | yes | same as `SITE_NAME` | Single-site routing | -| `INSTALL_APPS` | yes | `erpnext,payments,hrms,lending,lms` | First site only | -| `MIGRATE_SITES` | no | `true` | Migrate on redeploy | +| Variable | Set in Coolify? | Source | +|----------|----------------|--------| +| `CUSTOM_IMAGE` | yes | Jenkins artifact / `dist/coolify-image.env` | +| `CUSTOM_TAG` | yes | e.g. `main-26933f3` (pin) or `main` | +| `PULL_POLICY` | yes | `always` | +| `DB_PASSWORD` | yes | strong secret | +| `ADMIN_PASSWORD` | yes | Frappe `Administrator` password | +| `INSTALL_APPS` | yes | `erpnext,payments,hrms,lending,lms` | +| `SITE_NAME` | **no** (auto) | From Coolify domain via `SERVICE_FQDN_FRONTEND` | +| `FRAPPE_SITE_NAME_HEADER` | **no** (auto) | Same as domain via `SERVICE_FQDN_FRONTEND` | -> **Coolify env cache:** Changing defaults in `docker-compose.yml` does not update values already stored in Coolify. Edit them in the UI. +> **Coolify env cache:** If you previously set `SITE_NAME=erp.example.com` in Coolify, **delete it** so compose defaults use your real domain. Changing `docker-compose.yml` defaults alone does not update stored values. -## 3. Domain routing +## 3. Domain (before first deploy) -1. Add domain: `erp.yourdomain.com` -2. Attach to service **`frontend`** -3. Port **`8080`** +1. Coolify → your service → **Domains** +2. Add domain, e.g. `erp.aexoradao.com` +3. Attach to service **`frontend`**, port **`8080`** +4. Coolify writes `SERVICE_FQDN_FRONTEND=erp.aexoradao.com` into the stack `.env` +5. Compose sets: + - `create-site` → `SITE_NAME=erp.aexoradao.com` + - `frontend` → `FRAPPE_SITE_NAME_HEADER=erp.aexoradao.com` -## 4. First deploy timeline +**Order matters:** assign domain **then** deploy. If `create-site` runs with an empty site name, the stack exits with a clear error. + +## 4. First deploy ```text -pull custom image → db (healthy) → redis → configurator - → create-site (install erpnext + payments + hrms + lending + lms, ~10–20 min) +pull CUSTOM_IMAGE:TAG → db → redis → configurator + → create-site (install apps, ~10–20 min) → migrator → backend / workers / frontend ``` +Login: `https://your-domain` — user `Administrator`, password = `ADMIN_PASSWORD`. + ## 5. Upgrades -1. Push app changes to git → Jenkins builds new image -2. Set `CUSTOM_TAG` in Coolify to new `main-` -3. Redeploy — `migrator` runs `bench migrate` - -## Apps in the image - -See [`apps.json`](../apps.json). Site install list: `INSTALL_APPS` in [`example.env`](../example.env). +1. Jenkins builds new image → update `CUSTOM_TAG` in Coolify +2. Redeploy — `migrator` runs `bench migrate` ## Troubleshooting | Symptom | Fix | |---------|-----| -| Image pull failed | Check registry login on Coolify host; verify tag exists in Forgejo Packages | -| create-site fails on LMS | Ensure `payments` is in `INSTALL_APPS` before `lms` | -| 502 / unhealthy frontend | Wait for create-site; check `backend` health | -| Wrong site | `SITE_NAME` and `FRAPPE_SITE_NAME_HEADER` must match Coolify domain | +| `SITE_NAME empty` on create-site | Assign domain on `frontend:8080` before deploy | +| Wrong site / 404 nginx | Delete old `SITE_NAME` in Coolify UI; ensure header matches domain | +| Site created with wrong name | Wipe `sites` volume or rename site manually — env change alone won't rename | +| Image pull failed | Check `CUSTOM_IMAGE` / `CUSTOM_TAG` in Forgejo Packages | diff --git a/example.env b/example.env index e7670af..0b2ae6a 100644 --- a/example.env +++ b/example.env @@ -1,38 +1,24 @@ -# Copy to Coolify Environment Variables (Service > Environment). -# Image tags come from Jenkins (Forgejo container registry). +# Reference for local testing (`docker compose --env-file example.env config`). +# For Coolify, use coolify.env.example — domain comes from SERVICE_FQDN_FRONTEND. -# Custom image built by Jenkins (apps: ERPNext, HRMS, Lending, LMS + payments) +# Custom image (match latest Jenkins build — see dist/coolify-image.env) CUSTOM_IMAGE=git.aexoradao.com/epistemophiliac/erpnext CUSTOM_TAG=main PULL_POLICY=always +RESTART_POLICY=unless-stopped -# Frappe major line — must match apps.json branches (version-16) -FRAPPE_BRANCH=version-16 - -# MariaDB root password (required — change before production) -DB_PASSWORD=changeme - -# Frappe site name — MUST match your Coolify domain +# Local-only overrides when not using Coolify magic vars SITE_NAME=erp.example.com - -# Frappe Administrator password -ADMIN_PASSWORD=changeme - -# Nginx site header — for single-site Coolify, same as SITE_NAME FRAPPE_SITE_NAME_HEADER=erp.example.com -# Apps installed on first site creation (comma-separated, order matters) +DB_PASSWORD=changeme +ADMIN_PASSWORD=changeme INSTALL_APPS=erpnext,payments,hrms,lending,lms - -# Run bench migrate on every deploy (set false to skip) MIGRATE_SITES=true -# Gunicorn tuning (optional) GUNICORN_THREADS=4 GUNICORN_WORKERS=2 GUNICORN_TIMEOUT=120 - -# Proxy / upload limits (optional) PROXY_READ_TIMEOUT=120 CLIENT_MAX_BODY_SIZE=50m UPSTREAM_REAL_IP_ADDRESS=127.0.0.1 diff --git a/scripts/ci/validate-docker-compose.sh b/scripts/ci/validate-docker-compose.sh index 65b5617..834d8d0 100755 --- a/scripts/ci/validate-docker-compose.sh +++ b/scripts/ci/validate-docker-compose.sh @@ -66,6 +66,18 @@ else err "[DC-03] missing SERVICE_URL_FRONTEND_8080 on frontend" fi +if grep -q 'SERVICE_FQDN_FRONTEND' "$COMPOSE_FILE"; then + pass "[DC-07] SERVICE_FQDN_FRONTEND present (Coolify domain → SITE_NAME)" +else + warn "[DC-07] missing SERVICE_FQDN_FRONTEND — SITE_NAME will not track Coolify domain" +fi + +if grep -q 'CUSTOM_IMAGE' "$COMPOSE_FILE" && grep -q 'CUSTOM_TAG' "$COMPOSE_FILE"; then + pass "[DC-10] CUSTOM_IMAGE / CUSTOM_TAG configured for Jenkins registry" +else + warn "[DC-10] missing CUSTOM_IMAGE or CUSTOM_TAG in compose" +fi + # Bind mounts ./scripts on long-running services (heuristic) if grep -E '^\s+-\s+['\''"]?\./scripts' "$COMPOSE_FILE" >/dev/null 2>&1; then if grep -B30 './scripts' "$COMPOSE_FILE" | grep -qE 'restart:\s+(unless-stopped|always)'; then