No description
Find a file
Torben Hammes 1a4b3551c2
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
style(makefile): fix indentation for consistency
2025-09-11 00:54:38 +02:00
frappe_docker@6e78467603 chore: add submodule for frappe_docker 2025-09-08 13:18:56 +02:00
.gitignore chore: init 2025-07-16 22:44:04 +02:00
.gitmodules chore: add .gitmodules for frappe_docker submodule 2025-09-08 13:12:20 +02:00
.woodpecker.yml ci: switch to docker:27-dind and enable privileged mode 2025-09-09 14:27:19 +02:00
apps.json feat(apps): add payments app configuration 2025-09-09 13:54:30 +02:00
compose.site-creator.yaml fix(site-creator): update to use MariaDB instead of Postgres 2025-09-11 00:49:47 +02:00
compose.traefik.yaml feat(docker): add Makefile and configuration files for ERPNext setup 2025-09-11 00:07:44 +02:00
Makefile style(makefile): fix indentation for consistency 2025-09-11 00:54:38 +02:00
README.md fix(site-creator): update to use MariaDB instead of Postgres 2025-09-11 00:49:47 +02:00

ERPNext with Custom Apps (frappe_docker)

This guide shows how to build a custom ERPNext image from apps.json in the project root and then start the stack using the official frappe_docker/compose.yamlno push to a registry. The .env file is placed in the project root and is used by both the build commands and Compose.


Project layout

.
├── .env                   # Configuration (image/tag, pull policy, build args)
├── apps.json              # Your app list (ERPNext, Payments, custom repos) — in project root
├── frappe_docker/         # Cloned frappe_docker repo
│   ├── compose.yaml       # Official compose by frappe_docker
│   └── images/
│       ├── custom/Containerfile
│       └── layered/Containerfile
└── README.md

1) Configure .env (in project root)

Create a .env in the project root (./.env). Example:

# ---- Image & Tag (local, no push) ----
CUSTOM_IMAGE=codeyard.3dt.digital/3dt/frappe
CUSTOM_TAG=local-1.0.0

# Compose: prevent pulls when the image exists locally
PULL_POLICY=never

# Default ERPNext version (only used as fallback in compose.yaml)
ERPNEXT_VERSION=version-15

# Optional build args
FRAPPE_PATH=https://github.com/frappe/frappe
FRAPPE_BRANCH=version-15
PYTHON_VERSION=3.11.9
NODE_VERSION=20.19.2

IMPORTANT: Docker image names must not contain @. Use codeyard.3dt.digital/3dt/frappe instead of codeyard.3dt.digital/@3dt/frappe.


2) Create apps.json (in project root)

Create ./apps.json like this:

[
  {
    "url": "https://github.com/frappe/erpnext",
    "branch": "version-15"
  },
  {
    "url": "https://github.com/frappe/payments",
    "branch": "version-15"
  },
  {
    "url": "https://<PAT>@git.example.com/project/repository.git",
    "branch": "main"
  }
]
  • For private repositories use a PAT inside the url (e.g. https://<PAT>@...).
  • Validate the JSON:
    jq empty apps.json
    

3) Optional: Base64 round-trip check

You do not need to export any variable. The build targets base64encode apps.json inline. If you want a quick integrity check:

make encode          # validates and writes apps-test-output.json (round-trip)

4) Build the image (from project root)

You have two build options:

A) Custom build (flexible, configurable Python/Node versions)

make build-custom

This runs:

docker build \
  --build-arg FRAPPE_PATH="${FRAPPE_PATH}" \
  --build-arg FRAPPE_BRANCH="${FRAPPE_BRANCH}" \
  --build-arg PYTHON_VERSION="${PYTHON_VERSION}" \
  --build-arg NODE_VERSION="${NODE_VERSION}" \
  --build-arg APPS_JSON_BASE64="${APPS_JSON_BASE64}" \
  --tag "${CUSTOM_IMAGE}:${CUSTOM_TAG}" \
  --file frappe_docker/images/custom/Containerfile \
  frappe_docker

B) Layered build (faster, uses prebuilt base layers)

make build-layered

This runs:

docker build \
  --build-arg FRAPPE_PATH="${FRAPPE_PATH}" \
  --build-arg FRAPPE_BRANCH="${FRAPPE_BRANCH}" \
  --build-arg APPS_JSON_BASE64="${APPS_JSON_BASE64}" \
  --tag "${CUSTOM_IMAGE}:${CUSTOM_TAG}" \
  --file frappe_docker/images/layered/Containerfile \
  frappe_docker

