Self-Hosting Radicale CalDAV

Digital sovereignty is increasingly important for families and small teams not ony for companies. Hosting your own calendar server ensures full control of your data, avoids reliance on Google, Microsoft, Apple, Samsung or other cloud providers, and supports multiuser access without sacrificing privacy. Radicale is a lightweight, free CalDAV/CardDAV server ideal for self-hosting, offering simple deployment and broad client compatibility.

Decision-Making: Why Radicale?

Radicale is a minimal, open-source alternative to Google Calendar. Key considerations:

  • Multiuser support: Each user can have separate calendars.
  • Invitations: Basic support via clients; server-side email invitations require external handling.
  • Clients: Compatible with Thunderbird, DAVx⁵, Samsung Calendar, iOS Calendar.
  • Security & independence: Self-hosted, HTTPS-protected, data stays local.
  • Cost: Completely free under GPLv3.

💡Note: After evaluating Radicale, I decided to move forward with Nextcloud instead. Radicale is a lightweight and capable CalDAV/CardDAV server, but without a built-in web interface for viewing calendar entries and with the rights system and PAM proving tricky in practice, Nextcloud turned out to be the more practical choice for my setup. Evaluations often lead to such outcomes — understanding a system’s strengths before ultimately choosing what fits best. For personal use or setups without calendar sharing, Radicale is an excellent solution.

System Requirements & Prerequisites

  • OS: RHEL 8 or 9 or compatible
  • Python 3 & pip
  • Dedicated service user: Avoid running as root. Example: radicale
  • Optional: HAProxy for reverse proxy, TLS termination, and LXC container for isolation

Requirements

Before starting, make sure your server meets these prerequisites.

  • Initial update and system packages
sudo dnf update -y
sudo dnf install -y vim-enhanced nano
sudo dnf install -y python3 python3-pip python3-devel git
sudo dnf install -y httpd-tools # for htpasswd command

These packasges provide the Python runtime and tools needed to install Radicale cleanly under /opt.

  • Optional: Reverse Proxy and TLS

If you’re not already using HAProxy or another load balancer, you can set up Nginx as a simple reverse proxy with Let’s Encrypt:

sudo dnf install -y nginx 
sudo dnf install -y certbot python3-certbot-nginx # if you plan to use Let´s Encrypt Certbot

Use this only if you don’t terminate TLS elsewhere.

Network and DNS

  • Static IP or fixed hostname
  • DNS record (e.g. cal.examplecorp.io) pointing to the server
  • Ports 80 and 443 open if using Nginx or a proxy

Installation

  1. Create dedicated user and directories:
sudo useradd --system --create-home --home-dir /opt/radicale --shell /sbin/nologin radicale
sudo mkdir -p /opt/radicale/collections /opt/radicale/config
sudo chown -R radicale:radicale /opt/radicale
  1. Virtual environment installation:
sudo -u radicale python3 -m venv /opt/radicale/venv
source /opt/radicale/venv/bin/activate
pip install --upgrade pip
pip install radicale
  1. Configuration file: /opt/radicale/config/config

Create the configuration file manually, since they are not generated by default

sudo touch /opt/radicale/config/config && sudo chown radicale:radicale /opt/radicale/config/config 
sudo -u radicale vim /opt/radicale/config/config
[server]
hosts = 127.0.0.1:5232, [::1]:5232
max_connections = 20
max_content_length = 100000000
timeout = 30

[storage]
filesystem_folder = /opt/radicale/collections

[auth]
type = htpasswd
htpasswd_filename = /opt/radicale/config/users
htpasswd_encryption = autodetect

[web]
type = none
  1. Systemd service: /etc/systemd/system/radicale.service

After creating the config file, you need to create the systemd service manually.

sudo vim /etc/systemd/system/radicale.service
[Unit]
Description=Radicale CalDAV/CardDAV Server
After=network.target

[Service]
User=radicale
ExecStart=/opt/radicale/venv/bin/radicale --config /opt/radicale/config/config
Restart=on-failure
# Deny other users access to the calendar data
UMask=0027
# Optional security settings
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
ReadWritePaths=/opt/radicale/

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now radicale

⚠️ Notice: The service will not start yet if authentication is enabled, as additional steps (see below).

