Eviworx
Docs

Umgebungsvariablen – Referenz

Vollständige Übersicht aller Umgebungsvariablen für Eviworx. Die meisten Variablen sind direkt im docker-compose.yaml definiert.

⚠️
Secrets MÜSSEN geändert werden!

Alle als "ÄNDERN!" markierten Variablen enthalten Default-Werte die öffentlich bekannt sind. Ändere sie VOR Production-Deployment!

Backend-Service (Node.js API)

Variable Standard-Wert Beschreibung Ändern?
DATABASE_URL postgresql://helpdesk_user:supersecretpassword@db:5432/helpdesk_db PostgreSQL-Verbindungsstring ÄNDERN!
JWT_SECRET your-secret-key-change-in-production JWT-Token-Signierung (mind. 32 Bytes) ÄNDERN!
JWT_SECRET_OLD Nicht gesetzt Alter JWT-Secret für Secret-Rotation (optional) Optional
SHARE_SECRET 2b9f3c... Signiert zeitlich begrenzte öffentliche Freigabe-Links. PFLICHT — Backend startet mit Fatal-Error, wenn nicht gesetzt! PFLICHT!
SHARE_SECRET_OLD Nicht gesetzt Alter Share-Secret für Zero-Downtime-Rotation (optional) Optional
INTERNAL_API_KEY 2f568de4d1d8627d5a5cdca3890b7d8fa9953e6ebd67aad1baa5d399547b2564 Authentifizierung für Worker → Backend (32 Bytes Hex) ÄNDERN!
LICENSE_ENCRYPTION_KEY 46a0bb175f00dadf828a90042bfbb3cada81b385f73162c66059a32035826f0a AES-256-GCM für verschlüsselte License-Keys (GENAU 32 Bytes!) ÄNDERN!
TWO_FACTOR_ENCRYPTION_KEY 8b2e7f4a1c9d3e6f... AES-256-GCM für 2FA-Secrets (separater Key, NICHT gleich JWT_SECRET!) ÄNDERN!
ADMIN_INITIAL_PASSWORD ChangeMeNowXx Initiales Admin-Passwort (NUR beim 1. Start mit leerer DB!) Beim 1. Start!
FRONTEND_URL https://helpdesk.example.com,http://localhost:5173 CORS-erlaubte URLs (Komma-separiert) Anpassen
NODE_ENV production Node-Umgebung (production / development) -
SEED_DATABASE true Führt beim Start das Seeding aus: auf leerer Datenbank System- und Admin-User (admin@company.com), dazu fehlende Standarddaten wie E-Mail-Vorlagen, SLA-Policies und Kategorien. Vorhandenes wird übersprungen. Nach erstem Start auf false setzen! Nach 1. Start!
SESSION_MAX_HOURS 12 Maximale Session-Dauer in Stunden Optional
ACCESS_TOKEN_EXPIRY_MINUTES 60 Access-Token-Gültigkeit in Minuten Optional
REFRESH_TOKEN_EXPIRY_MINUTES 100 Refresh-Token-Gültigkeit in Minuten (Details: Abschnitt „Session & Token-Konfiguration“). Optional
IDLE_TIMEOUT_MINUTES 30 Inaktivitäts-Grenze in Minuten. Der Wert wird dem Client über sessionConfig mitgeteilt und dort durchgesetzt. Optional
COOKIE_SECURE true in Production Secure-Flag der Auth-Cookies. Ohne Angabe entscheidet NODE_ENV; explizit setzen, wenn ein produktives Deployment bewusst über HTTP läuft (internes Netz). Optional
UPLOAD_DIR /app/uploads Container-Pfad für Uploads -
QUARANTINE_DIR /app/quarantine Container-Pfad für infizierte Files — eigenes Docker-Volume, vom uploads-Volume isoliert (ClamAV sieht es nicht). Nur per Env/Deploy setzbar, NICHT in der Admin-UI. -
MAX_FILE_SIZE 104857600 Max. Upload-Größe (100 MB in Bytes) Optional
FILE_UPLOAD_RATE_LIMIT 200 Max. Uploads pro Stunde pro IP (weitere Rate-Limits: siehe eigener Abschnitt unten) Optional
REDIS_URL redis://:PASSWORD@redis:6379 Redis-Verbindung (mit Passwort!) Passwort ändern!
EMAIL_AUTO_CREATE_USER_DAILY_LIMIT 100 Tägliches Limit für Auto-Erstellung von Usern via Inbound-Email Optional
TURNSTILE_SITE_KEY 0x4AAAAAACX7uYnWmiRmC9TB Cloudflare Turnstile Site-Key (Bot-Schutz) Anpassen
TURNSTILE_SECRET_KEY 0x4AAAAAACX7uTWQ8PjpR_gii... Cloudflare Turnstile Secret-Key (Bot-Schutz) ÄNDERN!
ENABLE_FIPS false FIPS-Modus aktivieren (FIPS 140-2 kompatible Algorithmen, keine Zertifizierung). Das Backend prüft beim Start, dass Node.js im FIPS-Modus läuft, und startet sonst nicht. Optional
PBKDF2_ITERATIONS 210000 PBKDF2-SHA512-Iterationen für das Passwort-Hashing Optional
UV_THREADPOOL_SIZE 16 Node.js libuv Threadpool-Größe (für Crypto-Operationen) Optional
VAPID_PUBLIC_KEY BJ-o94UXnEkgBFI4xpEql84... Web-Push Public Key (Optional) Optional
VAPID_PRIVATE_KEY y1-b7G-vcsKsPra1_zsJt3Z... Web-Push Private Key (Optional) Optional
VAPID_SUBJECT mailto:admin@helpdesk.local Web-Push Subject (Email oder URL) Optional
TRUSTED_PROXIES 217.89.98.0/24 CIDR-Ranges externer Reverse Proxies (komma-separiert). Private Netzwerke (172.16/10/192.168) werden automatisch vertraut. Optional
LICENSE_KEY EVI-XXXX-XXXX-XXXX Lizenz-Key (von Eviworx erhalten). Ohne Eintrag startet die Anwendung im Trial-Modus. Optional
LICENSE_SECRET your-license-secret Lizenz-Secret für HMAC-Validierung (von Eviworx erhalten, zusammen mit LICENSE_KEY). Optional
LICENSE_SERVER_URL https://lic.eviworx.com Lizenzserver-URL. Nur ändern für air-gapped Umgebungen mit eigenem Proxy. Optional
SSRF_ALLOWLIST Leer Freigegebene Webhook-Ziele: IPv4/IPv6/CIDR/Hostname (komma-separiert). Gilt beim Speichern eines Webhooks und bei jedem Aufruf (job-worker, workflow-engine). Loopback-, Link-Local- und Cloud-Metadata-Adressen sind nie erlaubt. Optional
SSRF_ALLOWED_PORTS 80,443,8080,8443 Erlaubte Zielports für Webhooks (überschreibt Default). Optional

