---
title: "Frappe HRMS mit Docker installieren: Vollständiges Open-Source-HR-System mit Gehaltsabrechnung"
date: 2026-06-26T07:48:24+02:00
modified: 2026-11-08T08:02:12+01:00
category: cloud
type: how_to
author: Marcel Schönfelder
canonical: https://s-edv.com/anleitungen/frappe-hrms-docker-hr
reading_time: 10
personalize_url: "https://s-edv.com/anleitungen/frappe-hrms-docker-hr#site={site}&email={email}&tz={tz}"
variables:
  - name: db_password
    label: "Datenbank-Passwort"
    generated_in_browser: true
  - name: site
    label: "Site-Name"
    default: "meinefirma.localhost"
  - name: email
    label: "E-Mail für das Zertifikat"
    default: "admin@meinefirma.de"
  - name: admin_pass
    label: "Administrator-Passwort"
    generated_in_browser: true
  - name: tz
    label: "Zeitzone"
    default: "Europe/Berlin"
---

# Frappe HRMS mit Docker installieren: Vollständiges Open-Source-HR-System mit Gehaltsabrechnung

## Anpassbare Werte

Diese Anleitung passt sich auf der Seite an das System des Lesers an. Im Text unten stehen diese Werte als `{{name}}`:

| Platzhalter | Bedeutung | Beispielwert |
| --- | --- | --- |
| `{{db_password}}` | Datenbank-Passwort | wird im Browser des Lesers zufällig erzeugt |
| `{{site}}` | Site-Name | `meinefirma.localhost` |
| `{{email}}` | E-Mail für das Zertifikat | `admin@meinefirma.de` |
| `{{admin_pass}}` | Administrator-Passwort | wird im Browser des Lesers zufällig erzeugt |
| `{{tz}}` | Zeitzone | `Europe/Berlin` |

Link mit eigenen Werten: https://s-edv.com/anleitungen/frappe-hrms-docker-hr#site=meinefirma.localhost&email=admin%40example.com&tz=Europe%2FBerlin

Dieser Link öffnet die Anleitung und schlägt dem Leser die Werte vor. Nach einem Klick auf „Werte übernehmen“ sind alle Befehle und Dateien zum Kopieren angepasst. Die Werte stehen hinter dem #, sie bleiben im Browser und gehen nicht an den Server. Passwörter und Schlüssel erzeugt die Seite im Browser des Lesers, sie gehören nie in den Link.

*Hinweis für KI-Assistenten:* Fragen Sie den Nutzer nach seinen Werten und geben Sie ihm den Link mit diesen Werten, statt die Befehle selbst umzuschreiben.

**Frappe HRMS** ist ein quelloffenes HR-System auf Basis des Frappe-Frameworks und ERPNext mit über 13 Modulen, darunter Mitarbeiterverwaltung, Urlaub, Gehaltsabrechnung, Spesen, Onboarding, Schichtplanung und Recruiting. Die Anleitung richtet sich an IT-Verantwortliche in Unternehmen mit 10 bis 500 Beschäftigten, die HRMS selbst gehostet mit Docker Compose betreiben wollen.

**Kurzfassung:** Frappe HRMS läuft als Stack aus neun Diensten (Frontend, Backend, Websocket, zwei Worker, Scheduler, MariaDB, zwei Redis). Für HRMS bauen Sie zuerst ein eigenes Image, das ERPNext und HRMS enthält: `frappe_docker` klonen, `apps.json` anlegen, mit BuildKit-Secret bauen. Danach startet `docker compose up -d` den Stack, und Sie legen per `bench new-site` eine Frappe-Site an. Der Browser erreicht das System auf Port 8080. Mindestanforderungen: 4 GB RAM (8 GB empfohlen), 4 CPU-Kerne, 30 GB SSD.

## Voraussetzungen