User Management in Radicale Using htpasswd

Radicale 3.5 and later recommend managing users via htpasswd files instead of PAM for authentication. This approach is secure, portable, and avoids PAM-related permission issues.

Creating a User File

Choose a secure location outside your webroot, e.g.:

sudo touch /opt/radicale/config/users
sudo chown radicale:radicale /opt/radicale/config/users
sudo chmod 600 /opt/radicale/config/users
  • radicale is the system user running Radicale.
  • chmod 600 ensures that only the Radicale process can read the file.

Adding Users with bcrypt Encryption

On RHEL-based systems, install the Apache utilities:

sudo dnf install -y httpd-tools

Create the users:

sudo htpasswd -B /opt/radicale/config/users max.mustermann
  • -B → bcrypt encryption (strong and recommended)
  • You will be prompted to enter the password.

Radicale Configuration

Edit your Radicale config (~/.config/config):

[auth]
type = htpasswd
htpasswd_filename = /opt/radicale/config/users
htpasswd_encryption = autodetect

This tells Radicale to use the htpasswd file for authentication and ensures passwords are securely verified using bcrypt.

Testing User Authentication

Test with curl:

curl -v --user username:yourpassword http://localhost:5232/
  • Successful authentication returns HTTP 200 and the calendar listing.
  • Incorrect credentials return HTTP 401 Unauthorized.

Best Practices

  1. Always use bcrypt (-B) for password encryption.
  2. Store the htpasswd file outside web-accessible directories (e.g., /opt/radicale/config/users).
  3. Restrict file permissions to the Radicale user (chmod 600).
  4. Avoid PAM for Radicale unless absolutely necessary; htpasswd is simpler and avoids permission headaches.
  5. Add users individually to prevent overwriting existing entries (-c only for the first user).

This setup provides a simple, secure way to manage Radicale users, fully compatible with browser and CalDAV clients.

PAM Configuration
⚠️ Note: PAM authentication only works if Radicale is running with root privileges. For security reasons, this guide uses htpasswd-based authentication instead.
💡 If you still want to set up PAM authentication, click here for a detailed guide.

PAM Configuration

To enable PAM authentication for Radicale on AlmaLinux, first activate the EPEL repository:

sudo dnf install epel-release
sudo dnf update

This allows access to extra packages. Since Radicale runs in a Python virtual environment (venv), you also need to install the PAM module inside the venv:

source /opt/radicale/venv/bin/activate
pip install --upgrade pip
pip install python-pam

Installing inside the venv ensures Radicale can use PAM securely without affecting the system Python environment.

To authenticate Radicale users via system accounts, set up a dedicated PAM service.

  1. Create the PAM service file:
sudo vim /etc/pam.d/radicale
  1. Add the following content:
auth       required   pam_unix.so
account    required   pam_unix.so
  • auth handles login authentication
  • account checks account validity
  1. Ensure Radicale config points to this service:
[auth]
type = auth_pam
service = radicale
  • service = radicale must match the PAM filename.

This setup allows Radicale to use existing Linux users for login without creating separate accounts.

Check the Configuration

Verify the systemd Service

sudo systemctl status radicale
  • Ensure it shows Active: active (running).

Verify the Listener

sudo ss -tuln | grep 5232
  • Confirms Radicale is listening on the configured port for CalDAV clients.

Nginx Reverse Proxy for Radicale with SSL

You can use Nginx to securely expose Radicale behind HTTPS. Below is an example configuration for AlmaLinux or similar systems. Make sure your Radicale instance is running on localhost:5232 and listening on 127.0.0.1 / ::1.

Installing Nginx

On RHEL, you can install Nginx quickly using dnf:

sudo dnf install -y nginx
sudo systemctl enable --now nginx

Example Configuration

Create /etc/nginx/conf.d/radicale.conf:

server {
    listen 443 ssl;
    server_name cal.examplecorp.io;

    ssl_certificate     /etc/nginx/conf.d/cal.examplecorp.io.crt;
    ssl_certificate_key /etc/nginx/conf.d/cal.examplecorp.io.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # Optional security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-XSS-Protection "1; mode=block";

    location / {
        proxy_pass        http://127.0.0.1:5232/;
        proxy_set_header  X-Script-Name /;
        proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header  X-Forwarded-Host $host;
        proxy_set_header  X-Forwarded-Port $server_port;
        proxy_set_header  X-Forwarded-Proto $scheme;
        proxy_set_header  Host $http_host;
        proxy_pass_header Authorization;
    }
}

