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

Calendar and contacts on your server, on every device: Radicale. A few megabytes, plain-text storage, standard CalDAV and CardDAV.

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

TaskWhat
Folder~/docker/radicale/compose.yaml
Webhttp://server-ip:5232 (minimal UI for creating collections)
Usershtpasswd -B config/users john
PhoneiOS: Settings → Calendar/Contacts → Accounts → Other → CalDAV/CardDAV; Android: DAVx5
DesktopThunderbird (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.

Call us