Self-host a personal calendar and address book using Radicale

Last edited on 2021-07-01 Tagged under  #nginx   #network   #linux 

CalDAV and CardDAV are open protocols for sharing a calendar and address book respectively between devices. Radicale is a self-hosted CalDAV and CardDAV server that, as per their website,:

  • Shares calendars and contact lists through CalDAV, CardDAV and HTTP.
  • Supports events, todos, journal entries and business cards.
  • Works out-of-the-box, no complicated setup or configuration required.
  • Can limit access by authentication.
  • Can secure connections with TLS.
  • Works with many CalDAV and CardDAV clients.
  • Stores all data on the file system in a simple folder structure.
  • Can be extended with plugins.
  • Is GPLv3-licensed free software.

Sounds good! Let's get it setup.

Install and configure

Install ...

$ python3 -m pip install --upgrade radicale

Make directories ...

$ mkdir -p ~/.config/radicale
$ mkdir -p ~/.var/lib/radicale/collections
$ sudo mkdir /etc/radicale

Setup user authentication for <username> (choose a name) using htpasswd ...

$ sudo apt install apache2-utils
$ sudo htpasswd -c /etc/radicale/users <username>

Create ~/.config/radicale/config. Example:

# access over the network
hosts =, [::]:5232
max_connections = 20
# 100 Megabyte
max_content_length = 100000000
# 30 seconds
timeout = 30

filesystem_folder = ~/.var/lib/radicale/collections

type = htpasswd
htpasswd_filename = /etc/radicale/users
# encryption method used in the htpasswd file
htpasswd_encryption = md5
# Average delay after failed login attempts in seconds
delay = 1

Run as a service

Run as a service with systemd as a user. Create directory ...

$ mkdir ~/.config/systemd/user

Create file ~/.config/systemd/user/radicale.service. Example:

Description=A simple CalDAV (calendar) and CardDAV (contact) server

ExecStart=/usr/bin/env python3 -m radicale


At this point, a problem might arise. When I went to enable the service ...

$ systemctl --user enable radicale
Failed to connect to bus: No such file or directory

In my case, this was because the XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS environment variables were missing.

If this rears up and bites you, run the following commands and test ...

$ export XDG_RUNTIME_DIR="/run/user/$UID"
$ export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
$ systemctl --user status

It works! See: Managing another user's systemd units

To make this fix permanent, modify /etc/ssh/sshd_config and set UsePAM no to UsePAM yes (which in Debian 10 is the default setting).

Enable and start the service ...

$ systemctl --user enable --now radicale

Check the status ...

$ systemctl --user status radicale
$ journalctl --user --unit radicale.service

Login to the web interface at http://<server-ip-address>:5232 with the newly-created username and password.

Click on Create new addressbook or calendar and I create an empty calendar and a empty address book; each outputs a link to URL: http://server-ip-address>:5232/<username>/<string>/.

Use Nginx as a reverse proxy

First, see "Nginx web server" to install and configure Nginx, an open-source, high performance, lightweight HTTP and reverse proxy server. Nginx sits in front of Radicale as the proxy, removing the location from the URL path that is forwarded to Radicale.

Example: An nginx configuration in /etc/nginx/conf.d/my-website.conf that uses Duck DNS for Dynamic DNS and SSL via a self-signed certificate ...

server {
    listen 80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /etc/ssl/certs/nginx-server-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-server-selfsigned.key;

    location / {
        root   /home/my-username/html;
        index  index.html index.htm;

    location /radicale/ { # The trailing / is important!
        proxy_pass        http://localhost:5232/; # The / is important!
        proxy_set_header  X-Script-Name /radicale;
	    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header  X-Remote-User $remote_user;
        proxy_set_header  Host $http_host;
	    proxy_pass_header Authorization;

Restart nginx and radicale ...

$ sudo systemctl restart nginx
$ systemctl --user restart radicale

Navigate to and confirm the Radicale login is displayed.

See Radicale Reverse Proxy for more options.

Add a client: Thunderbird

Recent versions of the Thunderbird email client now include a built-in calendar. Click on the calendar tab icon in the upper right corner to open.

In the calendar tab, click on the + in the left calendar sidebar and add a new calendar on the network with CalDAV. Enter the URL for the Radicale-hosted calendar. Thunderbird's calendar populates with data.

For the address book, install the CardBook add-on. Click on the CardBook tab icon in the upper right corner to open.

In the address book column, right-click and choose New Addressbook, select Remote for location, CardDav, and enter the URL for the Radicale-hosted address book. After setup, click and drag Thunderbird's existing contacts into the new address book.

Add a client: DAVx5

DAVx5 is a CalDAV/CardDAV synchronization client for Android, which I install via F-Droid.

After setting up an account with your Radicale username and URL, the calendar and address book are synced to the phone, and viewable in your Calendar and Contacts apps.

Thanks for reading! Read other posts?

» Next: A look at Xfce

« Previous: Nginx web server