Hinweis: Firmenname und Application-URL werden für das Backend in der Oberfläche konfiguriert: Admin-Center → System → Allgemein. Den Firmennamen in Report-Exporten setzt der Report-Generator über seine eigene Variable COMPANY_NAME (siehe unten).

Database-Service (PostgreSQL)

Variable Standard-Wert Beschreibung Ändern?
POSTGRES_USER helpdesk_user Haupt-DB-User (Full Access) -
POSTGRES_PASSWORD supersecretpassword Haupt-DB-Passwort (MUSS mit DATABASE_URL übereinstimmen!) ÄNDERN!
POSTGRES_DB helpdesk_db Datenbank-Name -
JOBWORKER_DB_PASSWORD jobworker_secure_password_change_me Passwort für helpdesk_jobworker User (Restricted Access) ÄNDERN!
READONLY_DB_PASSWORD readonly_secure_password_change_me Passwort für helpdesk_readonly User (Read-Only, auch für Report-Generator) ÄNDERN!

Wichtig: Die Passwörter werden beim ersten DB-Start in Init-Scripts verwendet um die restricted Users anzulegen. Bei Änderung nach erstem Start müssen die DB-User manuell aktualisiert werden.

Redis-Service

Variable Standard-Wert Beschreibung Ändern?
REDIS_PASSWORD 7cf9d4a07745c26826a8ad8dc047023decfb95f67acc9d7c Redis-Passwort (verwendet in --requirepass und allen REDIS_URL-Verbindungen) ÄNDERN!

