From 4126cbf737d20bebb897c462de89b9ae656eac17 Mon Sep 17 00:00:00 2001 From: epistemophiliac Date: Tue, 16 Jun 2026 20:58:32 -0400 Subject: [PATCH] Load ERPNext image internally on Coolify deploy via Skopeo. Add image-preload init service on Forgejo Docker network, default PULL_POLICY never, and preload host docker after Jenkins push. --- coolify.env.example | 9 +++++-- docker-compose.yml | 44 ++++++++++++++++++++++++++++---- docs/COOLIFY_DEPLOY.md | 27 ++++++++++---------- scripts/ci/jenkins-push-image.sh | 20 ++++++++++++++- 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/coolify.env.example b/coolify.env.example index 06fd811..d830b13 100644 --- a/coolify.env.example +++ b/coolify.env.example @@ -4,8 +4,13 @@ # --- Custom image (required — from Jenkins Forgejo registry) --- CUSTOM_IMAGE=git.aexoradao.com/epistemophiliac/erpnext CUSTOM_TAG=main-26933f3 -# Use if_not_present after scripts/coolify/preload-image.sh on the Coolify host -PULL_POLICY=if_not_present +PULL_POLICY=never + +# Forgejo registry read (internal image-preload service — same creds as Jenkins) +REGISTRY_USER=epistemophiliac +REGISTRY_PASSWORD=replace-with-forgejo-token +FORGEJO_HOST=forgejo-vydgeq365afzmxe4s1d75fwv +FORGEJO_NETWORK=vydgeq365afzmxe4s1d75fwv # --- Secrets (required — change before first deploy) --- DB_PASSWORD=replace-with-strong-secret diff --git a/docker-compose.yml b/docker-compose.yml index b94ab2d..59df3fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,21 +6,50 @@ x-customizable-image: &customizable_image image: ${CUSTOM_IMAGE:-git.aexoradao.com/epistemophiliac/erpnext}:${CUSTOM_TAG:-main} - pull_policy: ${PULL_POLICY:-if_not_present} + pull_policy: ${PULL_POLICY:-never} restart: ${RESTART_POLICY:-unless-stopped} +x-depends-on-image-preload: &depends_on_image_preload + depends_on: + image-preload: + condition: service_completed_successfully + x-depends-on-configurator: &depends_on_configurator depends_on: configurator: condition: service_completed_successfully x-backend-defaults: &backend_defaults - <<: [*depends_on_configurator, *customizable_image] + <<: [*depends_on_image_preload, *depends_on_configurator, *customizable_image] platform: linux/amd64 volumes: - sites:/home/frappe/frappe-bench/sites services: + image-preload: + image: quay.io/skopeo/stable:v1.17.0 + exclude_from_hc: true + restart: 'no' + networks: + - default + - forgejo-internal + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - 'CUSTOM_IMAGE=${CUSTOM_IMAGE:-git.aexoradao.com/epistemophiliac/erpnext}' + - 'CUSTOM_TAG=${CUSTOM_TAG:-main}' + - 'FORGEJO_HOST=${FORGEJO_HOST:-forgejo-vydgeq365afzmxe4s1d75fwv}' + - 'REGISTRY_USER=${REGISTRY_USER}' + - 'REGISTRY_PASSWORD=${REGISTRY_PASSWORD}' + entrypoint: ['sh', '-c'] + command: + - > + if [ -z "$$REGISTRY_USER" ] || [ -z "$$REGISTRY_PASSWORD" ]; then echo "[image-preload] ERROR: set REGISTRY_USER and REGISTRY_PASSWORD in Coolify"; exit 1; fi; + echo "[image-preload] copying from http://$$FORGEJO_HOST:3000/epistemophiliac/erpnext:$$CUSTOM_TAG (internal Forgejo)"; + skopeo copy "docker://$$FORGEJO_HOST:3000/epistemophiliac/erpnext:$$CUSTOM_TAG" "docker-daemon:$$CUSTOM_IMAGE:$$CUSTOM_TAG" --src-creds "$$REGISTRY_USER:$$REGISTRY_PASSWORD" --src-tls-verify=false --retry-times 3; + if [ "$$CUSTOM_TAG" != "main" ]; then skopeo copy "docker://$$FORGEJO_HOST:3000/epistemophiliac/erpnext:main" "docker-daemon:$$CUSTOM_IMAGE:main" --src-creds "$$REGISTRY_USER:$$REGISTRY_PASSWORD" --src-tls-verify=false --retry-times 3; fi; + echo "[image-preload] OK: $$CUSTOM_IMAGE:$$CUSTOM_TAG on host docker"; + db: image: mariadb:11.8 restart: unless-stopped @@ -91,7 +120,7 @@ services: condition: service_healthy create-site: - <<: *customizable_image + <<: [*depends_on_image_preload, *customizable_image] exclude_from_hc: true restart: 'no' platform: linux/amd64 @@ -156,7 +185,7 @@ services: start_period: 120s websocket: - <<: [*depends_on_configurator, *customizable_image] + <<: [*depends_on_image_preload, *depends_on_configurator, *customizable_image] platform: linux/amd64 command: - node @@ -168,7 +197,7 @@ services: condition: service_completed_successfully frontend: - <<: *customizable_image + <<: [*depends_on_image_preload, *customizable_image] platform: linux/amd64 command: - nginx-entrypoint.sh @@ -238,3 +267,8 @@ volumes: sites: db-data: redis-queue-data: + +networks: + forgejo-internal: + external: true + name: ${FORGEJO_NETWORK:-vydgeq365afzmxe4s1d75fwv} diff --git a/docs/COOLIFY_DEPLOY.md b/docs/COOLIFY_DEPLOY.md index 2dfc71a..ef43015 100644 --- a/docs/COOLIFY_DEPLOY.md +++ b/docs/COOLIFY_DEPLOY.md @@ -24,7 +24,9 @@ Copy from [`coolify.env.example`](../coolify.env.example). **Required before fir |----------|----------------|--------| | `CUSTOM_IMAGE` | yes | Jenkins artifact / `dist/coolify-image.env` | | `CUSTOM_TAG` | yes | e.g. `main-26933f3` (pin) or `main` | -| `PULL_POLICY` | yes | `if_not_present` (after host preload; see below) | +| `PULL_POLICY` | yes | `never` (image-preload loads internally) | +| `REGISTRY_USER` | yes | Forgejo username | +| `REGISTRY_PASSWORD` | yes | Forgejo token (package read) | | `DB_PASSWORD` | yes | strong secret | | `ADMIN_PASSWORD` | yes | Frappe `Administrator` password | | `INSTALL_APPS` | yes | `erpnext,payments,hrms,lending,lms` | @@ -45,24 +47,21 @@ Copy from [`coolify.env.example`](../coolify.env.example). **Required before fir **Order matters:** assign domain **then** deploy. If `create-site` runs with an empty site name, the stack exits with a clear error. -## 4. Preload image on Coolify host (required once per tag) +## 4. Internal image load (automatic) -The custom image is **~1.2 GB**. Coolify deploy can fail with `exit code 255` while pulling large layers through Cloudflare/Traefik (you may see progress stop around ~100 MB). +Compose includes an **`image-preload`** init service (Skopeo → internal Forgejo, same path as Jenkins push). It copies the image into host Docker **before** ERPNext services start — no Cloudflare pull. -**On the Coolify server as root** (SSH or Coolify terminal): +Required Coolify env vars (see [`coolify.env.example`](../coolify.env.example)): -```bash -git clone https://git.aexoradao.com/epistemophiliac/erpnext.git /tmp/erpnext -cd /tmp/erpnext -export REGISTRY_USER=epistemophiliac -export REGISTRY_PASSWORD='' -export CUSTOM_TAG=main-26933f3 # from Jenkins dist/coolify-image.env -bash scripts/coolify/preload-image.sh +```env +REGISTRY_USER=epistemophiliac +REGISTRY_PASSWORD= +CUSTOM_IMAGE=git.aexoradao.com/epistemophiliac/erpnext +CUSTOM_TAG=main-26933f3 +PULL_POLICY=never ``` -This copies from internal Forgejo (`forgejo-vydgeq365afzmxe4s1d75fwv:3000`) — same path Jenkins uses for push — and tags `git.aexoradao.com/epistemophiliac/erpnext:` locally. - -Set `PULL_POLICY=if_not_present` in Coolify so redeploys skip the large pull. +Jenkins also preloads the host after each green build, so redeploys are fast. ## 5. First deploy diff --git a/scripts/ci/jenkins-push-image.sh b/scripts/ci/jenkins-push-image.sh index 7a71674..745e453 100755 --- a/scripts/ci/jenkins-push-image.sh +++ b/scripts/ci/jenkins-push-image.sh @@ -33,4 +33,22 @@ push_with_skopeo() { push_with_skopeo "${REGISTRY_IMAGE}:${IMAGE_TAG}" push_with_skopeo "${REGISTRY_IMAGE}:main" -echo "Pushed via internal Forgejo (public pull: ${REGISTRY_IMAGE}:)" +preload_host_docker() { + local tag="${1##*:}" + echo "Preloading Coolify host docker: ${REGISTRY_IMAGE}:${tag}" + $DOCKER run --rm \ + --network "${FORGEJO_NETWORK}" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + quay.io/skopeo/stable:v1.17.0 \ + copy \ + "docker://${FORGEJO_HOST}:3000/epistemophiliac/erpnext:${tag}" \ + "docker-daemon:${REGISTRY_IMAGE}:${tag}" \ + --src-creds "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \ + --src-tls-verify=false \ + --retry-times 3 +} + +preload_host_docker "${REGISTRY_IMAGE}:${IMAGE_TAG}" +preload_host_docker "${REGISTRY_IMAGE}:main" + +echo "Pushed via internal Forgejo and preloaded on host docker"