# Optional HTTP redirect
server {
    listen 80;
    server_name cal.examplecorp.io;
    return 301 https://$host$request_uri;
}

Notes

  • Trailing slash in location /radicale/ is crucial for proper URL rewriting.
  • Make sure the SSL certificate and key exist at the paths:
/etc/nginx/conf.d/cal.examplecorp.io.crt
/etc/nginx/conf.d/cal.examplecorp.io.key
  • After creating the config, test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
  • The X-Script-Name header ensures Radicale knows its URL prefix, so clients like Thunderbird or Samsung Calendar can access the /radicale/ path correctly.

Verify Access

  • After configuring Nginx and starting Radicale, open your browser and visit: https://cal.examplecorp.io/username/calendar
  • If everything is working, you should see: Radicale works!
  • This confirms that the Nginx reverse proxy and Radicale server are running correctly and accessible over HTTPS.

Backup and Maintenance

  • Backups: Copy /opt/radicale/collections and /opt/radicale/config regularly.
  • Monitoring: Use systemctl status radicale and HAProxy logs.

Updating Radicale

Keeping Radicale up-to-date is crucial for security, stability, and new features. If installed in a Python virtual environment under /opt with a dedicated service user (radicale), follow these steps:

Step 1: Stop the Radicale Service

sudo systemctl stop radicale
  • Ensures no clients are syncing during the update.

Step 2: Activate the Virtual Environment

sudo -u radicale /opt/radicale/venv/bin/activate
  • Ensures you update the correct Radicale installation isolated from system Python.

Step 3: Upgrade pip (optional but recommended)

pip install --upgrade pip
  • Keeps Python package management reliable.

Step 4: Upgrade Radicale

pip install --upgrade radicale
  • Pip downloads the latest stable Radicale version and updates the virtual environment.
  • Existing configurations (/opt/radicale/config) and collections (/opt/radicale/collections) remain untouched.

Step 5: Verify the Update

radicale --version
  • Confirms that the new version is active.

Step 6: Restart the Service

sudo systemctl start radicale
sudo systemctl status radicale
  • Ensures the service is running and accessible.

✅ Summary

Stop the service → activate venv → upgrade Radicale via pip → restart service.
The dedicated service user ensures permissions and security are maintained.

Radicale Web Frontend (just a connection manager)

The Radicale Web Frontend (by Kozea) provides a simple browser interface to manage CalDAV and CardDAV collections. It allows you to:

  • List, create, rename, and delete calendars or address books
  • Upload/download .ics files
  • Handle login and basic maintenance tasks

It does not display calendar events or contacts graphically. For full functionality, use a CalDAV/CardDAV client like Thunderbird, DAVx⁵ (Android), Evolution, or Apple Calendar.

Conclusion

Radicale is a lightweight, fully free, and secure self-hosted CalDAV solution. Perfect for families, small teams, or anyone valuing digital sovereignty.

Troubleshooting Radicale

Sometimes the Radicale service fails to start via systemd. Common causes include configuration errors, permission issues, or port conflicts. Use the steps below to diagnose and fix problems.

Run Radicale Directly

Before blaming systemd, test Radicale manually:

sudo -u radicale /opt/radicale/venv/bin/radicale --config /opt/radicale/config/config --debug
  • Running in debug mode prints errors directly to the console.
  • Look for issues with [server], [auth], or missing modules.
  • Confirm the port is not already in use.

Troubleshooting Radicale with journalctl

When running a self-hosted Radicale CalDAV server, most issues can be diagnosed by monitoring its logs in real time. The primary tool for this on systemd-based systems is journalctl.

Viewing Live Logs

Use the following command to follow the Radicale service logs in real time:

sudo journalctl -u radicale -f
  • -u radicale → Shows logs specifically for the Radicale service.
  • -f → Follows the log output, updating live as new events occur.

Official and resources for Radicale