Wichtig: Das REDIS_PASSWORD wird in allen Services als Teil der REDIS_URL verwendet: redis://:PASSWORD@redis:6379. Alle REDIS_URL-Werte müssen das gleiche Passwort enthalten!

Job-Worker-Service (CronJobs & Automation)

Variable Standard-Wert Beschreibung Ändern?
DATABASE_URL postgresql://helpdesk_jobworker:${JOBWORKER_DB_PASSWORD}@db:5432/helpdesk_db Restricted DB-User (nur CronJob-Tabellen) Passwort ändern
REDIS_URL redis://:PASSWORD@redis:6379 Redis-Verbindung (mit Passwort) -
BACKEND_URL http://backend:3000 Internal API für Domain-Daten -
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend-Service übereinstimmen! ÄNDERN!
NODE_ENV production Node-Umgebung -
METRICS_PORT 3001 Interner Port für den Health-Check des Job-Workers -
INSTANCE_ID Auto-generiert Custom Instance-ID für Multi-Instance Setup (optional) Optional

Email-Worker-Service

Variable Standard-Wert Beschreibung Ändern?
REDIS_URL redis://:PASSWORD@redis:6379 Redis für E-Mail-Queue (mit Passwort) -
REDIS_PASSWORD ${REDIS_PASSWORD} Redis-Passwort (muss mit Redis-Service übereinstimmen) -
BACKEND_URL http://backend:3000 Internal API für SMTP-Config & Templates -
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend übereinstimmen ÄNDERN!
LICENSE_ENCRYPTION_KEY 46a0bb175f00dadf... MUSS mit Backend übereinstimmen (für License-Validierung) ÄNDERN!
NODE_ENV production Node-Umgebung -
HEALTH_PORT 3005 Health-Check-Port -
EMAIL_ACCENT_COLOR #3b8f93 Akzentfarbe für E-Mail-Layout Optional
EMAIL_APP_NAME Eviworx App-Name in E-Mail-Header Optional
EMAIL_APP_URL Nicht gesetzt URL für Links im E-Mail-Layout Anpassen
EMAIL_FOOTER_TEXT Eviworx 2026 Footer-Text in E-Mails Optional
EMAIL_LAYOUT_ENABLED true E-Mail-Layout aktivieren/deaktivieren Optional
FRONTEND_URL Nicht gesetzt Frontend-URL für Links in E-Mails Anpassen
EMAIL_INBOUND_RATE_LIMIT_PER_MINUTE 60 Max. eingehende E-Mails pro Minute (DDoS-Schutz) Optional
EMAIL_INBOUND_RATE_LIMIT_PER_SENDER_PER_HOUR 30 Max. eingehende E-Mails pro Absender pro Stunde Optional
EMAIL_INBOUND_MAX_SIZE_MB 25 Max. E-Mail-Größe in MB (verhindert Speicherüberlauf) Optional

Hinweis: SMTP/IMAP-Konfiguration wird NICHT via Environment-Variablen gesetzt, sondern in der UI: Admin-Center → Kommunikation → E-Mail & Postfächer.

Report-Generator-Service

Variable Standard-Wert Beschreibung Ändern?
DATABASE_URL postgresql://helpdesk_readonly:${READONLY_DB_PASSWORD}@db:5432/helpdesk_db Read-Only DB-User (nur SELECT-Rechte) Passwort ändern
REDIS_URL redis://:PASSWORD@redis:6379 Redis für Report-Queue (BullMQ) -
BACKEND_URL http://backend:3000 Internal API -
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend übereinstimmen ÄNDERN!
PORT 3004 Interner HTTP-Port -
COMPANY_NAME Eviworx Software UG Firmenname in Report-Exporten (PDF-Deckblatt, Fußzeile, Dokument-Metadaten). Unabhängig vom Firmennamen im Admin-Center. Anpassen
CSV_DELIMITER ; CSV-Trennzeichen: ";" (deutsch), "," (international), "tab" Optional
NODE_ENV production Node-Umgebung -

Notification-Worker-Service

Variable Standard-Wert Beschreibung Ändern?
REDIS_URL redis://:PASSWORD@redis:6379 Redis für Notification-Queue (mit Passwort) -
REDIS_PASSWORD ${REDIS_PASSWORD} Redis-Passwort (muss mit Redis-Service übereinstimmen) -
BACKEND_URL http://backend:3000 Internal API für User/Templates -
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend übereinstimmen ÄNDERN!
NODE_ENV production Node-Umgebung -
HEALTH_PORT 3006 Health-Check-Port -
LOG_LEVEL info Log-Level (debug, info, warn, error) Optional

