Eintrag

Radicale: Selbstgehosteter CalDAV & CardDAV Server

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 erlangen
  • cap_drop: ALL – Alle Linux Capabilities werden entfernt
  • mem_limit: 256m – Speicher ist begrenzt, verhindert Ressourcen-Erschöpfung

Das config-Volume ist als ro (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 bcrypt als 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:

  1. DAVx⁵ installieren (F-Droid oder Google Play)
  2. Neues Konto anlegen → “Mit URL und Benutzername anmelden”
  3. Base-URL eingeben: https://radicale.example.com/ (oder http://192.168.60.166:5232/ im LAN)
  4. Benutzername und Passwort eingeben
  5. DAVx⁵ erkennt automatisch alle verfügbaren Kalender und Adressbücher
  6. 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:

  1. Kalender-Tab öffnen → Rechtsklick → “Neuer Kalender”
  2. “Im Netzwerk” auswählen
  3. Benutzername eingeben
  4. URL: https://radicale.example.com/benutzer/kalender-id/
  5. Passwort eingeben und speichern

Für Kontakte (CardDAV) benötigst du das Add-on CardBook:

  1. CardBook installieren (Add-ons → “CardBook” suchen)
  2. Neues Adressbuch → “Remote” → “CardDAV”
  3. URL: https://radicale.example.com/benutzer/kontakte-id/
  4. 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:

  1. EinstellungenKalender (oder Kontakte) → AccountsAccount hinzufügen
  2. AndereCalDAV-Account (bzw. CardDAV-Account)
  3. Server: radicale.example.com
  4. Benutzername und Passwort eingeben
  5. 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

  1. Bcrypt verwenden – Setze htpasswd_encryption = bcrypt für sichere Passwort-Hashes. MD5 und SHA1 sind veraltet.

  2. Regelmäßige Backups – Die Daten sind einfache Dateien, ein tägliches tar reicht völlig.

  3. HTTPS nutzen – CalDAV/CardDAV überträgt Credentials im Klartext. Verwende immer einen Reverse Proxy mit SSL oder Radicales eingebautes TLS.

  4. Well-Known-URLs konfigurieren – Viele Clients suchen nach /.well-known/caldav und /.well-known/carddav. Radicale unterstützt das nativ.

  5. Separate Kalender anlegen – Erstelle im Web-UI separate Kalender für Arbeit, Privat etc. statt alles in einen zu packen.

  6. htpasswd-manager absichern – Die Benutzerverwaltungs-UI sollte nur im LAN oder über VPN erreichbar sein, nicht öffentlich.

  7. Read-Only Container – Der read_only: true-Modus verhindert, dass ein kompromittierter Prozess das Container-Dateisystem verändert.

  8. 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 5232 erreichbar 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 2999 ist 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.

Ressourcen

Dieser Eintrag ist vom Autor unter CC BY 4.0 lizensiert.