Result: A local image CUSTOM_IMAGE:CUSTOM_TAG (no registry involved).


5) Start the stack (no push)

From the project root:

make up
# or
docker compose -f frappe_docker/compose.yaml up -d
  • The compose file uses your .env (CUSTOM_IMAGE, CUSTOM_TAG, PULL_POLICY).
  • With PULL_POLICY=never, Compose does not pull from a registry if the image exists locally.

Open ERPNext at: http://localhost:8080 (depending on your chosen overrides).


6) Useful commands

make logs            # follow logs
make down            # stop and remove containers
make images          # list the built image(s)
make config          # print the resolved compose config

Troubleshooting

  • Invalid image reference: remove @ from CUSTOM_IMAGE (use slashes /).

  • Compose tries to pull the image: ensure .env is in the project root and contains PULL_POLICY=never. Also confirm that the built image exists locally:

    docker images | grep $(basename "${CUSTOM_IMAGE}") | grep "${CUSTOM_TAG}"
    
  • apps.json errors: run jq empty apps.json to validate, and re-run make encode to refresh APPS_JSON_BASE64.

  • Switching tags for local experiments:

    CUSTOM_TAG=feature-x make build-custom
    make up
    

Makefile shortcuts

This repo ships with a Makefile containing the targets used above. See make help for a quick summary.


From-scratch run (DB, Redis, Traefik labels, Site creation)

The steps below get you from zero to a running site using your custom image and the official frappe_docker compose. No files are added to the frappe_docker submodule.

  1. Prepare .env (root)

Add these keys in addition to your existing ones:

# Site
SITE_NAME=erp.corp.3dt.digital
ADMIN_PASSWORD=change-me
DB_PASSWORD=change-me

# Traefik (labels only; Traefik runs elsewhere)
DOMAIN=erp.corp.3dt.digital
TRAEFIK_CERTRESOLVER=letsencrypt
TRAEFIK_ENTRYPOINT_WEB=web
TRAEFIK_ENTRYPOINT_WEBSECURE=websecure

# Optional: external DB/Redis instead of overrides
# DB_HOST=my-postgres
# DB_PORT=5432
# REDIS_CACHE=my-redis:6379
# REDIS_QUEUE=my-redis:6379
  1. Define apps in apps.json (root)

List all apps you want baked into the image. Example is in section 2 above.

  1. Build the image
make build-custom
  1. Ensure Traefik network exists (if you use compose.traefik.yaml)

Your Traefik runs separately but must share the external Docker network used by the frontend service labels. Create it on the host if needed:

docker network create dokploy-network || true
  1. Start services incl. MariaDB, Redis, and Traefik labels
make up-full

This composes:

  • frappe_docker/compose.yaml (core services)
  • frappe_docker/overrides/compose.mariadb.yaml (MariaDB)
  • frappe_docker/overrides/compose.redis.yaml (Redis)
  • compose.traefik.yaml (attach frontend to your external Traefik via labels/network)
  1. Create the site (one-time)

Use the one-shot site creator. It reads the app list from sites/apps.txt (generated by the configurator) and installs all apps whose code is already inside your custom image.

make site

What it does (MariaDB):

  • Waits for DB, sets MariaDB connection in bench config (db_type=db_host=db_port)
  • Creates site: bench new-site --mariadb-user-host-login-scope=% --db-root-password ${DB_PASSWORD} --admin-password ${ADMIN_PASSWORD} ${SITE_NAME}
  • Installs all apps found in sites/apps.txt (skips frappe)
  • Sets the site as default
  1. Verify
make logs

Open your domain via Traefik (e.g. https://erp.corp.3dt.digital).

Troubleshooting tips:

  • If the site was not created, re-run make site and inspect logs for DB connectivity
  • If assets look broken, run: docker compose -f frappe_docker/compose.yaml exec backend bench build
  • If you use external DB/Redis, set DB_HOST/DB_PORT/REDIS_CACHE/REDIS_QUEUE in .env and use make up-traefik (skip Postgres/Redis overrides)

Notes:

  • This flow uses MariaDB (recommended for ERPNext). If you must use Postgres, swap to overrides/compose.postgres.yaml and use the previous site-creator variant.
  • This flow does not modify the frappe_docker submodule; the additional files live at the project root (e.g. compose.traefik.yaml, compose.site-creator.yaml).