Radicale: Selbstgehosteter CalDAV & CardDAV Server
Kalender und Kontakte gehören zu den persönlichsten Daten, die wir täglich nutzen. Termine, Geburtstage, Telefonnummern, E-Mail-Adressen – all das liegt bei den meisten Menschen auf den Servern von Google, Apple oder Microsoft. Wer die Kontrolle über diese Daten behalten möchte, braucht eine selbstgehostete Alternative.
Radicale ist ein leichtgewichtiger CalDAV- und CardDAV-Server, der genau das ermöglicht: Kalender und Kontakte auf dem eigenen Server hosten und über standardisierte Protokolle mit allen gängigen Clients synchronisieren. Der Server ist in Python geschrieben, braucht keine Datenbank und lässt sich in wenigen Minuten aufsetzen.
In diesem Post zeige ich dir, wie du Radicale mit Docker installierst, konfigurierst und mit deinen Geräten verbindest.
Was ist Radicale?
Radicale ist ein Open-Source CalDAV/CardDAV-Server, der seit 2010 entwickelt wird. Im Gegensatz zu Schwergewichten wie Nextcloud oder Baikal setzt Radicale auf Minimalismus:
- Python-basiert – keine Abhängigkeiten zu PHP, MySQL oder anderen Diensten
- Dateibasierter Storage – Collections werden als einzelne Dateien gespeichert, kein Datenbank-Setup nötig
- Geringe Ressourcen – läuft problemlos mit 256 MB RAM
- Standardkonform – vollständige Implementierung von CalDAV (RFC 4791) und CardDAV (RFC 6352)
Der Quellcode ist auf GitHub verfügbar.
Wie funktioniert CalDAV/CardDAV?
CalDAV und CardDAV sind Erweiterungen des WebDAV-Protokolls (HTTP-basiert). Clients kommunizieren direkt über HTTPS mit dem Server und synchronisieren dabei nur die Änderungen:
flowchart LR
A["📱 Smartphone<br/>(DAVx⁵)"] -->|CalDAV/CardDAV| R["🗄️ Radicale<br/>:5232"]
B["🖥️ Thunderbird"] -->|CalDAV/CardDAV| R
C["🍎 iOS<br/>Kalender/Kontakte"] -->|CalDAV/CardDAV| R
R -->|Dateisystem| S["📁 /data/collections"]
| Protokoll | Zweck | RFC |
|---|---|---|
| CalDAV | Kalender-Synchronisation (Events, Todos) | RFC 4791 |
| CardDAV | Kontakte-Synchronisation (vCards) | RFC 6352 |
| WebDAV | Basis-Protokoll (HTTP-Erweiterung) | RFC 4918 |
Features im Überblick
| Feature | Beschreibung |
|---|---|
| Kalender | Mehrere Kalender pro Benutzer, wiederkehrende Termine, Erinnerungen |
| Kontakte | Adressbücher mit vCard 3.0/4.0 Support |
| Multi-User | Getrennte Collections pro Benutzer mit htpasswd-Auth |
| Web-Interface | Integriertes Web-UI zum Erstellen/Löschen von Kalendern |
| Rechte-System | Feingranulare Zugriffsrechte auf Collections |
| Keine Datenbank | Alles wird als Dateien im Filesystem gespeichert |
| Versionierung | Git-basierte Historie der Änderungen (optional) |
| HTTPS | Native TLS-Unterstützung oder via Reverse Proxy |
Installation mit Docker
Die empfohlene Methode ist Docker Compose mit dem offiziellen Image:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
services:
radicale:
image: ghcr.io/kozea/radicale:latest
container_name: radicale
restart: unless-stopped
command: ["--config", "/config/config"]
ports:
- "192.168.60.166:5232:5232"
volumes:
- ./data:/data
- ./config:/config:ro
environment:
- TZ=Europe/Berlin
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- KILL
mem_limit: 256m
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5232"]
interval: 30s
retries: 3
htpasswd-manager:
build: ./htpasswd-manager
container_name: htpasswd-manager
restart: unless-stopped
ports:
- "192.168.60.166:5280:5000"
volumes:
- ./config:/config
environment:
- TZ=Europe/Berlin
- HTPASSWD_FILE=/config/users
mem_limit: 64m
Verzeichnisstruktur
1
2
3
4
5
6
7
8
9
10
/opt/radicale/
├── config/
│ ├── config # Radicale-Konfiguration
│ └── users # htpasswd-Datei
├── data/
│ └── collections/ # Kalender & Kontakte
├── htpasswd-manager/
│ ├── app.py # Web-UI für Benutzerverwaltung
│ └── Dockerfile
└── docker-compose.yml
Security-Hardening
Der Container ist bewusst gehärtet:
read_only: true– Dateisystem ist schreibgeschützt (außer gemountete Volumes)no-new-privileges– Prozesse können keine zusätzlichen Rechte erlangencap_drop: ALL– Alle Linux Capabilities werden entferntmem_limit: 256m– Speicher ist begrenzt, verhindert Ressourcen-Erschöpfung
Das
config-Volume ist alsro(read-only) gemountet. Radicale braucht nur Lesezugriff auf die Konfiguration.
Container starten
1
2
cd /opt/radicale
docker compose up -d
Konfiguration
Die Radicale-Konfiguration ist übersichtlich und in Sektionen unterteilt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[server]
hosts = 0.0.0.0:5232
[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt
[storage]
filesystem_folder = /data/collections
[web]
type = internal
[logging]
level = info
mask_passwords = True
Sektionen erklärt
| Sektion | Parameter | Beschreibung |
|---|---|---|
[server] |
hosts |
Bind-Adresse und Port |
[auth] |
type |
Authentifizierungsmethode (htpasswd, none, remote_user) |
[auth] |
htpasswd_encryption |
Passwort-Hashing (bcrypt empfohlen, alternativ md5) |
[storage] |
filesystem_folder |
Pfad für Kalender- und Kontakt-Daten |
[web] |
type |
Integriertes Web-Interface (internal) oder deaktiviert (none) |
[logging] |
mask_passwords |
Passwörter in Logs maskieren |
Verwende immer
bcryptals Verschlüsselungsmethode. MD5 und SHA1 gelten als unsicher.
Benutzerverwaltung
Option 1: htpasswd-manager Web-UI
Der htpasswd-manager Container stellt eine Web-Oberfläche unter Port 5280 bereit, über die du Benutzer anlegen, ändern und löschen kannst. Die Passwörter werden automatisch mit bcrypt gehasht und in der users-Datei gespeichert.
Option 2: CLI
Ohne Web-UI kannst du Benutzer auch per Kommandozeile verwalten:
1
2
3
4
5
6
7
8
# Neuen Benutzer anlegen
docker exec -it radicale htpasswd -B /config/users neuerbenutzer
# Benutzer löschen
docker exec -it radicale htpasswd -D /config/users altbenutzer
# Datei manuell erstellen (erster Benutzer)
htpasswd -Bc /opt/radicale/config/users meinbenutzer
Nach dem Anlegen eines Benutzers muss Radicale nicht neu gestartet werden – die htpasswd-Datei wird bei jedem Login neu eingelesen.
Zugriffsrechte (Rights)
Standardmäßig verwendet Radicale owner_only – jeder Benutzer sieht nur seine eigenen Collections. Für gemeinsame Kalender oder feingranulare Rechte gibt es das Rights-System.
Verfügbare Presets
| Preset | Bedeutung |
|---|---|
none |
Kein Zugriffsschutz |
authenticated |
Jeder authentifizierte User sieht alles |
owner_only |
User sehen nur eigene Collections |
owner_write |
Eigene Collections r/w, fremde nur lesen |
from_file |
Eigene Regeln aus einer Datei |
Eigene Regeln mit from_file
Für feingranulare Rechte fügst du eine [rights]-Sektion in die Konfiguration ein:
1
2
3
[rights]
type = from_file
file = /config/rights
Die Rights-Datei arbeitet mit INI-Sektionen. user und collection sind Regex-Patterns, {user} wird automatisch durch den eingeloggten Benutzernamen ersetzt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Jeder darf die Root-URL lesen (nötig für Discovery)
[root]
user = .+
collection =
permissions = R
# User dürfen eigene Top-Level-Collections verwalten
[owner-access]
user = .+
collection = {user}
permissions = RW
# User dürfen eigene Sub-Collections verwalten
[owner-subcollections]
user = .+
collection = {user}/[^/]+
permissions = rw
# frank darf den team-kalender von anna lesen
[frank-reads-anna-team]
user = frank
collection = anna/team-kalender
permissions = r
# Alle dürfen den shared-Kalender lesen und schreiben
[shared-calendar]
user = .+
collection = shared/kalender
permissions = rw
Permissions erklärt
| Permission | Bedeutung |
|---|---|
r |
Collection-Inhalte lesen (Events/Kontakte) |
w |
Collection-Inhalte schreiben |
R |
Collection selbst lesen (Discovery/Listing) |
W |
Collection erstellen/löschen |
Groß- und Kleinbuchstaben haben unterschiedliche Bedeutung: R/W betrifft die Collection als Ganzes, r/w betrifft die Inhalte darin.
Regeln werden von oben nach unten geprüft – die erste passende Regel gewinnt. Ohne passende Regel wird der Zugriff verweigert.
Zugriff auf einzelne Einträge innerhalb einer Collection (z.B. ein bestimmtes Event in einem Kalender) ist nicht möglich. Die kleinste Einheit ist immer eine ganze Collection. Wenn du unterschiedliche Rechte brauchst, erstelle separate Kalender.
Bei Verwendung von from_file muss die Rights-Datei im Container lesbar sein. Passe dafür das config-Volume in der docker-compose.yml an und lege die Datei neben der config und users ab:
1
2
3
4
/opt/radicale/config/
├── config # Radicale-Konfiguration
├── rights # Zugriffsregeln
└── users # htpasswd-Datei
Client-Einrichtung
Android (DAVx⁵)
DAVx⁵ ist die Standard-App für CalDAV/CardDAV-Sync auf Android:
- DAVx⁵ installieren (F-Droid oder Google Play)
- Neues Konto anlegen → “Mit URL und Benutzername anmelden”
- Base-URL eingeben:
https://radicale.example.com/(oderhttp://192.168.60.166:5232/im LAN) - Benutzername und Passwort eingeben
- DAVx⁵ erkennt automatisch alle verfügbaren Kalender und Adressbücher
- Gewünschte Collections auswählen und Sync aktivieren
DAVx⁵ ist auf F-Droid kostenlos verfügbar. Im Google Play Store kostet die App eine einmalige Gebühr.
Thunderbird
Thunderbird unterstützt CalDAV nativ:
- Kalender-Tab öffnen → Rechtsklick → “Neuer Kalender”
- “Im Netzwerk” auswählen
- Benutzername eingeben
- URL:
https://radicale.example.com/benutzer/kalender-id/ - Passwort eingeben und speichern
Für Kontakte (CardDAV) benötigst du das Add-on CardBook:
- CardBook installieren (Add-ons → “CardBook” suchen)
- Neues Adressbuch → “Remote” → “CardDAV”
- URL:
https://radicale.example.com/benutzer/kontakte-id/ - Benutzername und Passwort eingeben
Die Collection-URLs findest du im Radicale Web-Interface unter
https://radicale.example.com/. Dort werden alle Kalender und Adressbücher mit ihren URLs aufgelistet.
iOS (Kalender/Kontakte)
iOS unterstützt CalDAV und CardDAV nativ in den Systemeinstellungen:
- Einstellungen → Kalender (oder Kontakte) → Accounts → Account hinzufügen
- Andere → CalDAV-Account (bzw. CardDAV-Account)
- Server:
radicale.example.com - Benutzername und Passwort eingeben
- iOS erkennt die Collections automatisch
Reverse Proxy
Für den Zugriff von außen empfiehlt sich ein Reverse Proxy mit SSL-Terminierung.
Variante 1: Nginx Proxy Manager
Im NPM einen neuen Proxy Host anlegen:
| Feld | Wert |
|---|---|
| Domain | radicale.example.com |
| Scheme | http |
| Forward Hostname | 192.168.60.166 |
| Forward Port | 5232 |
| SSL | Let’s Encrypt aktivieren |
| Websockets | Nicht benötigt |
Falls du auch den htpasswd-manager exponieren möchtest, erstelle einen zweiten Proxy Host auf Port
5280. Sichere diesen zusätzlich mit NPM Access Lists ab.
Variante 2: Traefik
1
2
3
4
5
6
labels:
- "traefik.enable=true"
- "traefik.http.routers.radicale.rule=Host(`radicale.example.com`)"
- "traefik.http.routers.radicale.entrypoints=websecure"
- "traefik.http.routers.radicale.tls.certresolver=letsencrypt"
- "traefik.http.services.radicale.loadbalancer.server.port=5232"
Zusätzlich empfiehlt sich ein Redirect für die Well-Known-URLs:
1
2
3
- "traefik.http.middlewares.radicale-wellknown.redirectregex.regex=^https://radicale.example.com/\\.well-known/(caldav|carddav)"
- "traefik.http.middlewares.radicale-wellknown.redirectregex.replacement=https://radicale.example.com/"
- "traefik.http.routers.radicale.middlewares=radicale-wellknown"
Backup
Das Backup ist denkbar einfach – alle Daten liegen im Dateisystem:
1
2
# Komplettes Backup des Datenverzeichnisses
tar -czf radicale-backup-$(date +%Y%m%d).tar.gz -C /opt/radicale data/ config/
Was wird gesichert?
| Verzeichnis | Inhalt |
|---|---|
data/collections/ |
Alle Kalender und Kontakte (iCal/vCard-Dateien) |
config/config |
Radicale-Konfiguration |
config/users |
Benutzer und Passwort-Hashes |
Die Collections sind einfache Textdateien im iCalendar- und vCard-Format. Du kannst sie bei Bedarf direkt lesen oder in andere Systeme importieren.
Automatisches Backup (Cron)
1
2
# Tägliches Backup um 3:00 Uhr
0 3 * * * tar -czf /backup/radicale-$(date +\%Y\%m\%d).tar.gz -C /opt/radicale data/ config/ && find /backup -name "radicale-*.tar.gz" -mtime +30 -delete
Vergleich: Radicale vs. Alternativen
| Feature | Radicale | Baïkal | Nextcloud |
|---|---|---|---|
| Sprache | Python | PHP | PHP |
| Datenbank | Keine (Dateien) | SQLite/MySQL | MySQL/PostgreSQL |
| RAM-Verbrauch | ~30 MB | ~50 MB | ~500 MB+ |
| Web-Interface | Minimal | Admin-UI | Vollständig |
| Kalender/Kontakte | Ja | Ja | Ja + vieles mehr |
| Setup-Komplexität | Sehr einfach | Einfach | Komplex |
| Dependencies | Keine | PHP, Webserver | PHP, Webserver, DB |
| Docker-Image | ~50 MB | ~200 MB | ~1 GB+ |
| Empfehlung | Wer nur CalDAV/CardDAV braucht | Wer ein besseres Web-UI will | Wer eine Komplettlösung sucht |
Tipps & Best Practices
-
Bcrypt verwenden – Setze
htpasswd_encryption = bcryptfür sichere Passwort-Hashes. MD5 und SHA1 sind veraltet. -
Regelmäßige Backups – Die Daten sind einfache Dateien, ein tägliches
tarreicht völlig. -
HTTPS nutzen – CalDAV/CardDAV überträgt Credentials im Klartext. Verwende immer einen Reverse Proxy mit SSL oder Radicales eingebautes TLS.
-
Well-Known-URLs konfigurieren – Viele Clients suchen nach
/.well-known/caldavund/.well-known/carddav. Radicale unterstützt das nativ. -
Separate Kalender anlegen – Erstelle im Web-UI separate Kalender für Arbeit, Privat etc. statt alles in einen zu packen.
-
htpasswd-manager absichern – Die Benutzerverwaltungs-UI sollte nur im LAN oder über VPN erreichbar sein, nicht öffentlich.
-
Read-Only Container – Der
read_only: true-Modus verhindert, dass ein kompromittierter Prozess das Container-Dateisystem verändert. -
Monitoring – Der Healthcheck in der docker-compose.yml prüft regelmäßig, ob Radicale erreichbar ist. Binde das in dein Monitoring ein.
Troubleshooting
Client kann sich nicht verbinden
- Prüfe, ob der Port
5232erreichbar ist:curl -v http://192.168.60.166:5232 - Überprüfe die Firewall-Regeln auf dem Host
- Stelle sicher, dass die URL korrekt ist (inkl. Trailing Slash)
Authentifizierung schlägt fehl
- Prüfe die
users-Datei:docker exec radicale cat /config/users - Stelle sicher, dass bcrypt-Hashes verwendet werden (beginnen mit
$2b$oder$2y$) - Passwort neu setzen:
docker exec -it radicale htpasswd -B /config/users benutzername
Kalender/Kontakte werden nicht angezeigt
- Im Radicale Web-UI (
http://192.168.60.166:5232) prüfen, ob Collections existieren - Neue Collection erstellen: Im Web-UI einloggen → “Create new addressbook or calendar”
- Client-URL prüfen: Muss auf die spezifische Collection zeigen
Berechtigungsprobleme
1
2
# Data-Verzeichnis muss für den Container beschreibbar sein
sudo chown -R 2999:2999 /opt/radicale/data
Die UID
2999ist der Standard-User im offiziellen Radicale-Docker-Image.
Container startet nicht
1
2
3
4
5
6
7
# Logs prüfen
docker logs radicale
# Häufige Ursachen:
# - Config-Datei nicht gefunden → Pfad in command prüfen
# - Port bereits belegt → netstat -tlnp | grep 5232
# - Rechte auf data/ falsch → chown prüfen
Fazit
Radicale ist die perfekte Lösung, wenn du einen einfachen, zuverlässigen CalDAV/CardDAV-Server suchst, ohne den Overhead einer Komplettlösung wie Nextcloud. Die Installation dauert wenige Minuten, der Ressourcenverbrauch ist minimal und die Konfiguration ist straightforward.
Mit DAVx⁵ auf Android, Thunderbird auf dem Desktop und den nativen iOS-Einstellungen hast du deine Kalender und Kontakte auf allen Geräten synchron – unter deiner eigenen Kontrolle.