Hinweis: Teams (Bot Framework) und Webex werden in der UI konfiguriert: Admin-Center → Kommunikation → Teams bzw. Webex.

Workflow-Engine-Service

Variable Standard-Wert Beschreibung
REDIS_HOST redis Redis Container-Name
REDIS_PORT 6379 Redis Port
REDIS_PASSWORD ${REDIS_PASSWORD} Redis-Passwort (muss mit Redis-Service übereinstimmen)
REDIS_URL redis://:PASSWORD@redis:6379 Redis-Verbindungsstring (mit Passwort)
BACKEND_URL http://backend:3000 Internal API für Workflow-Daten
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend übereinstimmen
NODE_ENV production Node-Umgebung
PORT 3003 Interner HTTP-Port (Health-Checks)
LOG_LEVEL info Log-Level
SLA_CHECK_INTERVAL_MINUTES 5 SLA-Timer-Check-Intervall (alle 5 Minuten)
RECOVERY_STUCK_THRESHOLD_MINUTES 10 Stuck Workflows nach 10 Minuten neu starten
CIRCUIT_BREAKER_THRESHOLD 5 Circuit Breaker öffnet nach 5 aufeinanderfolgenden Fehlern
CIRCUIT_BREAKER_RESET_MS 30000 Circuit Breaker Reset nach 30 Sekunden

AV-Worker-Service (Virus-Scan-Orchestrator)

Variable Standard-Wert Beschreibung
BACKEND_URL http://backend:3000 Internal API für Attachment-Status-Updates
INTERNAL_API_KEY 2f568de4d1d8627d... MUSS mit Backend übereinstimmen
CLAMAV_HOST clamav ClamAV Container-Name
CLAMAV_PORT 3310 ClamAV TCP-Port (clamd)
HEALTH_PORT 3007 Health-Check-Port
REDIS_URL redis://:PASSWORD@redis:6379 Redis-Verbindung (mit Passwort)
SCAN_POLL_CRON */10 * * * * * Polling-Intervall für neue Files (alle 10 Sekunden)
SCAN_BATCH_SIZE 5 Max. Files pro Batch-Scan
SCAN_TIMEOUT_MS 120000 Scan-Timeout pro File (2 Minuten)
NODE_ENV production Node-Umgebung
NODE_OPTIONS --max-old-space-size=256 Node.js Heap-Limit (256 MB)

ClamAV-Service (Virus-Scanner)

Variable Standard-Wert Beschreibung
FRESHCLAM_DAEMON yes Freshclam Daemon für automatische Signatur-Updates
CLAMD_DAEMON yes ClamAV Daemon (clamd) starten
FRESHCLAM_CHECKS 24 Signatur-Update-Checks pro Tag (alle 1 Stunde)

Traefik-Service (Reverse Proxy)

Traefik benötigt keine Environment-Variablen. Die Konfiguration erfolgt über YAML-Dateien:

Datei Zweck
./traefik/traefik.yml Statische Konfiguration (Entrypoints, Providers, Logging, forwardedHeaders.trustedIPs)
./traefik/dynamic.yml Dynamische Konfiguration (Routers, Services, Middleware, TLS)
./certs/cert.pem SSL-Zertifikat (Fullchain)
./certs/cert.key SSL Private Key

Externer Reverse Proxy: Wenn Eviworx hinter einem externen Reverse Proxy läuft, muss zusätzlich zu TRUSTED_PROXIES (.env) die CIDR-Range des Proxys in traefik/traefik.yml unter forwardedHeaders.trustedIPs für beide Entrypoints (web + websecure) eingetragen werden. Installation → Externer Reverse Proxy

Session & Token-Konfiguration

Session-Timings werden ausschließlich im Backend-Service konfiguriert. Das Frontend erhält die Werte zur Laufzeit vom Server (sessionConfig in der Antwort von Login und /auth/me).