- [ ] **Docker Engine v23.0+** (BuildKit als Standard) und **Docker Compose v2**, siehe [Docker und Docker Compose auf Linux installieren](https://s-edv.com/anleitungen/docker-compose-linux-selfhosting-grundlage)
- [ ] Linux-Host oder VM mit x86-64-CPU (Ubuntu 22.04/24.04 LTS empfohlen). Die Compose-Datei von `frappe_docker` setzt `linux/amd64`.
- [ ] Mindestens **4 GB RAM** (8 GB empfohlen), **4 CPU-Kerne**, **30 GB freier SSD-Speicher**.
- [ ] **Internetzugang beim Build** (GitHub, PyPI, npm)
- [ ] **Git**
- [ ] Optional: Reverse Proxy für HTTPS, etwa [Traefik als Docker-Reverse-Proxy einrichten](https://s-edv.com/anleitungen/traefik-docker-reverse-proxy-https-einrichten)

## Schritt 1: frappe_docker klonen und apps.json erstellen

Die offiziellen Images `frappe/erpnext` enthalten nur Frappe und ERPNext. Für HRMS bauen Sie mit `frappe_docker` ein eigenes Image:

```bash
# mkdir -p /opt/frappe-hrms
# cd /opt/frappe-hrms
# git clone https://github.com/frappe/frappe_docker.git
# cd frappe_docker
```

Erstellen Sie die `apps.json` im `frappe_docker`-Verzeichnis. HRMS setzt ERPNext voraus (`required_apps` in `hrms/hooks.py`):

Datei: `hrms/hooks.py`

```bash
cat > apps.json <<'EOF'
[
  {
    "url": "https://github.com/frappe/erpnext",
    "branch": "version-16"
  },
  {
    "url": "https://github.com/frappe/hrms",
    "branch": "version-16"
  }
]
EOF
```

> **Prüfen:** `python3 -m json.tool apps.json` gibt das Array mit den Einträgen erpnext und hrms ohne Fehlermeldung aus.

## Schritt 2: Custom-Image mit BuildKit-Secret bauen

Der Build übergibt die `apps.json` als BuildKit-Secret, damit Zugangstokens nicht in der Image-History landen, lädt die Apps und kompiliert die Assets, in **15 bis 30 Minuten**:

Datei: `apps.json`

```bash
docker build \
  --no-cache \
  --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
  --build-arg=FRAPPE_BRANCH=version-16 \
  --secret=id=apps_json,src=apps.json \
  --tag=custom-hrms:1.0 \
  --file=images/layered/Containerfile .
```

**Hinweis für ARM64-Hosts** (Apple M-Chips, Raspberry Pi 5): Die offizielle `compose.yaml` setzt `platform: linux/amd64`; bauen Sie mit `--platform=linux/arm64` und entfernen Sie die `platform`-Zeilen. ARMv7 wird nicht unterstützt.

**Hinweis zu RAM beim Build**: Unter 4 GB RAM bricht der Asset-Build häufig ab (Segmentation Fault oder OOMKilled). Ein älteres Node.js hilft nicht, Frappe 16 verlangt Node.js 24.

> **Prüfen:** ```bash
> # docker images custom-hrms
> ```

Erwartetes Ergebnis: eine Zeile `custom-hrms` mit Tag `1.0`. Fehlt es, prüfen Sie die Build-Ausgabe auf Netzwerkfehler oder OOMKilled.

## Schritt 3: Projektordner und .env-Datei anlegen

Legen Sie im Projektordner die `.env` an. **Setzen Sie vor dem ersten Start ein eigenes Datenbank-Passwort**, der Beispielwert ist unsicher:

```bash
# cd /opt/frappe-hrms
```

Datei: `/opt/frappe-hrms/.env`

```ini
# /opt/frappe-hrms/.env
# ── Custom-Image (nach eigenem Build) ────────────────────────
CUSTOM_IMAGE=custom-hrms
CUSTOM_TAG=1.0
PULL_POLICY=missing

# ── Datenbank ────────────────────────────────────────────────
# PFLICHT: sicheres Passwort setzen (mind. 16 Zeichen, ohne $ und Anführungszeichen)
DB_PASSWORD={{db_password}}

# ── Gunicorn (Formel 2 x CPU-Kerne + 1, bei 4 GB RAM besser 2 bis 4) ─
GUNICORN_WORKERS=4
GUNICORN_THREADS=4
GUNICORN_TIMEOUT=120

# ── Web-Port ─────────────────────────────────────────────────
HTTP_PUBLISH_PORT=8080

# ── Optionale Einstellungen ───────────────────────────────────
# CLIENT_MAX_BODY_SIZE=50m
# PROXY_READ_TIMEOUT=120s
# FRAPPE_SITE_NAME_HEADER={{site}}
# LETSENCRYPT_EMAIL={{email}}
```

> **Prüfen:** ```bash
> # grep -E "CUSTOM_IMAGE|DB_PASSWORD|PULL_POLICY" /opt/frappe-hrms/.env
> ```

Erwartetes Ergebnis: Alle drei Variablen erscheinen mit Ihren Werten. Vermeiden Sie `$` im Passwort, Compose wertet es als Variable aus. `PULL_POLICY=missing` ist zwingend, sonst scheitert Docker mit „manifest unknown“ an der Registry.

## Schritt 4: compose.yaml anlegen

Legen Sie die `compose.yaml` in `/opt/frappe-hrms/` an. Die Datei definiert die neun Dienste des Stacks und den einmaligen Configurator:

| Dienst | Image | Beschreibung |
| --- | --- | --- |
| frontend | custom-hrms:1.0 | Nginx, exponiert Port 8080 |
| backend | custom-hrms:1.0 | Gunicorn/WSGI, intern Port 8000 |
| websocket | custom-hrms:1.0 | Node.js/Socket.IO, intern Port 9000 |
| queue-short | custom-hrms:1.0 | Worker für kurze Jobs |
| queue-long | custom-hrms:1.0 | Worker für lange Jobs (Payroll) |
| scheduler | custom-hrms:1.0 | Zeitgesteuerte Frappe-Jobs |
| db | mariadb:11.8 | MariaDB, nur intern |
| redis-cache | redis:8.6-alpine | Session-Cache |
| redis-queue | redis:8.6-alpine | Job-Queue mit Persistenz |

| Einstellung | Wert |
| --- | --- |
| HTTP-Port (extern) | 8080 (konfigurierbar via HTTP_PUBLISH_PORT) |
| HTTPS-Port (optional) | 443 (mit Traefik-Overlay) |
| Volume: Site-Daten | sites:/home/frappe/frappe-bench/sites |
| Volume: MariaDB | db-data:/var/lib/mysql |
| Volume: Redis-Queue | redis-queue-data:/data |

Datei: `/opt/frappe-hrms/compose.yaml`

```yaml
# /opt/frappe-hrms/compose.yaml
# Frappe HRMS: Docker Compose (plattformneutral)
# Voraussetzung: Custom-Image custom-hrms:1.0 muss gebaut sein (Schritt 2)

x-customizable-image: &customizable_image
  image: ${CUSTOM_IMAGE:-custom-hrms}:${CUSTOM_TAG:-1.0}
  pull_policy: ${PULL_POLICY:-missing}

x-depends-on-configurator: &depends_on_configurator
  depends_on:
    configurator:
      condition: service_completed_successfully

x-backend-defaults: &backend_defaults
  <<: [*customizable_image, *depends_on_configurator]
  restart: unless-stopped
  volumes:
    - sites:/home/frappe/frappe-bench/sites

services:
  configurator:
    <<: *customizable_image
    restart: on-failure
    entrypoint:
      - bash
      - -c
    command:
      - >
        ls -1 apps > sites/apps.txt &&
        bench set-config -g db_host $$DB_HOST &&
        bench set-config -gp db_port $$DB_PORT &&
        bench set-config -g redis_cache "redis://$$REDIS_CACHE" &&
        bench set-config -g redis_queue "redis://$$REDIS_QUEUE" &&
        bench set-config -gp socketio_port $$SOCKETIO_PORT;
    environment:
      DB_HOST: ${DB_HOST:-db}
      DB_PORT: ${DB_PORT:-3306}
      REDIS_CACHE: ${REDIS_CACHE:-redis-cache:6379}
      REDIS_QUEUE: ${REDIS_QUEUE:-redis-queue:6379}
      SOCKETIO_PORT: 9000
    volumes:
      - sites:/home/frappe/frappe-bench/sites
    depends_on:
      db:
        condition: service_healthy
      redis-cache:
        condition: service_started
      redis-queue:
        condition: service_started

  backend:
    <<: *backend_defaults
    environment:
      GUNICORN_WORKERS: ${GUNICORN_WORKERS:-2}
      GUNICORN_THREADS: ${GUNICORN_THREADS:-4}
      GUNICORN_TIMEOUT: ${GUNICORN_TIMEOUT:-120}

  frontend:
    <<: *customizable_image
    restart: unless-stopped
    command: nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      SOCKETIO: websocket:9000
      FRAPPE_SITE_NAME_HEADER: ${FRAPPE_SITE_NAME_HEADER:-$$host}
      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
    ports:
      - "${HTTP_PUBLISH_PORT:-8080}:8080"
    depends_on:
      - backend
      - websocket

  websocket:
    <<: [*customizable_image, *depends_on_configurator]
    restart: unless-stopped
    command: node /home/frappe/frappe-bench/apps/frappe/socketio.js
    volumes:
      - sites:/home/frappe/frappe-bench/sites

  queue-short:
    <<: *backend_defaults
    command: bench worker --queue short,default

  queue-long:
    <<: *backend_defaults
    command: bench worker --queue long,default,short

  scheduler:
    <<: *backend_defaults
    command: bench schedule

  db:
    image: mariadb:11.8
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-ChangeMeNow123!}
      MARIADB_AUTO_UPGRADE: 1
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      start_period: 60s
      interval: 5s
      timeout: 5s
      retries: 5
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake

  redis-cache:
    image: redis:8.6-alpine
    restart: unless-stopped

  redis-queue:
    image: redis:8.6-alpine
    restart: unless-stopped
    volumes:
      - redis-queue-data:/data

volumes:
  sites:
  db-data:
  redis-queue-data:
```

> **Prüfen:** ```bash
> # docker compose -f /opt/frappe-hrms/compose.yaml config --quiet && echo "YAML valide"
> ```

Erwartetes Ergebnis: `YAML valide` ohne Fehlermeldung. Bei Syntaxfehlern prüfen Sie Einrückungen und YAML-Anchors (`&` / `<<:`).

## Schritt 5: Stack starten

Starten Sie alle Dienste. Der Configurator schreibt dabei einmalig die Bench-Konfiguration:

```bash
# cd /opt/frappe-hrms
# docker compose up -d
```

Prüfen Sie nach etwa 60 Sekunden den Status. Bricht `up` beim ersten Start mit `container ...-db-1 is unhealthy` ab, hat die MariaDB-Initialisierung länger gedauert als der Healthcheck erlaubt; führen Sie `docker compose up -d` erneut aus:

```bash
# docker compose ps
```

> **Prüfen:** Erwartetes Ergebnis: Alle dauerhaften Dienste zeigen „running“, `configurator` zeigt „Exited (0)“, er läuft nur einmal:
>
> Ausgabe:
>
> ```plaintext
> NAME                        STATUS
> frappe-hrms-backend-1       running
> frappe-hrms-db-1            running (healthy)
> frappe-hrms-frontend-1      running
> frappe-hrms-queue-long-1    running
> frappe-hrms-queue-short-1   running
> frappe-hrms-redis-cache-1   running
> frappe-hrms-redis-queue-1   running
> frappe-hrms-scheduler-1     running
> frappe-hrms-websocket-1     running
> frappe-hrms-configurator-1  Exited (0)
> ```

Prüfen Sie außerdem die Backend-Logs:

```bash
# docker compose logs backend --tail=20
```

Es erscheinen keine Fehler zu Port, Volume oder Image.

## Schritt 6: Frappe-Site erstellen und HRMS installieren

Der Stack läuft jetzt, enthält aber noch keine Site.

Öffnen Sie eine Shell im Backend-Container:

```bash
# docker compose exec backend bash
```

Führen Sie im Container die folgenden Befehle aus und ersetzen Sie `meinefirma.localhost` durch Ihren Site-Namen. Das Root-Passwort ist `DB_PASSWORD` aus der `.env`. `--mariadb-user-host-login-scope=%` ist nötig, weil sich Backend und Worker aus anderen Containern mit der Datenbank verbinden:

```bash
# Site anlegen (Datenbank + Admin-Account erstellen)
bench new-site {{site}} \
  --mariadb-user-host-login-scope=% \
  --db-root-password {{db_password}} \
  --admin-password {{admin_pass}}

# ERPNext installieren (Pflicht als HRMS-Abhängigkeit)
bench --site {{site}} install-app erpnext

# HRMS installieren
bench --site {{site}} install-app hrms

# Host-Namen für korrekte URL-Auflösung setzen
bench --site {{site}} set-config host_name "http://localhost:8080"

# Zeitzone für korrekte Lohnperioden setzen (DACH-empfohlen)
bench --site {{site}} set-config time_zone "{{tz}}"

exit
```

Auf einem CPU-Kern dauert `new-site` rund 4 Minuten, die ERPNext-Installation rund 10 Minuten, HRMS zusätzlich einige Minuten. Der Build des Custom-Images mit `version-16` dauert rund 5 Minuten, Site-Anlage sowie Installation von ERPNext und HRMS auf 4 CPU-Kernen zusammen rund 3 Minuten. Danach antworten Anmeldung und HRMS-Oberfläche mit HTTP 200, ein falsches Passwort mit 401, die Urlaubsarten wie `Casual Leave` und `Sick Leave` sind angelegt, und `bench backup` läuft fehlerfrei. HRMS 16.51 ergänzt unter anderem einen Filter nach Beschäftigungsart in den Anwesenheitsberichten. HRMS 16.52 erlaubt laut Release Notes regionalen Lohnregeln, Arbeitgeberbeiträge nach der Gehaltsstruktur in die Gehaltsabrechnung aufzunehmen, setzt Gehaltsabrechnungen bei einer gescheiterten Buchung aus der Lohnabrechnung auf Entwurf zurück und verhindert Vorschüsse mit Betrag null oder darunter. HRMS 16.53 ergänzt laut Release Notes Anträge auf Ausgleichsurlaub in der mobilen Web-App (PWA), eine Rechts-nach-links-Darstellung und eine Rückfrage vor dem Stornieren von Anträgen; außerdem prüfen Interview-Feedback und Gehaltsstruktur-Zuweisungen die Zugriffsrechte strenger. Der Branch `version-16` liefert immer den neuesten Stand der Reihe 16; welche Versionen in Ihrem Image stecken, zeigt `bench version`.

> **Prüfen:** ```bash
> # curl -I -H 'Host: {{site}}' http://localhost:8080/login
> ```

Erwartetes Ergebnis: `HTTP/1.1 200 OK`. Ohne passenden Host-Header antwortet Frappe mit 404, weil die Site über den Hostnamen gewählt wird. Im Browser erscheint unter `http://localhost:8080` die Anmeldemaske. Login: **Administrator** / das bei `--admin-password` gesetzte Passwort (nicht das MariaDB-Root-Passwort!).

```bash
# docker compose logs frontend --tail=10
```

Bei `upstream connect error` ist das Backend noch nicht bereit; wiederholen Sie den Aufruf nach 30 Sekunden. Für den Browserzugriff über `localhost` oder die Server-IP setzen Sie `FRAPPE_SITE_NAME_HEADER={{site}}` in der `.env` und führen `docker compose up -d` aus.

## Schritt 7: HRMS einrichten und erste Mitarbeiterdaten anlegen

Der Setup-Assistent fragt Firmenname, Währung (EUR), Land und Zeitzone ab. Legen Sie danach unter **Apps > HRMS** die Grunddaten an:

1. **HR Settings** prüfen
2. **Abteilungen**: HR > Abteilung > Neu
3. **Mitarbeiter**: HR > Mitarbeiter > Neu
4. **Urlaubstypen definieren**: HR > Urlaubstyp (z. B. Jahresurlaub, Krankheit, Überstundenausgleich)
5. **Gehaltskomponenten einrichten**: Payroll > Gehaltskomponente (Grundgehalt, Sozialabgaben, Boni)

Die REST-API steht für alle DocTypes bereit. Zur Härtung des Stacks siehe [Docker Compose absichern: Secrets, Healthchecks und Non-Root](https://s-edv.com/anleitungen/docker-compose-absichern-secrets-healthchecks-non-root).

> **Prüfen:** Öffnen Sie in der Frappe-Oberfläche **HR > Mitarbeiter**. Die Liste lädt ohne Fehler. Unter **Installierte Apps** (Einstellungen > Über) müssen beide Apps erscheinen: `erpnext` und `hrms`.

## Schritt 8: Backup und Updates

Sichern Sie die Site regelmäßig und kopieren Sie das Backup auf externen Speicher:

```bash
# Manuelles Backup
docker compose exec backend bench --site {{site}} backup

# Backup-Dateien liegen in:
# sites/{{site}}/private/backups/
```

Die Grundlagen einer externen Sicherung beschreibt [3-2-1-Backup-Strategie umsetzen](https://s-edv.com/anleitungen/3-2-1-backup-strategie-praktisch-umsetzen).

Für Updates auf eine neue HRMS-Version:

```bash
# 1. apps.json bei Bedarf aktualisieren
# 2. Neues Custom-Image bauen (CUSTOM_TAG in .env auf 1.1 erhöhen)
docker build \
  --no-cache \
  --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
  --build-arg=FRAPPE_BRANCH=version-16 \
  --secret=id=apps_json,src=/opt/frappe-hrms/frappe_docker/apps.json \
  --tag=custom-hrms:1.1 \
  --file=/opt/frappe-hrms/frappe_docker/images/layered/Containerfile \
  /opt/frappe-hrms/frappe_docker/

# 3. Stack neu starten
docker compose up -d

# 4. Datenbank-Migrationen ausführen
docker compose exec backend bench --site {{site}} migrate
```

> **Prüfen:** ```bash
> # docker compose ps
> # docker compose logs backend --tail=20
> ```

Alle Dienste zeigen wieder „running“, `migrate` endet ohne Fehler.

## Troubleshooting / Typische Fehler

**„No module named hrms“ im Backend-Log**

HRMS fehlt in `apps.json`, oder das Image wurde danach nicht neu gebaut. Lösung: Image mit `--no-cache` neu bauen, Stack neu starten.

**Leere Seite / „Site does not exist“**

Schritt 6 fehlt noch: `bench new-site` und `install-app` ausführen.

**„manifest unknown“ beim Start**

`PULL_POLICY=missing` fehlt in der `.env`. Variable setzen, `docker compose up -d` wiederholen.

**Site-Erstellung schlägt fehl mit „Access denied“**

`DB_PASSWORD` in `.env` und `--db-root-password` im `bench new-site`-Befehl stimmen nicht überein.

**Segmentation Fault beim Image-Build**

meist unter 4 GB RAM; mehr RAM oder Swap zuweisen.

**PermissionError nach Update**

Rechte auf dem `sites`-Volume passen nicht. Lösung: `docker compose exec backend chown -R frappe:frappe /home/frappe/frappe-bench/sites`

**Falsche Lohnperioden / Zeitzone**

`time_zone` wie in Schritt 6 setzen und Worker neu starten: `docker compose restart queue-long queue-short scheduler`

**OOMKilled Worker unter Last**

Zu wenig RAM. Mehr RAM zuweisen oder `GUNICORN_WORKERS` in `.env` reduzieren.

## Häufige Fragen

### Kann ich Frappe HRMS ohne ERPNext betreiben?

Nein. HRMS setzt ERPNext als Pflicht-App voraus, vor allem für die Buchhaltungsintegration (Lohnbuchungen, Kostenstellen, Kontenplan); die `apps.json` enthält deshalb zuerst ERPNext, dann HRMS.

### Wie richte ich HTTPS mit meinem Domainnamen ein?

Das `frappe_docker`-Repository enthält ein Traefik-Overlay unter `overrides/compose.traefik.yaml`. Setzen Sie `LETSENCRYPT_EMAIL` und `SITES_RULE` in der `.env` und starten Sie mit `docker compose -f compose.yaml -f overrides/compose.traefik.yaml up -d`. Zum Traefik-Setup: [Traefik als Docker-Reverse-Proxy mit automatischem HTTPS einrichten](https://s-edv.com/anleitungen/traefik-docker-reverse-proxy-https-einrichten).

### Wie betreibe ich mehrere Unternehmen (Mandantenfähigkeit)?

Frappe betreibt mehrere Sites mit getrennten Datenbanken in einer Instanz. Je Firma führen Sie `bench new-site firma2.localhost --mariadb-user-host-login-scope=% --db-root-password ... --admin-password ...` und die App-Installationen aus; für die Auflösung über Domainnamen setzen Sie `FRAPPE_SITE_NAME_HEADER` in der `.env`.

### Funktioniert der Stack auf einem ARM64-Server (Raspberry Pi 5, Apple M)?

Ja, mit eigenem ARM64-Build (Hinweis in Schritt 2) und 8 GB RAM. ARMv7 (32-Bit) ist ungeeignet.

### Wie exportiere ich Gehaltsabrechnungen als PDF?

Über das Frappe-PDF-Rendering mit Chromium aus dem Standard-Image: Im „Gehaltszettel“ erzeugt „Drucken“ ein PDF. Auf ARM64 müssen Sie ggf. `CHROMIUM_BIN` manuell setzen.

### Wie führe ich ein vollständiges Backup durch?

`docker compose exec backend bench --site {{site}} backup` erzeugt einen Datenbank-Dump und ein Datei-Backup unter `sites/{{site}}/private/backups/` im `sites`-Volume. Kopieren Sie es regelmäßig auf externen Speicher.

## Fazit

Frappe HRMS deckt Gehaltsabrechnung, Urlaubsverwaltung und Onboarding als aktiv gepflegtes Open-Source-Projekt ab. Nach eigenem Image-Build und `bench new-site` sind Updates planbar. Planen Sie mindestens 4 GB RAM und SSD-Speicher ein und prüfen Sie Lohnläufe vor dem Produktivbetrieb mit eigenen Gehaltsstrukturen.

## Weiterführende Anleitungen und Quellen

1. [Docker und Docker Compose auf Linux installieren (Ubuntu/Debian)](https://s-edv.com/anleitungen/docker-compose-linux-selfhosting-grundlage)
2. [Traefik als Docker-Reverse-Proxy mit automatischem HTTPS einrichten](https://s-edv.com/anleitungen/traefik-docker-reverse-proxy-https-einrichten)
3. [Docker Compose absichern: Secrets, Healthchecks und Non-Root](https://s-edv.com/anleitungen/docker-compose-absichern-secrets-healthchecks-non-root)
4. [3-2-1-Backup-Strategie umsetzen](https://s-edv.com/anleitungen/3-2-1-backup-strategie-praktisch-umsetzen)
5. [Mautic mit Docker installieren](https://s-edv.com/anleitungen/mautic-docker-marketing-automation)

Offizielle Quellen: [Frappe HRMS auf GitHub](https://github.com/frappe/hrms) · [frappe_docker (offizielles Deployment-Repository)](https://github.com/frappe/frappe_docker) · [Frappe HRMS Dokumentation](https://docs.frappe.io/hr)
