# Coolify deployment — Production ERPNext (+ HRMS, Lending, LMS) ## Prerequisites - Coolify v4+ with Docker Compose support - Jenkins green build → image in Forgejo Packages - **4 GB+ RAM** (8 GB+ recommended) - Compose file: `docker-compose.yml` ## 1. Create the Coolify service | Setting | Value | |---------|--------| | Type | Docker Compose | | Repository | `https://git.aexoradao.com/epistemophiliac/erpnext` | | Branch | `main` | | Compose file | `docker-compose.yml` | ## 2. Environment variables (Coolify UI) Copy from [`coolify.env.example`](../coolify.env.example). **Required before first deploy:** | Variable | Set in Coolify? | Source | |----------|----------------|--------| | `CUSTOM_IMAGE` | yes | Jenkins artifact / `dist/coolify-image.env` | | `CUSTOM_TAG` | yes | `main` (latest Jenkins build — do not pin commit tags) | | `PULL_POLICY` | yes | `if_not_present` | | `DB_PASSWORD` | yes | strong secret | | `ADMIN_PASSWORD` | yes | Frappe `Administrator` password | | `INSTALL_APPS` | yes | `erpnext,payments,hrms,lending,lms` | | `SITE_NAME` | **yes** | Canonical Frappe site name — set once (e.g. `erp.aexoradao.com`). **Do not omit** — changing Coolify domain without this created a second empty site. | | `FRAPPE_SITE_NAME_HEADER` | **no** (auto) | Set at frontend start from `sites/currentsite.txt` | > **Data persistence:** MariaDB (`db-data`), Frappe files (`sites`), and Redis queue data use **named Docker volumes** — they survive restarts and redeploys. The site **name** is stored in the `sites` volume; it must stay consistent via `SITE_NAME`. > **Changing domain:** Set `SITE_NAME` to your target domain **before** the first deploy, or leave it set when you change Coolify's domain. On redeploy, `create-site` reuses the existing site and runs `bench rename-site` if the folder name differs from `SITE_NAME`. ## 3. Domain (before first deploy) 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` **Order matters:** assign domain **then** deploy. If `create-site` runs with an empty site name, the stack exits with a clear error. ## 4. Image on the Coolify host (before first deploy) The custom image is **~1.2 GB**. Coolify must find `git.aexoradao.com/epistemophiliac/erpnext:main` on the **host** Docker daemon — not pulled through Cloudflare during deploy. **Jenkins does this automatically** after each green build (`jenkins-push-image.sh` copies `:main` from internal Forgejo via Skopeo). **Manual / one-time** on the Coolify server as root: ```bash export REGISTRY_USER=epistemophiliac export REGISTRY_PASSWORD='' bash scripts/coolify/sync-main-from-forgejo.sh ``` Coolify env: `CUSTOM_TAG=main` only — no `main-` pins. ## 5. First deploy ```text db → redis → configurator → create-site (install apps, ~10–20 min) → migrator → backend / workers / frontend ``` Login: `https://your-domain` — user `Administrator`, password = `ADMIN_PASSWORD`. ## 6. Upgrades 1. Jenkins builds new image → pushes `:main` and preloads host Docker 2. Redeploy Coolify — `if_not_present` uses the updated local `:main` ## Troubleshooting | Symptom | Fix | |---------|-----| | Traefik `404 page not found` / URL unreachable | Domain on service `frontend` port **8080**; compose must declare `SERVICE_URL_FRONTEND_8080`; add `traefik.http.services.frontend.loadbalancer.server.port=8080` (Coolify 4.1.x omits port if domain has no `:8080` suffix) | | Backend unhealthy / deploy fails after migrator | Gunicorn can take 6+ min on redeploy — backend `start_period` is 360s; healthcheck uses `Host` from `currentsite.txt` or `SERVICE_FQDN_FRONTEND` | | `SITE_NAME empty` on create-site | Assign domain on `frontend:8080` before deploy (`SERVICE_FQDN_FRONTEND`) | | Wrong site / setup wizard again after domain change | Set **`SITE_NAME`** in Coolify (e.g. `erp.aexoradao.com`) and redeploy — compose reuses existing site volume and renames if needed. Do not wipe volumes. | | Site created with wrong name | Set `SITE_NAME` and redeploy (auto rename), or `bench rename-site` manually — **never** wipe `sites` / `db-data` unless intentional | | Deploy log page crashes / blank | First `create-site` is huge — fixed by filtering DocType spam; redeploy after site exists | | create-site finished but backend/scheduler not running | Deploy timed out during first site install — **redeploy** (site exists, starts in seconds) | | Image pull failed | Ensure `:main` on host via Jenkins or sync script — do not pull large image through Cloudflare |