19 KiB
LeoCRM Admin Guide
Operations manual for LeoCRM administrators: deployment, backup, restore, environment configuration, monitoring, and troubleshooting.
Table of Contents
- Deployment
- Environment Configuration
- Environment Profiles
- System Dashboard
- Backup
- Restore
- Audit Log
- Trash Cleanup
- Monitoring
- Incident Response
- Troubleshooting
Deployment
Prerequisites
- Docker 24+ and Docker Compose v2
- PostgreSQL 16+ (or use the included Docker container with pgvector)
- Redis 7+ (or use the included Docker container)
- A Coolify instance (for managed deployment) or a VPS with Docker
Docker Compose Deployment (Production)
-
Clone the repository:
git clone <repo-url> leocrm cd leocrm -
Copy and configure environment:
cp .env.example .env # Edit .env — set DATABASE_URL, REDIS_URL, SECRET_KEY, CORS_ORIGINS nano .env -
Start services:
docker compose up -d -
Run database migrations:
docker compose exec api alembic upgrade head -
Create the first admin user:
docker compose exec api python -c \ "from app.services.auth_service import bootstrap_first_user; import asyncio; asyncio.run(bootstrap_first_user('admin@example.com', 'SecurePassword123!', 'Admin'))" -
Verify health:
curl http://localhost:8000/api/v1/health
Coolify Deployment
See deploy-guide.md for detailed Coolify deployment instructions.
Manual Deployment (without Docker)
- Install Python 3.12+ and PostgreSQL 16+
- Create a virtual environment:
python3 -m venv .venv && source .venv/bin/activate - Install dependencies:
pip install -r requirements.txt - Configure
.env(see Environment Configuration) - Run migrations:
alembic upgrade head - Start the server:
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2 - Start the ARQ worker:
arq app.core.jobs.WorkerSettings
Environment Configuration
All configuration is managed via environment variables (loaded from .env).
Required Variables
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL async connection URL | postgresql+asyncpg://user:pass@host:5432/dbname |
REDIS_URL |
Redis connection URL | redis://localhost:6379/0 |
SECRET_KEY |
Secret key for signing (≥32 chars) | Use python3 -c "import secrets; print(secrets.token_urlsafe(48))" |
CORS_ORIGINS |
Comma-separated allowed origins (no wildcards) | https://crm.example.com |
Database Configuration
| Variable | Default | Description |
|---|---|---|
DB_POOL_SIZE |
10 |
Connection pool size |
DB_MAX_OVERFLOW |
20 |
Max overflow connections |
DB_ECHO |
false |
Echo SQL statements (debug only) |
Redis Configuration
| Variable | Default | Description |
|---|---|---|
REDIS_URL |
redis://localhost:6379/0 |
Redis connection URL |
SESSION_TTL_SECONDS |
28800 |
Session lifetime (8 hours) |
SMTP / Email Configuration
| Variable | Default | Description |
|---|---|---|
SMTP_HOST |
localhost |
SMTP server hostname |
SMTP_PORT |
587 |
SMTP server port |
SMTP_USERNAME |
(none) | SMTP username (if auth required) |
SMTP_PASSWORD |
(none) | SMTP password (if auth required) |
SMTP_FROM_EMAIL |
noreply@leocrm.local |
From email address |
SMTP_USE_TLS |
true |
Use TLS for SMTP connection |
Storage Configuration
| Variable | Default | Description |
|---|---|---|
STORAGE_PATH |
/tmp |
File storage path (DMS, uploads) |
Security Configuration
| Variable | Default | Description |
|---|---|---|
BCRYPT_ROUNDS |
12 |
Password hashing cost factor |
SESSION_COOKIE_SECURE |
false |
Set true in production (HTTPS only) |
SESSION_COOKIE_SAMESITE |
strict |
SameSite cookie policy |
SESSION_COOKIE_HTTPONLY |
true |
HttpOnly cookie flag |
PASSWORD_RESET_EXPIRY_HOURS |
1 |
Password reset token lifetime |
Rate Limiting
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_LOGIN_MAX |
5 |
Max login attempts per window |
RATE_LIMIT_LOGIN_WINDOW |
900 |
Login window (seconds, 15 min) |
RATE_LIMIT_GENERAL_MAX |
60 |
General API rate limit |
RATE_LIMIT_GENERAL_WINDOW |
60 |
General window (seconds, 1 min) |
Environment Profiles
LeoCRM supports three environment profiles via the ENVIRONMENT variable.
Development (ENVIRONMENT=development)
- Database: Local PostgreSQL or Docker container
- Redis: Local instance
- Logging: DEBUG level (verbose)
- CORS:
http://localhost:5173,http://localhost:3000 - Session cookie secure:
false - DB Echo:
false(settruefor SQL debugging) - Auto-reload:
uvicorn app.main:app --reload --port 8000
Testing (ENVIRONMENT=testing)
- Database:
leocrm_testdatabase (separate from dev/prod) - Redis: Local instance (flushed between tests)
- Logging: WARNING level (minimal output)
- Test fixtures: Auto-seeded tenants, users, and test data
- Run tests:
pytest -v --tb=short
Production (ENVIRONMENT=production)
- Database: Managed PostgreSQL (e.g., Coolify managed DB)
- Redis: Separate Redis container or managed service
- Logging: INFO level (structured JSON via structlog)
- CORS: Production domain only (e.g.,
https://crm.example.com) - Session cookie secure:
true(HTTPS only) - DB Echo:
false - Workers:
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2 - ARQ Worker:
arq app.core.jobs.WorkerSettings - Secret key: Must be a secure random string (≥32 chars)
System Dashboard
Das System Dashboard ist die zentrale Monitoring-Oberfläche für Administratoren.
Zugriff
- Frontend:
/system-dashboard(Admin-only, Sidebar-Eintrag nur für Admins sichtbar) - API:
GET /api/v1/system/dashboard(Admin-only) - Alerts:
GET /api/v1/system/alerts(Admin-only)
Dashboard-Inhalte
| Bereich | Metriken |
|---|---|
| System Health | Overall status (healthy/degraded/down) |
| Database | Connections, Table Count, DB Size |
| Redis | Connected Clients, Used Memory, Peak Memory, Uptime |
| Worker | Queue Length, Active Workers, Status |
| API Stats | Total Requests, Error Count, Error Rate, Avg Response Time |
| Plugins | Total Discovered, Active Plugins |
| Storage | Disk Usage, File Count, Status |
| Alert Feed | System Messages aus Communication-System |
Alert-Dispatch
Bei Problemen (DB down, Redis down, Worker down, High Error Rate) sendet das System Dashboard automatisch eine System-Message an das Communication-System. Diese erscheint im Alert-Feed und in den Benachrichtigungen der Admins.
# Dashboard abfragen
curl -b "leocrm_session=<session>" https://crm.media-on.de/api/v1/system/dashboard | jq .
# Aktive Alerts abfragen
curl -b "leocrm_session=<session>" https://crm.media-on.de/api/v1/system/alerts | jq .
Siehe auch monitoring.md für Details zu Health Endpoints und Metrics.
Backup
Backup-Konfiguration (Settings → Backup)
Die Backup-Konfiguration erfolgt über die System-Settings API oder das Settings-UI.
| Einstellung | Default | Beschreibung |
|---|---|---|
backup_enabled |
false |
Backup Automation aktivieren |
backup_interval |
24h |
Backup-Intervall (Cron-Schedule) |
backup_retention_days |
7 |
Aufbewahrung in Tagen |
backup_destination |
local |
Backup-Ziel (local, s3, nextcloud) |
API-Endpunkte
| Method | Path | Beschreibung |
|---|---|---|
| GET | /api/v1/system-settings/backup-config |
Backup-Konfiguration abfragen |
| PUT | /api/v1/system-settings/backup-config |
Backup-Konfiguration aktualisieren |
| POST | /api/v1/system-settings/backup-now |
Sofortiges Backup auslösen (ARQ-Job) |
| GET | /api/v1/system-settings/backup-history |
Letzte 10 Backup-Ergebnisse (Audit Log) |
# Backup-Konfiguration abfragen
curl -b "leocrm_session=<session>" https://crm.media-on.de/api/v1/system-settings/backup-config | jq .
# Backup aktivieren und konfigurieren
curl -X PUT -b "leocrm_session=<session>" \
-H "Content-Type: application/json" \
-d '{"backup_enabled": true, "backup_interval": "24h", "backup_retention_days": 7, "backup_destination": "local"}' \
https://crm.media-on.de/api/v1/system-settings/backup-config
# Sofortiges Backup auslösen
curl -X POST -b "leocrm_session=<session>" https://crm.media-on.de/api/v1/system-settings/backup-now
# Backup-Historie abfragen
curl -b "leocrm_session=<session>" https://crm.media-on.de/api/v1/system-settings/backup-history | jq .
Automated Backup (ARQ Cron-Job)
- Job:
auto_backup_job(täglich 03:00 Uhr, einstellbar) - Script:
scripts/backup.py(pg_dump + files) - Bei Fehler: System-Message an Communication-System + Audit Log Eintrag
- Bei Erfolg: Audit Log Eintrag (
backup_success)
Manual Database Backup
# Full database dump (recommended daily)
pg_dump -U leocrm -h localhost leocrm > backup_$(date +%Y%m%d).sql
# Compressed backup
pg_dump -U leocrm -h localhost leocrm | gzip > backup_$(date +%Y%m%d).sql.gz
File Storage Backup
# Backup the storage directory
tar -czf storage_$(date +%Y%m%d).tar.gz /data/storage/
Redis Backup
# Save Redis snapshot
redis-cli SAVE
cp /var/lib/redis/dump.rdb /backups/redis_$(date +%Y%m%d).rdb
Restore
Database Restore
# Restore from SQL dump
psql -U leocrm -h localhost leocrm < backup_20260101.sql
# Restore from compressed backup
gunzip -c backup_20260101.sql.gz | psql -U leocrm -h localhost leocrm
File Storage Restore
# Restore storage directory
tar -xzf storage_20260101.tar.gz -C /data/
Redis Restore
# Stop Redis, replace dump file, start Redis
systemctl stop redis
cp /backups/redis_20260101.rdb /var/lib/redis/dump.rdb
systemctl start redis
See scripts/restore.py for the automated restore solution and scripts/restore_test.sh for restore testing.
Audit Log
Audit Log Export (CSV/JSON)
Audit-Logs können als CSV oder JSON exportiert werden. Der Export erfolgt als Streaming-Response (max. 10.000 Einträge).
# CSV Export
curl -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/audit-log/export?format=csv" \
-o audit_log_export.csv
# JSON Export
curl -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/audit-log/export?format=json" \
-o audit_log_export.json
# Gefilterter Export (nach Entity-Type und Datum)
curl -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/audit-log/export?format=csv&entity_type=contact&date_from=2026-01-01&date_to=2026-12-31" \
-o audit_log_contacts_2026.csv
Audit Log Retention (365 Tage)
Audit-Logs werden standardmäßig nach 365 Tagen archiviert/gelöscht. Die Retention ist einstellbar.
- Default: 365 Tage
- ARQ-Cron-Job:
audit_retention_cleanup(täglich 04:00 Uhr) - API:
DELETE /api/v1/audit-log/retention?retention_days=365
# Manuelle Retention-Bereinigung (Admin-only)
curl -X DELETE -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/audit-log/retention?retention_days=365"
# → {"deleted": 1234, "retention_days": 365, "cutoff": "2025-08-20T00:00:00"}
Audit Log Abfrage
# Audit-Logs abfragen (mit Filter und Pagination)
curl -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/audit-log?entity_type=contact&action=create&page=1&page_size=50" | jq .
Filter-Parameter:
entity_type— Filter nach Entity-Typuser_id— Filter nach User-IDaction— Filter nach Action (create/update/delete/login)date_from— ISO Datum-Start (inklusive)date_to— ISO Datum-Ende (inklusive)page/page_size— Pagination (max. 200 pro Seite)
Tamper-Proof
- DELETE auf AuditLog nur mit
?gdpr=true+ Admin-Berechtigung - Audit-Logs können nicht modifiziert werden (nur erstellen und lesen)
Trash Cleanup
Soft-deleted Entitäten werden nach Ablauf der Retention-Periode endgültig gelöscht.
- Default: 90 Tage (
trash_retention_days) - ARQ-Cron-Job:
cleanup_expired_trash(täglich 05:00 Uhr) - Kriterium:
deleted_at < now() - retention_days - Bei Löschung: Audit-Log Eintrag
Konfiguration
Die Retention-Periode ist über die System-Settings einstellbar:
# Trash-Retention konfigurieren
curl -X PUT -b "leocrm_session=<session>" \
-H "Content-Type: application/json" \
-d '{"trash_retention_days": 90}' \
https://crm.media-on.de/api/v1/system-settings
Hard-Delete (GDPR)
Einzelne Entitäten können sofort endgültig gelöscht werden (Admin-only):
# Hard-Delete mit GDPR-Flag
curl -X DELETE -b "leocrm_session=<session>" \
"https://crm.media-on.de/api/v1/contacts/{id}?gdpr=true"
Monitoring
Health Endpoints
# Liveness
curl http://localhost:8000/health/live
# → {"status":"alive"}
# Readiness
curl http://localhost:8000/health/ready
# → {"status":"ready","checks":{"database":"ok","redis":"ok","storage":"ok"}}
# Full health
curl http://localhost:8000/api/v1/health
# → {"status":"healthy","version":"1.0.0","checks":{...}}
Returns JSON with overall status and individual checks:
database— PostgreSQL connectivityredis— Redis connectivitystorage— Storage directory writabilityworker— ARQ worker queue status
Status values: healthy (all checks up) or degraded (one or more checks down).
Prometheus Metrics
# Requires admin authentication
curl -H "Cookie: leocrm_session=<session_id>" http://localhost:8000/api/v1/metrics
Available metrics:
leocrm_http_requests_total— Total HTTP requests (by method, path, status)leocrm_http_request_duration_seconds— Request duration histogramleocrm_db_pool_connections— Database connection pool sizeleocrm_arq_jobs_total— Total ARQ background jobs (by function, status)
Structured Logging
LeoCRM uses structlog for structured JSON logging. Logs include:
timestamp— ISO timestamplevel— Log level (INFO, ERROR, etc.)event— Event name (e.g.,api_request)method— HTTP methodpath— Request pathstatus— HTTP status codeduration_ms— Request duration in millisecondstenant_id— Tenant identifier (when available)
Error logs additionally include:
error— Error messagetraceback— Full stacktrace
Performance Testing
# Seed 200k contacts for performance testing
python scripts/seed_perf_data.py --count 200000
# Verify database indexes
python scripts/check_indexes.py
Siehe monitoring.md für vollständige Monitoring-Dokumentation.
Incident Response
Siehe docs/incident-response-runbook.md für detaillierte Notfall-Prozeduren.
Schnell-Referenz
| Incident | Erste Maßnahme | Eskalation |
|---|---|---|
| Server-Ausfall | Coolify Restart → Health-Check → System-Message | Hetzner Support |
| DB-Crash | PostgreSQL Restart → Migration-Check → Backup-Restore | Coolify DB Restart |
| Redis-Crash | Redis Restart → Session-Check | Coolify Service Restart |
| Security-Breach | Logs prüfen → Password-Reset → Audit-Log Export | Incident Response Team |
| Backup Failed | Backup-Log prüfen → Manueller Retry → Storage prüfen | Admin |
| Worker Down | Worker-Container Restart → Queue prüfen | Coolify Service Restart |
Incident Response Schritte
- Erkennen — Alert im System Dashboard oder externes Monitoring
- Eingrenzen — Health-Checks, Logs, Metrics prüfen
- Beheben — Restart, Restore, Konfiguration anpassen
- Verifizieren — Health-Check grün, System Dashboard ok
- Dokumentieren — Audit Log Eintrag, Post-Mortem bei Critical
Troubleshooting
Database Connection Issues
Symptom: Health check reports database: down
- Verify PostgreSQL is running:
systemctl status postgresql - Check connection string in
.env:DATABASE_URL=postgresql+asyncpg://... - Test connection:
psql -U leocrm -h localhost leocrm - Check pool settings:
DB_POOL_SIZEandDB_MAX_OVERFLOW
Redis Connection Issues
Symptom: Health check reports redis: down, sessions not persisting
- Verify Redis is running:
systemctl status redisorredis-cli ping - Check
REDIS_URLin.env - Check Redis logs for errors
Storage Issues
Symptom: Health check reports storage: down, file uploads failing
- Verify storage path exists:
ls -la $STORAGE_PATH - Check write permissions:
touch $STORAGE_PATH/test && rm $STORAGE_PATH/test - Update
STORAGE_PATHin.envif needed
Worker Issues
Symptom: Background jobs not processing, health check reports worker: down
- Verify ARQ worker is running:
ps aux | grep arq - Start worker:
arq app.core.jobs.WorkerSettings - Check Redis queue:
redis-cli ZCARD arq:queue - Check System Dashboard:
GET /api/v1/system/dashboard→.worker
Performance Issues
Symptom: Slow API responses (>500ms)
- Run
python scripts/check_indexes.py— verify all indexes exist - Check database pool size: increase
DB_POOL_SIZEif needed - Seed test data:
python scripts/seed_perf_data.py --count 10000 - Check Prometheus metrics at
/api/v1/metricsfor slow endpoints - Enable SQL echo (temporarily): set
DB_ECHO=truein.env
Authentication Issues
Symptom: Login fails, 401 errors
- Verify user exists and is active
- Check session cookie name:
SESSION_COOKIE_NAMEin.env - Verify
SESSION_COOKIE_SECUREisfalsein development (no HTTPS) - Check Redis for session data:
redis-cli KEYS session:*
CORS Errors
Symptom: Browser console shows CORS errors
- Check
CORS_ORIGINSin.env— must include frontend URL - No wildcards allowed — explicit origins only
- Restart server after changing
.env
Migration Issues
Symptom: alembic upgrade head fails
- Check database connectivity
- Verify Alembic config:
alembic.ini - Review migration files in
alembic/versions/ - Check current revision:
alembic current - Reset (DESTRUCTIVE):
alembic downgrade base && alembic upgrade head
Backup Issues
Symptom: Backup job fails, no backup history
- Check System Dashboard for backup alerts
- Check Audit Log for
backup_failedentries:GET /api/v1/audit-log?action=backup_failed - Verify storage path has enough disk space
- Try manual backup:
POST /api/v1/system-settings/backup-now - Check
scripts/backup.pylogs