Variable Standard-Wert Beschreibung
SESSION_MAX_HOURS 12 Hartes Session-Ende (12 Stunden). Wird vom Backend durchgesetzt und lässt sich auch per Refresh nicht verlängern.
ACCESS_TOKEN_EXPIRY_MINUTES 60 Access-Token-Gültigkeit (1 Stunde)
REFRESH_TOKEN_EXPIRY_MINUTES 100 Refresh-Token-Gültigkeit. Bewusst knapp: Sie muss nur den Refresh nach einer Inaktivitätsphase abdecken (Token dann ca. 60 min alt). Längere Laufzeiten vergrößern nur die Angriffsfläche bei entwendeten Tokens.
IDLE_TIMEOUT_MINUTES 30 Auto-Logout bei Inaktivität. Wird dem Client über sessionConfig mitgeteilt und dort durchgesetzt.
COOKIE_SECURE (NODE_ENV-abhängig) Wenn gesetzt, bestimmt allein dieser Wert das Secure-Flag aller Auth-Cookies. Ohne Angabe ist es in Production aktiv.

Rate-Limits

Alle Limits sind per ENV überschreibbar und werden beim Start gelesen — ein Wert lässt sich also im Betrieb anheben (ENV setzen + docker compose up -d backend), ohne das Image neu zu bauen. Ungültige oder nicht-positive Werte fallen auf den Default zurück.

Wichtig zu verstehen: Diese IP-Limits sind NICHT die eigentliche Brute-Force-Bremse — die sitzt am Ziel-Objekt (Konto-Sperre pro E-Mail mit exponentieller Verzögerung, CAPTCHA ab 3 Fehlversuchen, 2FA-Limit pro Benutzer, Reset-Limit pro E-Mail). Die IP-Werte sind ein Auffangnetz gegen Massenanfragen und bewusst großzügig gesetzt: Hinter einem Firmen-NAT teilen sich ALLE Mitarbeitenden EINE IP — zu enge Limits sperren dort ganze Büros aus, ohne einen Angreifer aufzuhalten.

