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
- 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
- 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
- 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
- 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
- Always use bcrypt (
-B
) for password encryption. - Store the htpasswd file outside web-accessible directories (e.g.,
/opt/radicale/config/users
). - Restrict file permissions to the Radicale user (
chmod 600
). - Avoid PAM for Radicale unless absolutely necessary; htpasswd is simpler and avoids permission headaches.
- 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.
- Create the PAM service file:
sudo vim /etc/pam.d/radicale
- Add the following content:
auth required pam_unix.so
account required pam_unix.so
auth
handles login authenticationaccount
checks account validity
- 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
- Radicale Main Documentation (v3.x) – comprehensive documentation for the latest Radicale versions
https://radicale.org/ - Radicale Tutorials – guides for installation and configuration on different systems
https://radicale.org/master.html#tutorials - Radicale GitHub Repository – source code, issues, pull requests, and release history
https://github.com/Kozea/Radicale - Radicale PyPI Page – version info and Python installation instructions
https://pypi.org/project/Radicale/