How to Host Your Calendar and Contacts with Radicale (CalDAV and CardDAV in One Tiny Container)

Nextcloud (32) does calendars and contacts well, but if you don't need the rest of Nextcloud, Radicale does just those two in a container that uses a few megabytes. It speaks CalDAV and CardDAV, which every phone and desktop understands natively — no app from Radicale needed — and stores everything as plain .ics and .vcf files you can back up and read with cat. A family calendar, each person's own, and shared contacts, with the Google account out of the loop.
Quick reference
| Task | What |
|---|---|
| Folder | ~/docker/radicale/compose.yaml |
| Web | http://server-ip:5232 (minimal UI for creating collections) |
| Users | htpasswd -B config/users john |
| Phone | iOS: Settings → Calendar/Contacts → Accounts → Other → CalDAV/CardDAV; Android: DAVx5 |
| Desktop | Thunderbird (built in), Outlook via CalDAV Synchronizer |
| Data | ./data/collections/collection-root/john/ — plain files |
1. The compose file
mkdir -p ~/docker/radicale/{config,data} && cd ~/docker/radicale
nano compose.yaml
services:
radicale:
image: tomsquest/docker-radicale:latest
container_name: radicale
ports:
- "127.0.0.1:5232:5232"
init: true
read_only: true
security_opt:
- no-new-privileges:true
cap_drop: [ALL]
cap_add: [SETUID, SETGID, CHOWN, KILL]
volumes:
- ./data:/data
- ./config:/config:ro
restart: unless-stopped
nano config/config
[server]
hosts = 0.0.0.0:5232
[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt
[storage]
filesystem_folder = /data/collections
[rights]
type = owner_only
2. Users
sudo apt install apache2-utils -y
htpasswd -B -c config/users john
htpasswd -B config/users sarah
docker compose up -d
3. Reverse proxy
Phones need HTTPS to be happy. cal.example.com → 127.0.0.1:5232 (tutorial 28, or the internal wildcard from 147), with:
proxy_set_header X-Script-Name /;
proxy_pass_header Authorization;
Open https://cal.example.com, log in as john, and in the small web UI create new addressbook or calendar: "Personal" (calendar), "Family" (calendar), "Contacts" (address book). Each gets a URL like /john/abc123-.../.
4. Connect devices
iPhone/iPad: Settings → Calendar → Accounts → Add Account → Other → Add CalDAV Account: server cal.example.com, username, password. Same under Contacts → Add CardDAV Account. All calendars and address books appear; events and contacts created on the phone sync up.
Android: install DAVx5 (F-Droid or Play) → Login with URL → https://cal.example.com/john/ → it finds every collection; tick which to sync. Calendars appear in any calendar app; contacts in the Contacts app.
Thunderbird: File → New → Calendar → On the Network → the collection URL; Address Book → New CardDAV address book.
Outlook (Windows): the free CalDAV Synchronizer add-in with the same URLs.
5. A shared family calendar
Radicale's owner_only rights means only the owner sees a collection. For sharing, switch to a rights file:
[rights]
type = from_file
file = /config/rights
nano config/rights
[owner-write]
user: .+
collection: {user}/.*
permissions: RW
[family-shared]
user: .+
collection: john/family/.*
permissions: rw
Restart. Now every user can read and write John's family calendar; add it on each phone as a second CalDAV account pointing at /john/family/ (or DAVx5 finds it when logging in as any user with the base URL /john/). Put the school calendar, birthdays and bin days in it.
6. Import and back up
Takeout or any .ics/.vcf (tutorial 149): upload through the web UI, or copy the files into the collection folder and restart. Backup is just ./data — every event is a text file; ./config holds the users. Both in the nightly backup (23).
Troubleshooting
Phone can't find the account. Needs HTTPS with a trusted certificate; and the X-Script-Name header if Radicale is under a subpath.
DAVx5 shows no collections. Log in with the URL ending in your username and a slash: /john/.
Two-way sync conflicts. Rare; Radicale keeps the last write. Avoid editing the same event on two offline devices.
Changed a password. htpasswd -B config/users john again; update it on each device.
Wrapping up
One container, an htpasswd file, a proxy entry with HTTPS, native CalDAV/CardDAV accounts on each device. Calendars and contacts sync everywhere from a folder of text files on your server, a shared family calendar needs five lines of rights, and nothing heavier than a few megabytes is running.
