Radicale Cribsheet

Created by: Lester Caine, Last modification: 28 Apr 2026 (12:39 UTC)

Radicale CalDAV/CardDAV Server - Key Points

Installing

zypper install radicale
# Creates radicale:radicale system user automatically

Configuration `/etc/radicale/config`

Key active settings:

[server]
hosts = localhost:5232

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

[storage]
filesystem_folder = /srv/radicale/collections

Critical systemd override

Default service restricts writes to `/var/lib/radicale/` — must add `/srv/radicale/` to allowed paths. Copy service file to webstack and add:

ReadWritePaths=/var/lib/radicale/collections /srv/radicale/

Storage setup

mkdir -p /srv/radicale/collections
chown -R radicale:radicale /srv/radicale
chmod 755 /srv/radicale
chmod 755 /srv/radicale/collections

**Critical — directories need 755 not 644!**

Collection structure

# Create directory structure
mkdir -p /srv/radicale/collections/collection-root/lsces/calendar
mkdir -p /srv/radicale/collections/collection-root/lsces/contacts

# Props files — collection-root needs one too!
echo '{}' > /srv/radicale/collections/collection-root/.Radicale.props

cat > /srv/radicale/collections/collection-root/lsces/.Radicale.props << 'EOF'
{"D:displayname": "lsces"}
EOF

cat > /srv/radicale/collections/collection-root/lsces/calendar/.Radicale.props << 'EOF'
{"C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO", "D:displayname": "Personal Calendar", "tag": "VCALENDAR"}
EOF

cat > /srv/radicale/collections/collection-root/lsces/contacts/.Radicale.props << 'EOF'
{"D:displayname": "Personal Contacts", "tag": "VADDRESSBOOK"}
EOF

chown -R radicale:radicale /srv/radicale/collections

Create user

htpasswd -B -c /etc/radicale/users lsces

Enable and start

systemctl daemon-reload
systemctl enable radicale
systemctl start radicale
systemctl status radicale

nginx reverse proxy

location /dav/ {
    proxy_pass http://localhost:5232/;
    proxy_set_header X-Script-Name /dav;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass_header Authorization;
    proxy_set_header Authorization $http_authorization;
}

Both Authorization headers needed — nginx strips it without explicit passthrough!

Migrating from Nextcloud

Calendar export — Nextcloud UI:
Calendar → Edit → Export → saves `.ics`
Radicale requires **one event per file** — split the export:

import re, os

with open('personal.ics', 'r') as f:
    content = f.read()

header = content[:content.index('BEGIN:VEVENT')]
footer = 'END:VCALENDARn'
events = re.findall('BEGIN:VEVENT.*?END:VEVENT', content, re.DOTALL)
os.makedirs('/srv/radicale/split', exist_ok=True)

for event in events:
    uid = re.search('UID:(.*?)n', event).group(1).strip()
    with open(f'/srv/radicale/split/{uid}.ics', 'w') as f:
        f.write(header)
        f.write(event + 'n')
        f.write(footer)

print(f'Split {len(events)} events')

**Contacts export** — Nextcloud UI: Contacts → Settings → Export → saves `.vcf` Same split needed for contacts:

import re, os

with open('contacts.vcf', 'r') as f:
    content = f.read()

contacts = re.findall('BEGIN:VCARD.*?END:VCARD', content, re.DOTALL)
os.makedirs('/srv/radicale/split_contacts', exist_ok=True)

for contact in contacts:
    uid = re.search('UID:(.*?)n', contact)
    uid = uid.group(1).strip() if uid else f'contact_{contacts.index(contact)}'
    with open(f'/srv/radicale/split_contacts/{uid}.vcf', 'w') as f:
        f.write(contact + 'n')

print(f'Split {len(contacts)} contacts')

**Import via curl:**

# Calendar events
for f in /srv/radicale/split/*.ics; do
    uid=$(basename "$f" .ics)
    curl -s -u lsces:password 
         -X PUT 
         -H "Content-Type: text/calendar" 
         --data-binary @"$f" 
         "https://rdm1.uk/dav/lsces/calendar/${uid}.ics"
    echo "$uid"
done

# Contacts
for f in /srv/radicale/split_contacts/*.vcf; do
    uid=$(basename "$f" .vcf)
    curl -s -u lsces:password 
         -X PUT 
         -H "Content-Type: text/vcard" 
         --data-binary @"$f" 
         "https://rdm1.uk/dav/lsces/contacts/${uid}.vcf"
    echo "$uid"
done

Client configuration

  • Thunderbird — Add new calendar/contacts:
  • Network → CalDAV/CardDAV
  • URL: https://yourdomain/dav/username/calendar/
  • DAVx⁵ (Android) — Add account:
  • Base URL:https://yourdomain/dav/username/
  • Auto-discovers both calendar and contacts

Developed with Claude AI assistance - Anthropic - April 2026