How to Self-Host Radicale: CalDAV/CardDAV Server 2026
TL;DR
Radicale (GPL 3.0, ~3K GitHub stars, Python) is the simplest self-hosted CalDAV/CardDAV server — sync your calendars and contacts across all devices without Google or Apple. It's a single Python process, stores data as plain files, and can be set up in under 5 minutes. iCloud+ charges $1-3/month just for sync storage. Radicale is free and stores your calendars and contacts on your own hardware as simple .ics and .vcf files you can read directly.
Key Takeaways
- Radicale: GPL 3.0, ~3K stars, Python — CalDAV + CardDAV in one lightweight server
- File storage: Calendars stored as
.icsfiles, contacts as.vcf— human-readable, easily backed up - Multi-user: Each user gets their own calendars and address books
- Rights management: Configurable per-user or per-collection permissions
- vs Baikal: Radicale has no web UI (just protocol server); Baikal has a browser-based admin
- Pairs well with: Nextcloud (built-in), standalone server, or alongside any mail setup
Part 1: Docker Setup
# docker-compose.yml
services:
radicale:
image: tomsquest/docker-radicale:latest
container_name: radicale
restart: unless-stopped
ports:
- "5232:5232"
volumes:
- radicale_data:/data
- radicale_config:/config
environment:
- RADICALE_CONFIG=/config/radicale.cfg
# Run as specific user:
user: "1000:1000"
volumes:
radicale_data:
radicale_config:
docker compose up -d
Part 2: Configuration
# Create config file:
mkdir -p ~/.radicale
cat > /path/to/radicale_config/radicale.cfg << 'EOF'
[server]
hosts = 0.0.0.0:5232
max_connections = 20
max_content_length = 100000000
timeout = 30
[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt
[storage]
filesystem_folder = /data/collections
[rights]
type = owner_only
[logging]
level = warning
EOF
Create users (htpasswd)
# Install htpasswd (apache2-utils) or use Docker:
# Create first user (creates file):
docker run --rm httpd:alpine htpasswd -nbB alice password123 >> users
# Add more users:
docker run --rm httpd:alpine htpasswd -nbB bob password456 >> users
# Copy to volume:
docker cp users radicale:/config/users
# Restart:
docker compose restart radicale
Part 3: HTTPS with Caddy
cal.yourdomain.com {
reverse_proxy localhost:5232
}
Part 4: iOS / macOS Setup
Calendar (CalDAV)
iOS:
- Settings → Calendar → Accounts → Add Account → Other
- Add CalDAV Account
- Server:
https://cal.yourdomain.com - Username:
alice - Password: your password
macOS:
- System Settings → Internet Accounts → Add Account → Other
- CalDAV Account
- Account type: Advanced (to specify server URL)
- Server:
https://cal.yourdomain.com - Username:
alice, Password: your password
Contacts (CardDAV)
iOS:
- Settings → Contacts → Accounts → Add Account → Other
- Add CardDAV Account
- Server:
https://cal.yourdomain.com - Username:
alice, Password: your password
macOS:
- System Settings → Internet Accounts → Other → CardDAV Account
- Account type: Advanced
- Server:
https://cal.yourdomain.com
Part 5: Android Setup
Via DAVx⁵ (recommended — free on F-Droid)
- Install DAVx⁵ from F-Droid or Google Play
- + → Login with URL:
https://cal.yourdomain.com/alice/ - Enter username and password
- DAVx⁵ discovers all calendars and address books
- Select which to sync → toggle each calendar/address book
Calendar apps
| App | Platform | CalDAV |
|---|---|---|
| DAVx⁵ | Android | Yes (sync layer) |
| Simple Calendar | Android | Via DAVx⁵ |
| Etar | Android | Via DAVx⁵ |
| Thunderbird | All | Built-in (no plugin needed) |
| Evolution | Linux | Built-in |
Part 6: Thunderbird Setup
Thunderbird supports CalDAV/CardDAV natively (no plugin required since TB 78):
Calendar
- Thunderbird → Calendar → New Calendar
- On the Network → CalDAV
- URL:
https://cal.yourdomain.com/alice/calendar-name/ - Enter credentials
Contacts
- Thunderbird → Address Book → New Address Book → CardDAV
- URL:
https://cal.yourdomain.com/alice/contacts/ - Enter credentials
Part 7: Multi-User Rights Configuration
For shared calendars:
# /config/radicale.cfg — advanced rights config:
[rights]
type = from_file
file = /config/rights
# /config/rights file:
# Format: [user:collection_path] = permissions
# R = read, W = write (implies read)
# Alice can read/write her own collections:
[alice:alice/.*]
user = alice
permissions = RW
# Bob can read/write his own:
[bob:bob/.*]
user = bob
permissions = RW
# Alice can read Bob's shared calendar:
[alice_reads_bob_shared]
user = alice
collection = bob/shared-calendar
permissions = R
# Everyone can read public holiday calendar:
[public_holidays]
user = .*
collection = .*/holidays
permissions = R
Part 8: Collection URLs and Structure
Radicale collections follow this URL pattern:
https://cal.yourdomain.com/{username}/{collection-name}/
Examples:
https://cal.yourdomain.com/alice/personal/ # Alice's personal calendar
https://cal.yourdomain.com/alice/work/ # Alice's work calendar
https://cal.yourdomain.com/alice/contacts/ # Alice's contacts
https://cal.yourdomain.com/alice/family-shared/ # Shared family calendar
Create a collection via curl
# Create a calendar collection:
curl -u alice:password \
-X MKCOL "https://cal.yourdomain.com/alice/work/" \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="utf-8"?>
<mkcol xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<set><prop>
<resourcetype><collection/><C:calendar/></resourcetype>
<displayname>Work</displayname>
</prop></set>
</mkcol>'
Maintenance and Backup
# Update:
docker compose pull
docker compose up -d
# Backup (just copy the data directory — it's plain files):
tar -czf radicale-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect radicale_radicale_data --format '{{.Mountpoint}}')
# The backup contains:
# collections/alice/personal/event1.ics
# collections/alice/contacts/contact1.vcf
# etc. — all human-readable
# Logs:
docker compose logs -f radicale
# Test CalDAV connection:
curl -u alice:password \
-X PROPFIND "https://cal.yourdomain.com/alice/" \
-H "Depth: 1"
See also: Baikal (CalDAV/CardDAV with web admin UI)
See all open source calendar and contacts tools at OSSAlt.com/categories/productivity.