Home calendar service setup

Setup Home Calendar Service

We earn commissions when you shop through the links on this page, at no additional cost to you.

I stopped trusting Google Calendar years ago. Every calendar entry gets mined, every scheduling pattern analyzed, every meeting time leaked into shadow profiles. When I decided to self-host, a calendar service was non-negotiable. After testing both Nextcloud and Radicale, I settled on a lightweight Radicale instance paired with Nextcloud for occasional sync—the best of both worlds without the surveillance overhead.

Why Self-Host Your Calendar?

Calendar data is intimate. It reveals your routine, your priorities, your health appointments, your financial meetings. Cloud calendar vendors don't just store events; they timestamp your behavior, correlate it with email metadata, and sell insights to advertisers. Self-hosting gives you absolute control.

I also needed something that worked across devices without vendor lock-in. CalDAV (Calendaring Extensions to WebDAV) is the open standard that makes this possible. Any client that speaks CalDAV—whether it's GNOME Calendar, Thunderbird, Apple Calendar, or Nextcloud—can sync seamlessly with your home server.

Radicale: The Lightweight Choice

Radicale is a minimal CalDAV and CardDAV server written in Python. It's stateless, requires minimal resources, and scales from a Raspberry Pi to production environments. I prefer it over Nextcloud when calendars are the only requirement because it demands less CPU, memory, and storage.

Here's my Docker Compose setup for Radicale with persistent storage and basic authentication:

version: '3.8'
services:
  radicale:
    image: tomsquest/docker-radicale:latest
    container_name: radicale
    ports:
      - "5232:5232"
    volumes:
      - ./radicale/data:/data
      - ./radicale/config:/etc/radicale
    environment:
      - RADICALE_AUTHENTICATION_TYPE=htpasswd
      - RADICALE_AUTHENTICATION_HTPASSWD_FILENAME=/etc/radicale/.htpasswd
      - RADICALE_STORAGE_FILESYSTEM_FOLDER=/data/collections
    restart: unless-stopped
    networks:
      - homelab

Before starting the container, create the authentication file:

mkdir -p radicale/config radicale/data
cd radicale/config

# Install htpasswd utility (on Ubuntu/Debian)
sudo apt-get install apache2-utils

# Create .htpasswd with your username (you'll be prompted for password)
htpasswd -c .htpasswd myusername

# Fix permissions
chmod 644 .htpasswd
Tip: Use a strong password. This file controls access to all your calendars. I regenerate mine every six months and rotate credentials when adding new devices.

Start the service with docker-compose up -d. Radicale will be available at http://localhost:5232. The initial login creates a default calendar automatically.

Configuring Client Access

Once Radicale is running, connecting clients is straightforward. I'll show you GNOME Calendar (Linux), Thunderbird (cross-platform), and iOS since those cover most of my devices.

GNOME Calendar: Open Settings → Online Accounts → Add Account → Select "CalDAV" → Enter:

Thunderbird: Open Calendar view → Right-click calendar list → Create New Calendar → Choose "On the Network" → Add:

iOS (Apple Calendar): Settings → Mail → Accounts → Add Account → Other → Add CalDAV Account:

Watch out: If connecting over the internet (not just local LAN), you absolutely need HTTPS. Sending credentials over HTTP is a security disaster. Either use a reverse proxy with TLS (Caddy, Traefik) or restrict to local access only.

Adding a Reverse Proxy for HTTPS

If you want calendar access from outside your home network, a reverse proxy with automatic TLS is essential. I use Caddy because it handles Let's Encrypt renewal painlessly.

Update your Docker Compose to use a shared network, then add Caddy:

version: '3.8'
services:
  radicale:
    image: tomsquest/docker-radicale:latest
    container_name: radicale
    ports:
      - "127.0.0.1:5232:5232"
    volumes:
      - ./radicale/data:/data
      - ./radicale/config:/etc/radicale
    environment:
      - RADICALE_AUTHENTICATION_TYPE=htpasswd
      - RADICALE_AUTHENTICATION_HTPASSWD_FILENAME=/etc/radicale/.htpasswd
      - RADICALE_STORAGE_FILESYSTEM_FOLDER=/data/collections
    restart: unless-stopped
    networks:
      - homelab

  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/data:/data
      - ./caddy/config:/config
    restart: unless-stopped
    networks:
      - homelab

networks:
  homelab:
    driver: bridge

Create a Caddyfile in your project root:

calendar.example.com {
  reverse_proxy radicale:5232
  
  # Optional: Basic auth layer
  basicauth / {
    myusername HASHED_PASSWORD_HERE
  }
}

# Generate hashed password: caddy hash-password "your_password"

Replace calendar.example.com with your actual domain. Caddy will automatically fetch a certificate from Let's Encrypt and keep it renewed.

Nextcloud Integration (Optional)

If you already run Nextcloud for files, notes, and contacts, you get calendars built-in. But I prefer Radicale for its simplicity. However, Nextcloud and Radicale can coexist—Nextcloud can sync its calendars to Radicale as a backup.

In Nextcloud, install the "Sync my calendar" app, configure it to push to your Radicale instance, and you have automatic backup. This adds redundancy without the resource overhead of running Nextcloud as your primary calendar server.

Maintenance and Backups

Radicale stores calendars as ICS files in the data directory. Backup is trivial—just copy the entire radicale/data folder periodically:

#!/bin/bash
BACKUP_DIR="/mnt/backup/radicale"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)

mkdir -p "$BACKUP_DIR"
cp -r radicale/data "$BACKUP_DIR/radicale-$TIMESTAMP"

# Keep last 30 days of backups
find "$BACKUP_DIR" -type d -name "radicale-*" -mtime +30 -exec rm -rf {} \;

I run this daily via cron. Since calendars are text-based ICS files, they compress exceptionally well—a year of events typically occupies under 100 KB.

Performance on Limited Hardware

Radicale runs comfortably on a Raspberry Pi 4 with under 50 MB RAM usage. GNOME Calendar syncs in milliseconds, and even bulk imports of 500+ events complete in seconds. The bottleneck is usually network latency, not the server.

If you're running on older hardware, keep the authentication method simple (htpasswd, not complex LDAP). Avoid enabling WebDAV explorers or plugins that scan collections on every request—stick to the CalDAV core functionality.

Privacy Considerations

Self-hosting calendar is a privacy win, but be mindful: your server's IP logs contain timing data. If threat modeling includes nation-state adversaries, add Cloudflare Tunnel or WireGuard before Caddy to obfuscate your origin IP. For most people, standard TLS is sufficient.

Also, encrypt calendar backups at rest. If backups live on a NAS or cloud storage, apply file-level encryption with tools like age or gpg.

Next Steps

Once your calendar is self-hosted, consider adding contacts (CardDAV) with the same Radicale instance—it supports both calendar and address book protocols. From there, integrate with a personal task manager like Vikunja or Plane for unified scheduling. Your calendar becomes the source of truth, not Google's.

Discussion

```