ERPNextΒΆ
DescriptionΒΆ
ERPNext is an open-source ERP suite built on the Frappe Framework. It covers finance, CRM, inventory, manufacturing, HR, sales, projects, and websites in one self-hosted stack.
OverviewΒΆ
This role deploys ERPNext as an Infinito.Nexus web app using the upstream frappe/erpnext single-image multi-role pattern from frappe_docker: one Docker image, different commands per container (backend gunicorn, frontend nginx, websocket socketio, scheduler, two queue workers, plus a one-shot configurator). MariaDB and Redis come from the central svc-db-mariadb and svc-db-redis providers β the three Frappe Redis logical roles (cache, queue, socketio) share one central Redis instance via DB-number split (0 / 1 / 2). Authentication uses Frappeβs built-in Social Login Key against the shared Keycloak OIDC client without an oauth2-proxy sidecar; LDAP federation and outbound SMTP via Stalwart are wired when their providers are present.
CosmosΒΆ
The diagram places ERPNext in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).
flowchart LR
subgraph deps [Dependencies]
dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local π»"]
dep_svc_db_mariadb["svc-db-mariadb π³π"]
dep_svc_db_openldap["svc-db-openldap π³π"]
dep_svc_db_redis["svc-db-redis π³π"]
dep_svc_net_tor["svc-net-tor π³π"]
dep_web_app_dashboard["web-app-dashboard π³π"]
dep_web_app_keycloak["web-app-keycloak π³π"]
dep_web_app_matomo["web-app-matomo π³π"]
dep_web_app_prometheus["web-app-prometheus π³π"]
dep_web_app_stalwart["web-app-stalwart π³π"]
dep_web_svc_css["web-svc-css π»"]
dep_web_svc_logout["web-svc-logout π³π"]
end
subgraph role [web-app-erpnext π³π]
svc_sso["sso"]
svc_ldap["ldap"]
svc_email["email"]
svc_logout["logout"]
svc_dashboard["dashboard"]
svc_matomo["matomo"]
svc_prometheus["prometheus"]
svc_tor["tor"]
svc_css["css"]
svc_mariadb["mariadb"]
svc_redis["redis"]
svc_erpnext["erpnext"]
svc_backend["backend"]
svc_frontend["frontend"]
svc_websocket["websocket"]
svc_scheduler["scheduler"]
svc_queue_short["queue-short"]
svc_queue_long["queue-long"]
svc_configurator["configurator"]
svc_container_backup["container_backup"]
end
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
dep_svc_db_mariadb -. "0..1" .-> svc_mariadb
dep_svc_db_openldap -. "0..1" .-> svc_ldap
dep_svc_db_redis -. "0..1" .-> svc_redis
dep_svc_net_tor -. "0..1" .-> svc_tor
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
dep_web_app_keycloak -. "0..1" .-> svc_sso
dep_web_app_matomo -. "0..1" .-> svc_matomo
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
dep_web_app_stalwart -. "0..1" .-> svc_email
dep_web_svc_css -. "0..1" .-> svc_css
dep_web_svc_logout -. "0..1" .-> svc_logout
Solid 1:1 edges are fixed relationships; dashed 0..1 edges are conditional (enabled only in matching deployments); red 0..0 edges are turned off in this role. Node markers show the roleβs deploy modes (π» host, π³ compose, π swarm); β marks a service that is explicitly turned off, and βοΈ an Ansible role dependency declared in meta/main.yml.
FeaturesΒΆ
ERP / CRM / inventory desk β full ERPNext frontend at
next.erp.{{ DOMAIN_PRIMARY }}.Direct OIDC SSO β Frappeβs Social Login Key talks to the shared Keycloak client; redirect URI auto-registered via the
redirect_urisfilter (no per-app Keycloak entry needed).Keycloak group β Frappe role mapping β three tiers:
roles/web-app-erpnext/administratorβSystem Manager,roles/web-app-erpnext/managerβ{Sales, Purchase, Stock, Accounts} User, default βCustomer. Reconciled on each login by a hook reading the persisted map.LDAP federation β when
svc-db-openldapis present, Frappeβs LDAP Settings doctype is auto-configured. End-to-end LDAP login (auto-create Frappe user from LDAP bind) is deferred to a follow-up requirement; v1 ships the Settings doctype only. The LDAP-login Playwright specs are intentionally absent for v1.Outbound mail (v1) β when
web-app-stalwartis present, Frappeβs outbound Email Account is auto-configured against the central SMTP endpoint. IMAP inbound (mail-to-Communication) is deferred to a follow-up requirement (no precedent in the repo for auto-provisioning role-owned Stalwart mailboxes yet).Wizard bypass β first deploy seeds the API-bot user, marks
setup_complete=1in System Settings, and skips the Frappe setup wizard entirely.
Quick SetupΒΆ
DevelopmentΒΆ
Clone, set up the workstation, and deploy ERPNext onto the local stack:
git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-app-erpnext full_cycle=false
ProductionΒΆ
Run the published image to provision the inventory and deploy ERPNext to a managed server (the mounted volume persists the inventory):
APP=web-app-erpnext
HOST=<your-server>
DOMAIN=<your-domain>
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"
docker run --rm -it \
-v "$PWD/inventories:/etc/infinito.nexus/inventories" \
-e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
ghcr.io/infinito-nexus/core/debian bash -c '
INVENTORY=/etc/infinito.nexus/inventories/production
infinito administration inventory provision "$INVENTORY" \
--inventory-file "$INVENTORY/devices.yml" \
--host "$HOST" \
--include "$APP" \
--vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
infinito administration deploy dedicated "$INVENTORY/devices.yml" \
--password-file "$INVENTORY/.password" \
--diff -vv'
Image & version policyΒΆ
Image:
frappe/erpnext(the official single-image, multi-role build from frappe_docker).Pin: a concrete stable v15.x semver (no
:latest, no:edge, no v14 LTS, no v16 pre-release line).Bump path: update
services.erpnext.versioninmeta/services.yml, redeploy. Schema migrations run automatically on first start of the new image (Frappe shipsbench migratein the backend containerβs entrypoint).
Central-service consumer patternΒΆ
Frappe role |
Provided by |
Notes |
|---|---|---|
MariaDB primary DB |
|
|
Redis |
|
URL |
Redis |
|
URL |
Redis |
|
URL |
The DB-number split is stable for v1. If svc-db-redis later partitions tenants differently, this role gets swept by the same migration.
VariantsΒΆ
meta/variants.yml defines three variants (mirrors the web-app-kix / web-app-zammad pattern):
# |
SSO |
LDAP |
Use |
|
|---|---|---|---|---|
V1 |
β |
β |
β |
Full stack β everything that can be true, is true. |
V2 |
β |
β |
β |
No auth β bare ERPNext, local-user only (break-glass / dev). |
V3 |
β |
β |
β |
LDAP-only β sign in against the central OpenLDAP. |
Backup & restore (operator command, v1)ΒΆ
A svc-bkp-* pre-backup hook for bench backup is NOT wired in v1 (deferred to a follow-up requirement). The v1 backup story relies on the standard svc-bkp-* driver picking up the central MariaDB DB and the roleβs named volumes. To make a Frappe-aware snapshot in flight, run:
make compose-exec service=erpnext-backend \
cmd="bench --site next.erp.<DOMAIN_PRIMARY> backup --with-files"
The artefacts (*.sql.gz, *-files.tar, *-private-files.tar) land under /home/frappe/frappe-bench/sites/next.erp.<DOMAIN_PRIMARY>/private/backups/ inside the backend container.
Restore is the inverse:
make compose-exec service=erpnext-backend \
cmd="bench --site next.erp.<DOMAIN_PRIMARY> restore <path-to-sql-gz> --with-public-files <files.tar> --with-private-files <private-files.tar>"
Developer NotesΒΆ
Variant matrix lives in variants.yml. Service flags and image pins in services.yml. Credentials declared in secrets.yml.
Site name MUST match the HTTP Host header. The Frappe site is created with
bench new-site next.erp.<DOMAIN_PRIMARY>andFRAPPE_SITE_NAME_HEADERon the frontend nginx maps Host β site.All post-bootstrap configuration (OIDC Social Login Key, LDAP Settings, outbound Email Account, group-role mapping) runs as Python scripts piped to
pythoninside the backend container (seefiles/scripts/).
Further ResourcesΒΆ
CreditsΒΆ
Implemented by Kevin Veen-Birkenbach. Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).