Umgebungsvariablen – Referenz
Vollständige Übersicht aller Umgebungsvariablen für Eviworx. Die meisten Variablen sind direkt im docker-compose.yaml definiert.
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
Zurück zur Installation-Anleitung
Detaillierte docker-compose.yaml Erklärung