erpnext/docs/COOLIFY_DEPLOY.md
epistemophiliac 20d1a46711 Persist Frappe site across domain changes and redeploys
Use stable SITE_NAME instead of SERVICE_FQDN_FRONTEND for site identity.
On redeploy: reuse existing site volume, recover from duplicate empty site
after domain change, and bench rename-site when folder name differs.
2026-06-16 23:23:20 -04:00

92 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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='<forgejo-token>'
bash scripts/coolify/sync-main-from-forgejo.sh
```
Coolify env: `CUSTOM_TAG=main` only — no `main-<sha>` pins.
## 5. First deploy
```text
db → redis → configurator
→ create-site (install apps, ~1020 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 |