Variable Standard Zählt Beschreibung
AUTH_FAIL_PER_PAIR_MAX 5 / 15 min IP + E-Mail Fehl-Logins je Konto UND IP. Ein Kollege mit Tippfehler sperrt damit nur sich selbst — nicht das Büro. Weil Konto und IP gemeinsam gezählt werden, kann niemand fremde Konten gezielt aussperren.
AUTH_FAIL_PER_IP_MAX 30 / 15 min IP Fehl-Logins pro IP über ALLE Konten — Deckel gegen das Durchprobieren vieler E-Mail-Adressen von einer Quelle.
REFRESH_FAIL_PER_IP_MAX 60 / 15 min IP Fehlgeschlagene Token-Erneuerungen pro IP. Refresh-Tokens sind 64 Zeichen lang und nicht erratbar — das Limit ist reiner DoS-Schutz. Bewusst hoch: nach einem Deploy, der alle Sitzungen ungültig macht, laufen alle Clients eines Büros gleichzeitig in einen Fehlschlag.
LOGOUT_PER_IP_MAX 120 / 15 min IP Logout-Aufrufe pro IP — zählt ALLE (der Endpunkt antwortet immer mit 200).
TWOFA_FAIL_PER_IP_MAX 20 / 15 min IP Fehlgeschlagene 2FA-Codes pro IP. Die echte Bremse ist das Versuchslimit pro Benutzer.
FORGOT_PW_PER_IP_MAX 20 / 15 min IP Passwort-vergessen-Anfragen pro IP (pro E-Mail gilt zusätzlich 1 Anfrage / 2 min).
RESET_PW_PER_IP_MAX 20 / 15 min IP Fehlversuche auf Passwort-Reset und Einladungs-Passwort-Setup (Token 64 Zeichen).
OAUTH_CB_PER_IP_MAX 30 / 15 min IP Fehlgeschlagene Entra-ID-Callbacks pro IP (Erfolge zählen nicht).
INVITE_LOOKUP_PER_IP_MAX 30 / 15 min IP Prüfungen von Einladungs-Tokens pro IP — zählt alle (auch gültige). Onboarding mehrerer Neuer aus einem Büro.
PASSWORD_CHANGE_MAX 10 / 15 min Benutzer Passwortwechsel je Benutzer (NICHT je IP) — schützt die CPU vor wiederholtem Passwort-Hashing.
CRITICAL_OPS_RATE_LIMIT_MAX 60 / min IP Schreibende Zugriffe auf Benutzer-/Rollen-/Admin-Endpunkte. Zählt nur Änderungen (POST/PUT/PATCH/DELETE); Lesezugriffe zählen nicht.
GLOBAL_RATE_LIMIT_MAX 2000 / min IP Gesamt-Deckel über /api/*. Faustregel: ein Büro mit 50 aktiven Agenten erzeugt durch Polling und Nachladen gut 1000 Requests/min — der Wert braucht Luft nach oben.
FILE_UPLOAD_RATE_LIMIT 200 / h IP Uploads pro Stunde — ebenfalls pro Büro-IP, bei vielen Anhängen ggf. anheben.

Zähler im Arbeitsspeicher: Die Zähler liegen im Arbeitsspeicher des Backends, ein Neustart setzt sie zurück. Das Backend läuft auf einer Instanz, die Werte oben gelten also genau wie angegeben (siehe Skalierung).

Hinter einem externen Reverse-Proxy zwingend TRUSTED_PROXIES prüfen: Ohne korrekte Client-IP-Auflösung sehen ALLE Requests wie eine einzige IP aus — dann greift jedes IP-Limit sofort für alle.

Secrets generieren - Detaillierte Anleitung

JWT_SECRET (Backend)

# Generate 64-char hex string (no special characters, URL-safe)
openssl rand -hex 32

# Example output:
# 9f4a2c8e1b7d5f3a0e6c9d2b4f8a1e3c7d5b9f2a4e6c8d0b3f5a7e9c1d3b5f7a

# Add to .env:
JWT_SECRET=9f4a2c8e1b7d5f3a0e6c9d2b4f8a1e3c7d5b9f2a4e6c8d0b3f5a7e9c1d3b5f7a

SHARE_SECRET (Backend)

# REQUIRED — without this value the backend will NOT start (fatal error)!
# Signs time-limited public share links.
openssl rand -hex 32

# Add to .env:
SHARE_SECRET=generated_hex_string
# Optional for zero-downtime rotation:
# SHARE_SECRET_OLD=old_hex_string

INTERNAL_API_KEY (Backend + alle Worker)

# Generate 32-byte hex string
openssl rand -hex 32

# Example output:
# a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456

# Enter the SAME key everywhere:
# - Backend-Service: INTERNAL_API_KEY=a1b2c3d4...
# - Job-Worker-Service: INTERNAL_API_KEY=a1b2c3d4...
# - Email-Worker-Service: INTERNAL_API_KEY=a1b2c3d4...
# - Notification-Worker-Service: INTERNAL_API_KEY=a1b2c3d4...
# - Workflow-Engine-Service: INTERNAL_API_KEY=a1b2c3d4...
# - AV-Worker-Service: INTERNAL_API_KEY=a1b2c3d4...
# - Report-Generator-Service: INTERNAL_API_KEY=a1b2c3d4...

LICENSE_ENCRYPTION_KEY (Backend + Email-Worker)

# MUST be exactly 32 bytes (64 hex chars)!
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Example output:
# 1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890

# Add to .env (used automatically in Backend + Email-Worker):
LICENSE_ENCRYPTION_KEY=1a2b3c4d5e6f7890...

TWO_FACTOR_ENCRYPTION_KEY (Backend)

# MUST be exactly 32 bytes (64 hex chars)!
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Separate key (NOT same as JWT_SECRET or LICENSE_ENCRYPTION_KEY!)
- TWO_FACTOR_ENCRYPTION_KEY=your_generated_key_here

REDIS_PASSWORD

# Generate a secure Redis password (64 hex chars, no special characters)
openssl rand -hex 32

# Add to .env:
REDIS_PASSWORD=generated_hex_string

# Used automatically in all REDIS_URL connections:
# redis://:PASSWORD@redis:6379

KRITISCH: Der LICENSE_ENCRYPTION_KEY darf NIEMALS geändert werden nachdem Lizenzen verschlüsselt wurden! Backup des Keys ist PFLICHT!

Datenbank-Passwörter

# Generate 3 different secure passwords (64 hex chars)
openssl rand -hex 32    # → POSTGRES_PASSWORD
openssl rand -hex 32    # → JOBWORKER_DB_PASSWORD
openssl rand -hex 32    # → READONLY_DB_PASSWORD

# Add to .env:
POSTGRES_PASSWORD=generated_password
JOBWORKER_DB_PASSWORD=another_password
READONLY_DB_PASSWORD=third_password

# The DATABASE_URL connections in docker-compose.yaml
# reference these values automatically via ${POSTGRES_PASSWORD} etc.

# report-generator service:
- DATABASE_URL=postgresql://helpdesk_readonly:$PW3@db:5432/helpdesk_db

Erweiterte Variablen

Container-Ressourcen-Limits

Ressourcen-Limits sind in docker-compose.yaml unter deploy.resources.limits definiert:

# Example: ClamAV
clamav:
  deploy:
    resources:
      limits:
        memory: 2G
        cpus: '2.0'
      reservations:
        memory: 512M

Security-Hardening (AV-Worker)

Der AV-Worker ist besonders gehärtet:

av-worker:
  read_only: true              # Read-only Filesystem
  tmpfs:
    - /app/tmp:size=64M        # Temporary writable space
    - /tmp:size=64M
  security_opt:
    - no-new-privileges:true   # No privilege escalation
  cap_drop:
    - ALL                      # All Linux capabilities dropped

Logging

Log-Rotation ist für alle Container konfiguriert:

logging:
  driver: "json-file"
  options:
    max-size: "10m"    # Max 10 MB per log file
    max-file: "3"      # Max 3 log files (total: 30 MB)

Variablen-Validierung

Alle Variablen überprüfen

# Show all environment variables (resolved)
docker compose config

# Check INTERNAL_API_KEY is the same everywhere
docker compose config | grep INTERNAL_API_KEY

# Check DATABASE_URL password match
docker compose config | grep -E "POSTGRES_PASSWORD|DATABASE_URL"

# Check REDIS_PASSWORD is the same everywhere
docker compose config | grep -E "REDIS_PASSWORD|REDIS_URL"

Häufige Fehler

Fehler Symptom Lösung
INTERNAL_API_KEY unterschiedlich Worker können nicht mit Backend kommunizieren (401) Prüfe dass alle Worker denselben Key haben
DB-Passwort-Mismatch Backend kann nicht zur DB verbinden POSTGRES_PASSWORD === DATABASE_URL Passwort
REDIS_PASSWORD-Mismatch Services können nicht zu Redis verbinden (NOAUTH) Prüfe dass REDIS_PASSWORD überall identisch ist
LICENSE_ENCRYPTION_KEY falsche Länge License-Key-Encryption schlägt fehl MUSS genau 32 Bytes (64 Hex-Zeichen) sein
SEED_DATABASE bleibt true Seeding läuft bei jedem Neustart; fehlende Standarddaten (z. B. Kategorien, SLA-Policies) werden neu angelegt Nach erstem Start auf false setzen

Production-Deployment-Checkliste

Kategorie Variable Status
Secrets JWT_SECRET Geändert
SHARE_SECRET Gesetzt (PFLICHT!)
INTERNAL_API_KEY Geändert & überall identisch
LICENSE_ENCRYPTION_KEY Geändert (32 Bytes!) & gesichert
TWO_FACTOR_ENCRYPTION_KEY Geändert (32 Bytes!)
Datenbank & Redis POSTGRES_PASSWORD Geändert
JOBWORKER_DB_PASSWORD Geändert
READONLY_DB_PASSWORD Geändert
REDIS_PASSWORD Geändert & überall identisch
Konfiguration FRONTEND_URL Auf echte Domain gesetzt
TURNSTILE_SITE_KEY / SECRET_KEY Eigene Cloudflare-Keys gesetzt
Post-Install SEED_DATABASE Auf false gesetzt nach erstem Start
Default-User-Passwörter Alle geändert in der UI

Optionale Variablen

JWT Secret Rotation

Für JWT-Secret-Rotation (Zero-Downtime) kannst du JWT_SECRET_OLD nutzen:

# Current secret: JWT_SECRET=old_key_123

# Step 1: Add the new key
- JWT_SECRET=new_key_456
- JWT_SECRET_OLD=old_key_123  # Old key stays valid

# Step 2: Wait 24h (until all old tokens have expired)

# Step 3: Remove JWT_SECRET_OLD
# - JWT_SECRET_OLD=...  # Comment out or delete

VAPID Keys (Web-Push-Notifications)

Wenn du Browser-Push-Notifications nutzen willst:

# 1. Install the web-push CLI
npm install -g web-push

# 2. Generate a VAPID key pair
web-push generate-vapid-keys

# 3. Add to docker-compose.yaml (backend service):
- VAPID_PUBLIC_KEY=BJ-o94UXnEkgBFI4xpEql84...
- VAPID_PRIVATE_KEY=y1-b7G-vcsKsPra1_zsJt3Z...
- VAPID_SUBJECT=mailto:admin@your-domain.com

Custom Instance-ID (Multi-Instance)

# For multi-instance deployments (HA)
# Job-Worker Service:
- INSTANCE_ID=job-worker-prod-1

# Useful for:
# - Monitoring (unique identification)
# - Logging (which instance ran the job)
# - Debugging

Variablen überschreiben via .env-Datei

Du kannst eine .env-Datei im Root-Verzeichnis erstellen, um Variablen zu überschreiben:

# Create .env in the root directory
cat > .env << 'EOF'
# Custom Overrides
FRONTEND_URL=https://your-domain.com
JOBWORKER_DB_PASSWORD=your_secure_password_here
READONLY_DB_PASSWORD=another_secure_password
REDIS_PASSWORD=your_redis_password_here
ADMIN_INITIAL_PASSWORD=your_initial_admin_password
COMPANY_NAME=Your Company Ltd
EOF

# Docker Compose reads .env automatically
docker compose up -d

Sicherheit: Füge .env zur .gitignore hinzu! Niemals Secrets in Git committen.

Variablen zur Laufzeit prüfen

In laufendem Container

# Show backend environment
docker compose exec backend env | grep -E "JWT_SECRET|DATABASE_URL|INTERNAL"

# Job-Worker environment
docker compose exec job-worker env

# Compare all container environments
for service in backend job-worker email-worker notification-worker workflow-engine av-worker report-generator; do
  echo "=== $service ==="
  docker compose exec $service env | grep INTERNAL_API_KEY
done

Vor dem Start (Dry-Run)

# Show the complete resolved config (without starting)
docker compose config

# Show only the backend service config
docker compose config backend

# Validate docker-compose.yaml
docker compose config --quiet && echo "Valid YAML" || echo "Invalid YAML"

Best Practices

1. Secrets niemals hardcoden

  • Secrets direkt in docker-compose.yaml (schlecht für Git) - VERMEIDEN
  • Secrets in .env-Datei (nicht in Git!) - EMPFOHLEN
  • Oder: Secrets eines Orchestrators (z. B. Kubernetes), übergeben als Umgebungsvariablen

2. INTERNAL_API_KEY synchron halten

Der INTERNAL_API_KEY MUSS in allen 7 Services identisch sein:

  • backend
  • job-worker
  • email-worker
  • notification-worker
  • workflow-engine
  • av-worker
  • report-generator

Tipp: Nutze eine .env-Variable:

# In .env:
INTERNAL_API_KEY=your_generated_key_here

# In docker-compose.yaml (all services):
environment:
  - INTERNAL_API_KEY=${INTERNAL_API_KEY}

3. LICENSE_ENCRYPTION_KEY sichern

Der LICENSE_ENCRYPTION_KEY verschlüsselt alle License-Keys in der Datenbank. OHNE diesen Key sind die Lizenzen NICHT entschlüsselbar!

# Backup strategy:
# 1. Store the key in a password manager (1Password, Bitwarden, etc.)
# 2. Offline backup (paper wallet)
# 3. Encrypted backup on a separate server

# NEVER:
# - Store only in docker-compose.yaml
# - Commit to Git
# - Send via email

4. Session-Zeiten setzen

Nur im Backend — das Frontend holt sich die Werte zur Laufzeit vom Server:

# In docker-compose.yaml (backend):
- SESSION_MAX_HOURS=12
- ACCESS_TOKEN_EXPIRY_MINUTES=60
- REFRESH_TOKEN_EXPIRY_MINUTES=100
- IDLE_TIMEOUT_MINUTES=30

# frontend: no session variables needed
Nächste Schritte
← Installation

Zurück zur Installation-Anleitung

Docker Compose Details →

Detaillierte docker-compose.yaml Erklärung