ERPNext mit Docker installieren: Vollständiges Open-Source-ERP für KMU
ERPNext ist mit 35.000+ GitHub-Stars das umfassendste kostenlose ERP-System der Welt. Diese Anleitung zeigt dir, wie du Buchhaltung, Lager, HR, CRM und Fertigung per Docker Compose auf einem beliebigen Linux-Server startest – ohne SAP-Lizenzkosten.

Wer als KMU ein vollständiges ERP-System sucht, stolpert schnell über fünfstellige Lizenzkosten für SAP Business One oder Microsoft Dynamics. ERPNext bietet denselben Funktionsumfang – doppelte Buchführung, Lager, Einkauf und Verkauf, Fertigung, HRMS, CRM, Projektmanagement und POS – als GPL-3.0-lizenzierte Open-Source-Software ohne jegliche Lizenzgebühren. Mit dem offiziellen frappe/frappe_docker-Repository läuft ERPNext auf jedem Linux-Host via Docker Compose, egal ob dedizierter Server, Cloud-VPS oder NAS. Diese Anleitung führt dich durch die komplette Installation auf Basis von ERPNext v16 mit allen neun Microservices, erklärt die Besonderheiten der Frappe-Architektur und zeigt typische Fallstricke, die beim ersten Start beinahe jeden treffen.
Voraussetzungen
- Docker Engine v24.0+ und Docker Compose Plugin v2 installiert –
docker composemit Leerzeichen (nichtdocker-compose). Wer die Basis noch einrichten muss, findet eine vollständige Schritt-für-Schritt-Anleitung unter Docker und Docker Compose auf Linux installieren. - Linux-Host (Ubuntu 22.04/24.04 empfohlen), macOS oder Windows mit Docker Desktop. ARM64 wird offiziell unterstützt.
- Mindestens 4 GB RAM für Tests und Evaluierung; 8 GB RAM und 4 CPU-Kerne für den Produktivbetrieb. Mit weniger als 4 GB RAM stürzen Queue-Worker oder Gunicorn ab (Out-of-Memory, Symptom: 502/504).
- Mindestens 40 GB freier Speicherplatz (Image-Download ca. 2–3 GB, Produktionsempfehlung 160 GB SSD).
- Optionale, aber empfohlene Domain mit DNS für HTTPS – ein vorgelagerter Reverse-Proxy (Traefik oder Nginx Proxy Manager) übernimmt TLS-Terminierung.
Schritt 1: Eckdaten und Architektur verstehen
ERPNext v16 setzt auf eine Microservice-Architektur mit neun Containern. Bevor du startest, lohnt ein Blick auf die Kerngrößen:
| Eigenschaft | Wert |
|---|---|
| Image (ERPNext) | frappe/erpnext:v16.22.0 (Docker Hub) / ghcr.io/frappe/erpnext:v16.22.0 (GHCR) |
| Image (Datenbank) | mariadb:11.8 |
| Image (Cache/Queue) | redis:6.2-alpine (zwei Instanzen) |
| Öffentlicher Port | 8080 → Nginx-Frontend |
| Interne Ports | backend:8000, websocket:9000, db:3306, redis:6379 – alle nur containerseitig |
| Lizenz | GPL-3.0, kostenlos, kein Vendor-Lock-in |
| Architektur | x86_64 und ARM64 (Multi-Arch-Images) |
| Volume | Zweck | Kritisch? |
|---|---|---|
sites | Frappe-Bench-Konfiguration, Site-Daten, Uploads | Ja – Datenverlust bei Löschung |
db-data | MariaDB-Datenbankdateien | Ja – Datenverlust bei Löschung |
redis-queue-data | Persistente Job-Queue für Hintergrundaufgaben | Empfohlen |
logs | Anwendungslogs | Optional |
Die Container configurator und create-site sind Init-Container – sie laufen einmalig durch und beenden sich dann (Exitcode 0). Das ist kein Fehler, sondern das korrekte Verhalten. Alle anderen sieben Container laufen dauerhaft.
Verifizieren: Prüfe Docker und Compose-Version mit docker --version und docker compose version. Du solltest mindestens Docker version 24.x und Docker Compose version v2.x sehen. Weniger als 4 GB freien RAM? Dann zuerst Ressourcen prüfen: free -h.
Schritt 2: Projektordner anlegen
Lege einen dedizierten Ordner für den ERPNext-Stack an. Alle Konfigurationsdateien kommen hier rein:
mkdir -p /opt/erpnext
cd /opt/erpnextDu kannst stattdessen auch ~/erpnext verwenden – wichtig ist nur, dass alle nachfolgenden Dateien im selben Verzeichnis liegen, damit docker compose sie findet.
Verifizieren: ls -la /opt/erpnext zeigt ein leeres Verzeichnis. Kein Fehler = korrekt.
Schritt 3: .env-Datei mit sicheren Passwörtern anlegen
Die .env-Datei steuert alle geheimen und anpassbaren Werte. Lege sie vor dem ersten Start an – nachträgliches Ändern der Passwörter bei laufender Datenbank erfordert zusätzliche Schritte.
# /opt/erpnext/.env
# ERPNext Version – exaktes Image-Tag
ERPNEXT_VERSION=v16.22.0
# MariaDB Root-Passwort (NIEMALS den Default "123" oder "changeme" verwenden!)
DB_PASSWORD=sEhrSicheresDbPasswort42!
# ERPNext Admin-Passwort (für Login als "Administrator" im Browser)
ADMIN_PASSWORD=nochEinSicheresAdminPw!
# Öffentlicher HTTP-Port (Standard: 8080)
HTTP_PUBLISH_PORT=8080
# Optional: Gunicorn-Worker (Faustregel: 2 × CPU-Kerne + 1)
GUNICORN_WORKERS=2
# Optional: Nginx Upload-Limit und Timeout
CLIENT_MAX_BODY_SIZE=50m
PROXY_READ_TIMEOUT=120Vergib unterschiedliche, starke Passwörter für DB_PASSWORD und ADMIN_PASSWORD. Ein einfaches Werkzeug für sichere Passwörter: openssl rand -base64 24. Die Datei gehört auf 600:
chmod 600 /opt/erpnext/.envVerifizieren: cat /opt/erpnext/.env zeigt die Werte. Beide Passwörter sind unterschiedlich und enthalten Sonderzeichen. Dateiberechtigung: ls -la /opt/erpnext/.env → -rw-------.
Schritt 4: compose.yaml anlegen
Die folgende compose.yaml basiert auf der offiziellen pwd.yml aus dem frappe/frappe_docker-Repository und ist für den Standalone-Betrieb auf einem einzelnen Host optimiert. YAML-Anker (&customizable_image, *backend_defaults) reduzieren Wiederholungen deutlich – das ist valides Docker-Compose-v2-Syntax:
# /opt/erpnext/compose.yaml
# ERPNext v16 – Docker Compose (Standalone, plattformneutral)
# Quelle: github.com/frappe/frappe_docker
x-customizable-image: &customizable_image
image: frappe/erpnext:${ERPNEXT_VERSION:-v16.22.0}
pull_policy: always
x-depends-on-configurator: &depends_on_configurator
depends_on:
configurator:
condition: service_completed_successfully
x-backend-defaults: &backend_defaults
<<: [*depends_on_configurator, *customizable_image]
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
networks:
- frappe_network
services:
# ---------- Datenbank ----------
db:
image: mariadb:11.8
restart: unless-stopped
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
retries: 10
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeme_now}
MYSQL_DATABASE: frappe
volumes:
- db-data:/var/lib/mysql
networks:
- frappe_network
# ---------- Redis Cache (flüchtig) ----------
redis-cache:
image: redis:6.2-alpine
restart: unless-stopped
networks:
- frappe_network
# ---------- Redis Queue (persistent) ----------
redis-queue:
image: redis:6.2-alpine
restart: unless-stopped
volumes:
- redis-queue-data:/data
networks:
- frappe_network
# ---------- Konfiguration (einmaliger Init-Container) ----------
configurator:
<<: *customizable_image
restart: "no"
command: >
bash -c "
bench set-config -g db_host db &&
bench set-config -gp db_port 3306 &&
bench set-config -g redis_cache redis://redis-cache:6379 &&
bench set-config -g redis_queue redis://redis-queue:6379 &&
bench set-config -gp socketio_port 9000
"
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
db:
condition: service_healthy
networks:
- frappe_network
# ---------- Site-Erstellung (einmaliger Init-Container) ----------
create-site:
<<: *customizable_image
restart: "no"
command: >
bash -c "
wait-for-it -t 120 db:3306 &&
wait-for-it -t 120 redis-cache:6379 &&
wait-for-it -t 120 redis-queue:6379 &&
export start=$(date +%s) &&
until [[ -n $(grep -hs ^ sites/common_site_config.json | python3 -c \"import sys, json; config = json.load(sys.stdin); print(config.get('db_host', '').strip())\") ]]; do
echo 'Warte auf Konfiguration ...' &&
sleep 5 &&
if (( $(date +%s) - start > 120 )); then echo 'Timeout!' && exit 1; fi;
done &&
echo 'Konfiguration vorhanden, erstelle Site ...' &&
bench new-site --mariadb-root-password ${DB_PASSWORD:-changeme_now} --admin-password ${ADMIN_PASSWORD:-changeme_admin} --no-mariadb-socket frontend &&
bench --site frontend install-app erpnext
"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
configurator:
condition: service_completed_successfully
networks:
- frappe_network
# ---------- Backend (Gunicorn) ----------
backend:
<<: *backend_defaults
restart: unless-stopped
# ---------- WebSocket ----------
websocket:
<<: [*depends_on_configurator, *customizable_image]
restart: unless-stopped
command: node /home/frappe/frappe-bench/apps/frappe/socketio.js
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
networks:
- frappe_network
# ---------- Queue-Worker (kurze Jobs) ----------
queue-short:
<<: *backend_defaults
restart: unless-stopped
command: bench worker --num-workers 2 --queue short,default
# ---------- Queue-Worker (lange Jobs) ----------
queue-long:
<<: *backend_defaults
restart: unless-stopped
command: bench worker --num-workers 1 --queue long,default,short
# ---------- Scheduler ----------
scheduler:
<<: *backend_defaults
restart: unless-stopped
command: bench schedule
# ---------- Frontend (Nginx Reverse Proxy) ----------
frontend:
<<: *customizable_image
restart: unless-stopped
command: nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: frontend
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: ${UPSTREAM_REAL_IP_ADDRESS:-127.0.0.1}
UPSTREAM_REAL_IP_HEADER: ${UPSTREAM_REAL_IP_HEADER:-X-Forwarded-For}
UPSTREAM_REAL_IP_RECURSIVE: ${UPSTREAM_REAL_IP_RECURSIVE:-off}
PROXY_READ_TIMEOUT: ${PROXY_READ_TIMEOUT:-120}
CLIENT_MAX_BODY_SIZE: ${CLIENT_MAX_BODY_SIZE:-50m}
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
ports:
- "${HTTP_PUBLISH_PORT:-8080}:8080"
depends_on:
- websocket
networks:
- frappe_network
volumes:
sites:
logs:
db-data:
redis-queue-data:
networks:
frappe_network:
driver: bridgeWichtig: Der Site-Name frontend (im bench new-site-Befehl) muss mit dem Wert von FRAPPE_SITE_NAME_HEADER im frontend-Container übereinstimmen. Weichen beide ab, erscheint später im Browser „Site frontend not found".
Verifizieren: YAML-Syntax prüfen mit docker compose config – die Ausgabe zeigt die aufgelöste Konfiguration ohne Fehlermeldung. Alle neun Services sollten aufgelistet sein.
Schritt 5: Stack starten und Initialisierung abwarten
Starte alle Container im Hintergrund:
cd /opt/erpnext
docker compose up -dDocker lädt nun alle Images herunter (ca. 2–3 GB, je nach Verbindung 5–15 Minuten) und startet die Container. Anschließend läuft der create-site-Container durch und legt die MariaDB-Datenbank an sowie installiert ERPNext. Diesen Prozess auf keinen Fall abbrechen. Verfolge den Fortschritt live:
docker compose logs -f create-siteTypischer Abschluss nach 5–10 Minuten – du siehst Ausgaben wie:
create-site-1 | Installing frappe...
create-site-1 | Installing erpnext...
create-site-1 | ERPNext has been installed successfully.
create-site-1 exited with code 0Erst wenn create-site mit Exitcode 0 beendet ist, ist die Installation vollständig.
Verifizieren: Alle dauerhaften Dienste müssen laufen:
docker compose psErwartetes Ergebnis (gekürzt):
NAME STATUS
erpnext-db-1 Up (healthy)
erpnext-redis-cache-1 Up
erpnext-redis-queue-1 Up
erpnext-backend-1 Up
erpnext-websocket-1 Up
erpnext-queue-short-1 Up
erpnext-queue-long-1 Up
erpnext-scheduler-1 Up
erpnext-frontend-1 Up
erpnext-configurator-1 Exited (0)
erpnext-create-site-1 Exited (0)Die zwei Init-Container mit Exited (0) sind korrekt – alles andere mit Up ist das Ziel. Zusätzlicher HTTP-Check:
curl -I http://localhost:8080Erwartete Antwort: HTTP/1.1 200 OK oder 302 Found (Redirect auf Login-Seite).
Schritt 6: Ersteinrichtung im Browser
Öffne http://localhost:8080 (oder die Server-IP) im Browser. Der Login-Screen erscheint:
- Benutzername:
Administrator - Passwort: Wert aus
ADMIN_PASSWORDin der.env
Nach dem ersten Login startet automatisch der Setup Wizard. Hier wählst du Land (Deutschland), Sprache, Zeitzone und Währung. Für DACH-Betrieb ist die Auswahl „Germany" wichtig, da ERPNext dann die passenden Steuerkonten (SKR03/SKR04), UStVA-relevante Konfigurationen und DATEV-Export-Optionen vorschlägt. Der Setup-Assistent aktiviert außerdem die benötigten Module – nimm dir 5–10 Minuten dafür.
Prüfe nach dem Wizard, ob ERPNext korrekt installiert ist:
docker compose exec backend bench --site frontend list-appsErwartete Ausgabe:
frappe
erpnextVerifizieren: Das ERPNext-Dashboard ist sichtbar, Module wie „Buchhaltung", „Lager" und „Einkauf" erscheinen in der Navigation. Taucht erpnext bei list-apps nicht auf, installiere es nach: docker compose exec backend bench --site frontend install-app erpnext.
Schritt 7: Backups einrichten
Backups sind Pflicht, bevor der Stack produktiv geht. ERPNext speichert seine Daten in zwei kritischen Volumes: sites (Konfiguration, Uploads) und db-data (MariaDB). Ein einfacher manueller Backup-Befehl:
# Site-Backup inklusive Uploads (landet in sites/frontend/private/backups/)
docker compose exec backend bench --site frontend backup --with-files
# MariaDB-Dump direkt aus dem Container
docker compose exec db mysqldump -uroot -p"${DB_PASSWORD}" frappe > /opt/erpnext/backup_frappe_$(date +%Y%m%d).sqlFür regelmäßige automatisierte Backups – inklusive Offsite-Kopie auf S3 oder Hetzner Storage Box – empfiehlt sich die Kombination aus cron und Restic. Eine vollständige Backup-Strategie beschreibt die Anleitung MySQL & PostgreSQL Backup automatisieren.
Verifizieren: ls -lh sites/frontend/private/backups/ (im Container oder per docker compose exec backend ls /home/frappe/frappe-bench/sites/frontend/private/backups/) zeigt .sql.gz- und .tar-Dateien mit aktuellem Datum.
Schritt 8: Updates durchführen
ERPNext wird aktiv gepflegt (Stand Juni 2026: 1.755 Releases, 58.504 Commits). Updates erfolgen über die .env-Datei:
# 1. Backup erstellen (Pflicht vor jedem Update!)
docker compose exec backend bench --site frontend backup --with-files
# 2. Neue Version in .env setzen, z.B.:
# ERPNEXT_VERSION=v16.23.0
# 3. Neue Images ziehen und Container neu starten
docker compose pull
docker compose up -d
# 4. Datenbankmigrationen ausführen
docker compose exec backend bench --site frontend migrateWichtig: Schritt 4 (bench migrate) darf nicht übersprungen werden – ohne Datenbankmigrationen können nach einem Update Fehlermeldungen oder inkonsistente Daten auftreten.
Verifizieren: docker compose ps zeigt alle Dienste als Up. docker compose logs backend --tail=20 zeigt keine Fehlermeldungen. Im Browser unter Einstellungen → Über ERPNext ist die neue Version sichtbar.
Troubleshooting / Typische Fehler
- „create-site hat nach 10 Minuten noch nicht beendet": Das ist normal bei langsamer CPU oder wenig RAM. Weiter warten und mit
docker compose logs -f create-sitebeobachten. Erst bei expliziter Fehlermeldung (nicht bei Wartemeldungen) handeln. AbbrechenwennExited (1)erscheint; dann Logs lesen. - „Site frontend not found" im Browser: Der Site-Name in
bench new-site ... frontendstimmt nicht mitFRAPPE_SITE_NAME_HEADER: frontendüberein. Beide Werte müssen identisch sein. Prüfen:docker compose exec frontend printenv FRAPPE_SITE_NAME_HEADER. - „Access denied for user 'root'" beim Starten: Das in
.envgesetzteDB_PASSWORDstimmt nicht mit dem tatsächlichen MariaDB-Passwort überein. Passiert, wenn dasdb-data-Volume bereits mit einem anderen Passwort initialisiert wurde. Lösung: Volume löschen (nur möglich wenn keine Daten verloren gehen dürfen) und neu starten:docker compose down -v && docker compose up -d. - 502/504 Bad Gateway: Meistens Out-of-Memory bei weniger als 4 GB RAM.
docker compose logs backend --tail=50zeigtWorker failed to boot. Lösung: RAM erhöhen oderGUNICORN_WORKERS=1in.envsetzen und Container neu starten. - MariaDB startet nicht /
healthcheckschlägt fehl: Die fehlenden Startparameter--character-set-server=utf8mb4und--collation-server=utf8mb4_unicode_civerursachen Zeichensatz-Fehler. In dercompose.yamlsind diese untercommand:enthalten – nicht entfernen. - Nach
docker compose down -vsind alle Daten weg: Das Flag-vlöscht alle benannten Volumes inklusivesitesunddb-data. Für normales Herunterfahren immer nurdocker compose down(ohne-v) verwenden. - Keine Echtzeit-Updates / WebSocket-Fehler hinter Reverse-Proxy: Traefik oder Nginx als vorgelagerter Proxy muss WebSocket-Upgrade-Header korrekt weiterleiten (
Upgrade,Connection). Fehlt dies, funktionieren Live-Benachrichtigungen und Echtzeit-Updates nicht. Symptom: keine Live-Aktualisierungen im Browser. - Custom Apps (z. B. HRMS) lassen sich nicht nachinstallieren: Container sind ephemer –
bench get-appin einen laufenden Container funktioniert nicht dauerhaft. Zusätzliche Frappe-Apps müssen beim Image-Build viaAPPS_JSON_BASE64und dem offiziellenContainerfileaus demfrappe_docker-Repo eingebunden werden.
Häufige Fragen
Kann ich ERPNext auf ARM64 betreiben (Raspberry Pi 5, Apple M-Chip)?
Ja. Alle offiziellen ERPNext-Images werden als Multi-Arch-Images bereitgestellt und unterstützen linux/amd64 sowie linux/arm64. Das offizielle frappe_docker-Repository enthält außerdem einen dedizierten ARM64-Guide mit spezifischen Hinweisen für ARM-Hosts.
Wie führe ich ein Backup durch?
Innerhalb des laufenden Stacks: docker compose exec backend bench --site frontend backup --with-files. Die Backup-Dateien landen im sites-Volume unter sites/frontend/private/backups/. Zusätzlich sollte das db-data-Volume regelmäßig per mysqldump oder Volume-Snapshot gesichert werden.
Wie aktualisiere ich auf eine neue ERPNext-Version?
In der .env-Datei ERPNEXT_VERSION auf die neue Version setzen, dann docker compose pull && docker compose up -d ausführen. Anschließend unbedingt docker compose exec backend bench --site frontend migrate aufrufen. Vorher immer ein Backup erstellen.
Unterstützt ERPNext deutsche Buchhaltungsanforderungen?
ERPNext hat eine (teilweise) deutsche Übersetzung und unterstützt DATEV-Export, UStVA-relevante Steuerkonten sowie SKR03/SKR04-Kontenrahmen. Im Setup-Wizard wähle „Germany" als Land. Vollständige GoBD-Konformität erfordert möglicherweise zusätzliche Konfiguration und sollte mit einem Steuerberater abgestimmt werden.
Kann ich mehrere Sites (Multi-Tenancy) betreiben?
Ja, aber das erfordert die Multi-Bench-Overrides aus dem frappe_docker-Repository (overrides/compose.multi-bench.yaml) und typischerweise einen vorgelagerten Traefik-Proxy. Der hier beschriebene Standalone-Ansatz unterstützt nur eine einzige Site.
Was ist der Unterschied zwischen Docker Hub und GHCR für ERPNext-Images?
Beide Registries (frappe/erpnext auf Docker Hub und ghcr.io/frappe/erpnext auf GitHub Container Registry) sind identisch und werden synchron gepflegt. Das aktuelle README des frappe_docker-Repos empfiehlt ghcr.io als bevorzugte Registry.
Fazit
ERPNext via Docker Compose ist eine ernstzunehmende SAP-Alternative für KMU: GPL-lizenziert, aktiv gepflegt, mit vollständiger Buchhaltung, Lager, CRM, HRMS und Fertigung auf einer Plattform. Die neun Container sind beim ersten Blick einschüchternd, folgen aber einer klaren Logik – zwei Init-Container für die Initialisierung, sieben dauerhafte Dienste für den Betrieb. Die kritischen Punkte sind sichere Passwörter vor dem ersten Start, Geduld beim create-site-Prozess und konsequente Backups bevor die ersten echten Daten eingetragen werden. Für den Produktionsbetrieb ist ein vorgelagerter Reverse-Proxy für HTTPS und mindestens 8 GB RAM unverzichtbar. Wer diese Punkte beachtet, bekommt ein vollständiges ERP-System ohne Vendor-Lock-in und ohne monatliche Lizenzkosten.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren – Grundlage für alle Docker-basierten Selbst-Hosting-Projekte
- Traefik als Docker-Reverse-Proxy mit automatischem HTTPS einrichten – für SSL-Terminierung vor ERPNext
- MySQL & PostgreSQL Backup automatisieren – für regelmäßige MariaDB-Sicherungen
- Docker Compose absichern: Secrets, Healthchecks, Non-Root – Härtung für den Produktivbetrieb
Offizielle Quellen: frappe/frappe_docker auf GitHub (Referenz-Repository), Frappe Docker Dokumentation und ERPNext Dokumentation.