1.223 CalDAV Server Libraries (Python)#


Explainer

Domain Explainer: CalDAV Servers#

What Problem Does This Solve?#

Imagine you run a hardware store and track all your delivery schedules in a custom spreadsheet app. Your delivery drivers want to see their schedules on their phones, but they use different calendar apps: some use Apple Calendar, some use Google Calendar, some use Thunderbird on their laptops.

You could build a custom mobile app that shows schedules. But your drivers already have calendar apps they know and love. What if you could just make their existing calendar apps show your delivery schedules automatically?

That is what a CalDAV server does. It speaks the universal language of calendars (CalDAV protocol) so any calendar app can connect to it and display your data as if it were a regular calendar. Your spreadsheet data becomes visible in Apple Calendar, Thunderbird, and any other app that speaks CalDAV.

Why Does This Domain Matter?#

Calendar apps are one of the most-used applications on any device. People check their calendar reflexively throughout the day. If your data does not appear in someone’s calendar, it might as well not exist.

CalDAV (Calendaring Extensions to WebDAV) is the open standard that makes calendar sync possible. It is what Apple Calendar uses to sync with iCloud. It is what Thunderbird uses to connect to shared calendars. It is the reason you can see your work calendar on your phone.

The key insight: You do not need to build a calendar app. You need to build a CalDAV server that serves your data in the format calendar apps already understand. Let Apple, Mozilla, and Google handle the UI.

The Hardware Store Analogy#

Think of CalDAV like a universal plug adapter for calendars:

  • Your data is the foreign plug (unique shape, only your system understands it)
  • Calendar apps are the wall outlets (standard shape, widely available)
  • A CalDAV server is the adapter that makes your plug fit any outlet

Without the adapter, you need a custom outlet for every device (build custom apps). With the adapter, any standard device works immediately.

What is in the Adapter?#

The CalDAV “adapter” handles three things:

  1. Translation: Your task data (JSON from Vikunja, rows from a database) gets converted to iCalendar format (the universal calendar data format, like PDF is for documents).

  2. Protocol: Calendar apps do not just download a file. They have a conversation with the server: “What calendars do you have?” “What changed since I last checked?” “I moved this event to Tuesday.” The CalDAV protocol defines this conversation.

  3. Sync: Calendar apps are smart about bandwidth. They only fetch what changed since last time (using tokens called CTags and ETags). Your server must track changes efficiently.

Key Concepts#

CalDAV vs. iCalendar vs. ICS#

These terms are often confused:

  • iCalendar (RFC 5545): A data format for calendar events, like JSON but for calendars. A single event looks like: BEGIN:VEVENT ... SUMMARY:Team Meeting ... END:VEVENT
  • ICS file: A file containing iCalendar data (the .ics extension). Static, read-only.
  • CalDAV (RFC 4791): A protocol for reading AND writing calendar data over the network. Built on top of WebDAV (itself built on HTTP). Supports sync, discovery, and bidirectional editing.

Analogy: iCalendar is like a printed receipt (data format). An ICS file is like a PDF of that receipt (static file). CalDAV is like a live bank account (interactive, bidirectional, syncable).

VEVENT vs. VTODO#

iCalendar defines different component types:

  • VEVENT: A calendar event with a start time and end time. Shows as a block on your calendar timeline. “Meeting at 2pm.”
  • VTODO: A task/to-do with a due date (and optionally a start date). Shows in task lists. “Fix login bug, due Friday.”

Most calendar apps display VEVENTs prominently but hide VTODOs in a separate “tasks” or “reminders” section. If you want tasks to appear on the calendar timeline, serve them as VEVENTs instead of VTODOs.

WebDAV: The Foundation#

CalDAV extends WebDAV (Web Distributed Authoring and Versioning), which extends HTTP. WebDAV adds file system operations to the web:

  • PROPFIND: “What properties does this resource have?” (like ls -l)
  • PROPPATCH: “Change these properties” (like chmod)
  • MKCOL: “Create a directory” (like mkdir)
  • REPORT: “Run this query and give me results” (like find with filters)

CalDAV adds calendar-specific semantics on top: calendar collections, time-range queries, component filtering.

Who Uses CalDAV Servers?#

  1. Self-hosters: People running Radicale or Nextcloud at home to replace Google Calendar
  2. Enterprise: Companies running Zimbra, SOGo, or Cyrus for shared corporate calendars
  3. Developers: Building integrations that serve custom data as calendar events
  4. IoT: Home automation systems that use calendar events to schedule device actions

The Landscape in One Sentence#

Radicale is the only actively-maintained Python CalDAV server that lets you plug in a custom data source, making it the obvious choice for serving non-calendar data (like tasks from Vikunja) through standard calendar apps.

S1: Rapid Discovery

S1 Approach: Rapid Discovery#

Pass: S1-rapid Topic: CalDAV Server Libraries (Python) Focus: WHAT Python libraries exist for building CalDAV servers? (Speed-focused, ecosystem-driven)

Methodology#

S1 is a shopping comparison, not a tutorial. The goal is to answer: “WHICH library can I use to serve CalDAV?” not “HOW to implement CalDAV.”

Research Questions#

  1. What server libraries exist for CalDAV in Python?
  2. Can existing servers be used as libraries with custom storage backends?
  3. How mature is each option? (Download stats, maintenance activity, community size)
  4. What are the trade-offs? (Flexibility, compliance, maintenance risk)

Information Sources#

  • PyPI: Download statistics, version history, dependencies
  • GitHub: Stars, contributors, commit activity, issue response time
  • RFC compliance: Which standards does each library implement?
  • Plugin ecosystems: Which servers support custom storage backends?
  • Client compatibility: Which clients work with each server?

What S1 Does NOT Include#

  • Installation tutorials - Defer to official docs
  • Code samples - S2 covers technical implementation
  • Step-by-step guides - S3 covers use case walkthroughs
  • Protocol specification details - S2 covers RFC internals

Scope Note#

This topic covers server-side CalDAV: libraries that let you serve calendar data over CalDAV protocol. The companion topic 1.220 covers client-side CalDAV (caldav, icalendar, vobject). There is deliberate overlap with 1.220 on parsing libraries (icalendar, vobject) since any CalDAV server needs them.


Other Options#

WsgiDAV (WebDAV Only - No CalDAV)#

Package: WsgiDAV Repository: mar10/wsgidav Stars: 1,165 | Downloads: ~50K/month | License: MIT

WsgiDAV is a generic WebDAV server based on WSGI. It handles WebDAV (RFC 4918) but does not implement CalDAV (RFC 4791). CalDAV support was requested in issue #5 but is deferred indefinitely.

Relevance: Could theoretically be extended with CalDAV by implementing the REPORT method, calendar-query, calendar-multiget, and CalDAV-specific properties. However, this amounts to implementing CalDAV from scratch on top of a WebDAV layer - significant effort with no existing examples.

Verdict: Not viable for CalDAV without massive custom development. The MIT license is attractive, but the effort negates any advantage over Radicale.


py-webdav (Experimental)#

Repository: JensPfeifle/py-webdav Stars: 0 | License: MIT | Last commit: Jan 2026

A Python library claiming WebDAV, CalDAV, and CardDAV support. Tightly coupled to the INFORM API system (a specific university scheduling system). 413 commits but zero stars suggests this is a personal/institutional project.

Relevance: Demonstrates that someone has built a CalDAV server in Python from scratch, but the tight coupling to INFORM makes it unusable as a general-purpose library.

Verdict: Not viable as a dependency. May be useful as reference code for understanding CalDAV server implementation.


Apple Calendar Server (ccs-calendarserver) - Archived#

Repository: apple/ccs-calendarserver License: Apache-2.0

Apple’s open-source CalDAV/CardDAV server, once the reference implementation. Written in Python using Twisted. Officially archived - the developers have moved on, and the project requires significant maintenance due to Python ecosystem changes.

Relevance: Was the most complete CalDAV implementation ever written in Python (full RFC 6638 scheduling support). Uses ccs-pycalendar for iCalendar parsing. The codebase is large and complex.

Verdict: Dead project. Do not use for new development. May be useful as a reference for understanding CalDAV scheduling protocol.


sabre/dav (PHP - Reference Only)#

Website: sabre.io Repository: sabre-io/dav Stars: 8,200+ | License: BSD-3-Clause

The gold standard CalDAV server library, but written in PHP. Powers Nextcloud, ownCloud, and Baikal calendars. Excellent documentation, including the best guide for building CalDAV clients.

Relevance: Not usable from Python, but its documentation at sabre.io/dav/building-a-caldav-client/ is the best resource for understanding CalDAV protocol requirements. The architecture (pluggable backends, middleware stack) is a model for how a Python CalDAV server framework could be designed.

Verdict: PHP only. Use as documentation and architecture reference.


Supporting Libraries (Not Servers)#

These libraries are essential building blocks for any CalDAV server implementation:

icalendar#

Downloads: 4.7M/month | Version: 6.3.2 | License: BSD

The de facto standard for iCalendar (RFC 5545) parsing in Python. Any CalDAV server must parse and generate iCalendar data. Covered in depth in topic 1.220.

vobject#

Downloads: 783K/month | Version: 1.x | License: Apache-2.0

Legacy iCalendar/vCard parser. Still used by Radicale internally. Covered in topic 1.220.

defusedxml#

Downloads: 21M/month | License: PSF-2.0

Secure XML parsing. CalDAV uses XML extensively for PROPFIND/REPORT requests and responses. Required to prevent XML bomb attacks.

lxml#

Downloads: 20M/month | License: BSD

Fast XML processing. CalDAV servers need to parse and generate XML for WebDAV properties, REPORT responses, and multiget queries.


Radicale#

Package: Radicale Repository: Kozea/Radicale Maintainer: Kozea (French web agency) + community contributors First Release: 2009 (17 years of history)

Quick Stats#

MetricValueAssessment
Monthly Downloads21KNiche but established
Latest Version3.6.1 (Feb 2026)Active, recent release
Release FrequencyEvery few monthsSteady maintenance
Python Support3.8+Modern Python
LicenseGPL-3.0Copyleft (affects distribution)
GitHub Stars4,500Strong community
Contributors46+Healthy contributor base
Commits3,842Mature codebase

What It’s Good For#

Primary use case: Self-hosted CalDAV/CardDAV server with pluggable storage

Strengths:

  • The most popular Python CalDAV server by a wide margin
  • Plugin architecture for authentication, rights, and storage
  • Custom storage backends via BaseStorage subclass
  • Built-in filesystem storage works out of the box
  • Minimal dependencies, easy deployment
  • Supports events, todos, journals, business cards
  • Works with Apple Calendar, Thunderbird, DAVx5, Evolution, and many others
  • Built-in TLS support and multiple auth backends

Limitations:

  • GPL-3.0 license (copyleft, may affect how you distribute)
  • Storage plugin API is not formally documented beyond source code
  • No built-in database backend (filesystem only, custom plugins needed for DB)
  • Single-threaded by design (relies on external WSGI server for concurrency)
  • No scheduling support (RFC 6638) - cannot handle meeting invitations
  • No ASGI support (WSGI only)

Storage Plugin Architecture#

The key differentiator: Radicale supports custom storage backends via plugins.

Configuration:

[storage]
type = my_custom_storage_module

Required classes:

  • Storage extending radicale.storage.BaseStorage
  • Collection (inner class) with methods for item CRUD

Existing storage plugins (proving the pattern works):

  • radicale-storage-etesync - EteSync API backend
  • radicale-sirius-plugin - CVUT Sirius REST API backend
  • radicale-storage-family-scheduler - Scheduling overlay on filesystem
  • Radicale-DecSync - Decentralized sync backend

Methods to implement (from BaseStorage/BaseCollection):

  • discover() - Find collections at a path
  • create_collection() - Create new calendar/address book
  • list() - List items in a collection
  • get() - Retrieve a single item
  • upload() - Create/update an item
  • delete() - Remove an item
  • get_meta() / set_meta() - Collection properties
  • acquire_lock() - Concurrency control
  • last_modified - Timestamp property

Maturity Indicators#

Maintenance health: Strong

  • Latest release: Feb 2026 (v3.6.1)
  • Active issue triage on GitHub
  • Regular security updates
  • 3,842 commits over 17 years

Community health: Strong

  • 4,500 GitHub stars
  • Multiple third-party storage plugins
  • Referenced in every CalDAV comparison
  • Packaged in most Linux distros (Debian, Arch, Fedora, NixOS)

RFC Compliance#

RFCDescriptionSupport
4791CalDAV corePartial (no MKCALENDAR on some paths)
4918WebDAVSupported (underlying protocol)
6352CardDAVSupported
6578Collection syncSupported (sync-token/ctag)
6638CalDAV schedulingNot supported
6764SRV discoveryNot supported (client-side concern)
5397Current principalSupported

Client Compatibility#

Works with: Apple Calendar (macOS/iOS), Thunderbird, DAVx5 (Android), Evolution (GNOME), GNOME Calendar, Kontact (KDE), vdirsyncer, tasks.org, and many more.

When to Choose Radicale#

  • You need a CalDAV server with custom storage (database, API, etc.)
  • You want a proven, actively maintained Python server
  • GPL-3.0 is acceptable for your use case
  • You do not need meeting scheduling (RFC 6638)
  • You want the largest ecosystem of existing plugins and documentation

S1 Recommendation: CalDAV Server Libraries#

Quick Decision Matrix#

LibraryCustom Backend?RFC ComplianceMaintenanceLicenseVerdict
RadicaleYes (plugin API)GoodActive (Feb 2026)GPL-3.0Best choice
XandikosNo (Git only)ExcellentActive (Jan 2026)GPL-3.0Good if Git storage is OK
WsgiDAVN/ANo CalDAVActiveMITNot applicable
py-webdavHardcodedUnknownActiveMITToo niche
Apple CalServYesExcellentArchivedApache-2.0Dead project

Recommendation#

Use Radicale with a custom storage plugin.

Radicale is the only viable option for serving CalDAV from a custom backend (database, API, etc.) in Python. The storage plugin API is proven by multiple third-party plugins, including at least one that fetches data from a REST API (radicale-sirius-plugin).

For the Vikunja Use Case Specifically#

There are two viable approaches:

  1. Use Vikunja’s built-in CalDAV (simplest): Vikunja already exposes tasks as VTODO via CalDAV at /dav/. This is functional but alpha-quality with known iOS bugs.

  2. Radicale + Vikunja API plugin (more control): Write a Radicale storage plugin that reads/writes tasks via Vikunja’s REST API, exposing them as VEVENT (calendar events, not just tasks). This gives you control over how tasks appear in calendar apps.

Key Insight#

The landscape is sparse. There is no dedicated “CalDAV server framework” in Python - just two complete servers (Radicale, Xandikos) and a handful of experiments. If Radicale’s plugin API is insufficient, the next step is implementing CalDAV from scratch on top of a WSGI framework, which is a multi-week effort.

What S2 Should Investigate#

  • Radicale’s BaseStorage and BaseCollection API in detail
  • The radicale-sirius-plugin source code as a reference implementation
  • Minimum CalDAV subset needed for Apple Calendar + Thunderbird
  • Effort estimate for a from-scratch implementation vs. Radicale plugin

Xandikos#

Package: xandikos Repository: jelmer/xandikos Maintainer: Jelmer Vernooij (Debian developer, Samba contributor) First Release: 2017

Quick Stats#

MetricValueAssessment
Monthly Downloads1,617Small user base
Latest Version0.3.3 (Jan 2026)Still in 0.x (pre-stable)
Release FrequencyEvery few monthsActive maintenance
Python Support3.8+ (CPython + PyPy)Good compatibility
LicenseGPL-3.0Copyleft
GitHub Stars554Modest community

What It’s Good For#

Primary use case: Lightweight, Git-backed CalDAV/CardDAV server

Strengths:

  • Excellent RFC compliance (most comprehensive of any Python CalDAV server)
  • Git-backed storage gives you free version history and backup
  • CalDAV (RFC 4791) + CardDAV (RFC 6352) in one server
  • Supports collection sync (RFC 6578), extended MKCOL (RFC 5689)
  • Calendar availability (RFC 7953) - rare feature
  • Current principal discovery (RFC 5397)
  • Tested with 18+ clients (Thunderbird, Evolution, DAVx5, iOS, home-assistant)
  • Pure Python with minimal dependencies

Limitations:

  • No custom storage backends - Git is the only option, hard-coded
  • No multi-user support (single-user design)
  • No CalDAV scheduling (RFC 6638)
  • No timezone service (RFC 7809)
  • Pre-1.0 version (API may change)
  • Small community (554 stars vs Radicale’s 4,500)
  • No WSGI/ASGI interface (standalone HTTP server only)

Dependencies#

  • Dulwich - Pure Python Git library
  • Jinja2 - Templating
  • icalendar - iCalendar parsing
  • defusedxml - Secure XML parsing

RFC Compliance (Best in Class)#

RFCDescriptionSupport
4791CalDAV coreFull
4918WebDAVFull (partial LOCK)
6352CardDAVFull
5689Extended MKCOLFull
6578Collection syncFull
7953Calendar availabilityFull
5397Current principalFull
6638CalDAV schedulingNot supported
7809CalDAV timezonesNot supported
7529WebDAV quotaNot supported

Maturity Indicators#

Maintenance health: Moderate

  • Active development by maintainer
  • Regular releases
  • Still pre-1.0 after 8 years

Community health: Small but quality

  • Maintainer is a well-known open-source contributor (Samba, Debian)
  • Used in personal setups and small deployments
  • Not widely packaged in distros compared to Radicale

When to Choose Xandikos#

  • You want the best RFC compliance in a Python CalDAV server
  • Git-backed storage is acceptable (or desired for versioning)
  • Single-user deployment is sufficient
  • You do not need custom storage backends
  • You want a lightweight, focused implementation

When NOT to Choose Xandikos#

  • You need custom storage (API, database) - Xandikos only supports Git
  • You need multi-user support
  • You need meeting scheduling (RFC 6638)
  • You want a large community and ecosystem of plugins
S2: Comprehensive

S2 Approach: Comprehensive Analysis#

Pass: S2-comprehensive Topic: CalDAV Server Libraries (Python) Focus: HOW do these libraries work technically? Architecture deep-dive.

Methodology#

S2 digs into the technical architecture and implementation details of the recommended libraries. Focus on Radicale’s plugin API since that is the primary recommendation.

Research Questions#

  1. What is Radicale’s storage plugin API? Full method signatures and contracts
  2. How does radicale-sirius-plugin work? Real-world API-backed storage reference
  3. What does CalDAV protocol require? HTTP methods, XML payloads, iCalendar format
  4. What is the minimum viable CalDAV? Smallest subset for Apple Calendar + Thunderbird
  5. How would a from-scratch implementation work? Architecture and effort estimate

Technical Areas#

  • Radicale BaseStorage/BaseCollection class hierarchy
  • XML namespace handling (DAV:, CalDAV:, CardDAV:)
  • iCalendar VEVENT/VTODO structure for tasks-as-events
  • WebDAV locking and concurrency model
  • Authentication flow (Basic, Bearer, client certificates)

CalDAV Protocol: Technical Requirements#

Protocol Stack#

CalDAV sits on top of a layered protocol stack:

CalDAV (RFC 4791)          ← Calendar-specific extensions
  ↓ extends
WebDAV (RFC 4918)          ← File system operations over HTTP
  ↓ extends
HTTP/1.1 (RFC 7230-7235)  ← Transport

CalDAV adds calendar-specific semantics to WebDAV: calendar collections, VTODO/VEVENT filtering, time-range queries, and scheduling.

HTTP Methods Required#

Minimum Viable CalDAV#

These methods are required for basic calendar client compatibility:

MethodPurposeWhen Used
OPTIONSCapability discoveryClient initial connection
PROPFINDRead properties of resourcesCalendar listing, sync
REPORTComplex queries (calendar-query, calendar-multiget)Event/task retrieval
GETRetrieve a single calendar objectIndividual event fetch
PUTCreate or update a calendar objectEvent creation/edit
DELETERemove a calendar objectEvent deletion

Optional but Commonly Expected#

MethodPurposeWhen Used
MKCALENDARCreate new calendar collectionUser creates calendar
PROPPATCHModify collection propertiesRename calendar, set color
MKCOLCreate collection (WebDAV)Address book creation
MOVERelocate resourceDrag event between calendars

Not Required for Basic Functionality#

MethodPurposeRFC
LOCK / UNLOCKConcurrent editingRFC 4918
ACLAccess controlRFC 3744
POST (scheduling)Meeting invitationsRFC 6638

Key PROPFIND Properties#

CalDAV clients query specific properties via PROPFIND. A server must support:

Discovery Properties (DAV: namespace)#

  • resourcetype - Is this a calendar? (<D:collection/><C:calendar/>)
  • displayname - Human-readable name
  • current-user-principal - User’s principal URL (RFC 5397)
  • principal-URL - Same as above in some clients

Calendar Properties (CalDAV namespace: urn:ietf:params:xml:ns:caldav)#

  • calendar-home-set - URL where user’s calendars live
  • calendar-description - Calendar description text
  • supported-calendar-component-set - Which components: VEVENT, VTODO, VJOURNAL
  • calendar-data - The actual iCalendar data (used in REPORT responses)
  • calendar-timezone - Default timezone for the calendar

Sync Properties#

  • getctag (CS: namespace) - Collection tag, changes when any item changes
  • getetag - Entity tag for individual items
  • sync-token - WebDAV sync token (RFC 6578)

REPORT Types#

calendar-query (Most Important)#

Filters calendar objects by component type, time range, or property values.

Request example (simplified):

<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"
                  xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
    <C:calendar-data/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:time-range start="20260301T000000Z" end="20260401T000000Z"/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>

Response: DAV:multistatus with matching items.

calendar-multiget#

Fetches specific items by URL (used after sync-token detects changes).

<C:calendar-multiget xmlns:C="urn:ietf:params:xml:ns:caldav"
                     xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
    <C:calendar-data/>
  </D:prop>
  <D:href>/user/calendar/event-1.ics</D:href>
  <D:href>/user/calendar/event-2.ics</D:href>
</C:calendar-multiget>

sync-collection (RFC 6578)#

Efficient incremental sync - client sends last sync-token, server returns only changes.

iCalendar Format for Tasks#

For serving Vikunja tasks, the iCalendar output must be valid VCALENDAR containing VTODO or VEVENT components.

Task as VTODO#

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Custom//CalDAV//EN
BEGIN:VTODO
UID:[email protected]
DTSTAMP:20260309T120000Z
SUMMARY:Fix login bug
DESCRIPTION:The login page crashes on Safari
DUE:20260315T170000Z
PRIORITY:1
STATUS:IN-PROCESS
PERCENT-COMPLETE:50
CATEGORIES:bug,frontend
CREATED:20260301T090000Z
LAST-MODIFIED:20260309T120000Z
END:VTODO
END:VCALENDAR

Task as VEVENT (Alternative)#

If you want tasks to appear as calendar events (blocks on the timeline), use VEVENT:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Custom//CalDAV//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20260309T120000Z
DTSTART:20260315T090000Z
DTEND:20260315T170000Z
SUMMARY:Fix login bug
DESCRIPTION:The login page crashes on Safari
STATUS:CONFIRMED
CATEGORIES:bug,frontend
CREATED:20260301T090000Z
LAST-MODIFIED:20260309T120000Z
END:VEVENT
END:VCALENDAR

Vikunja Field Mapping#

Vikunja FieldVTODO PropertyVEVENT Property
titleSUMMARYSUMMARY
descriptionDESCRIPTIONDESCRIPTION
due_dateDUEDTEND
start_dateDTSTARTDTSTART
priorityPRIORITY (1=high, 9=low)N/A
doneSTATUS:COMPLETEDSTATUS:CANCELLED
percent_donePERCENT-COMPLETEN/A
labelsCATEGORIESCATEGORIES
createdCREATEDCREATED
updatedLAST-MODIFIEDLAST-MODIFIED
idUID (prefixed)UID (prefixed)

Client Discovery Flow#

When a client first connects, the typical handshake is:

1. OPTIONS /                          → Server returns DAV: 1, 2, calendar-access
2. PROPFIND /                         → Find current-user-principal
3. PROPFIND /principals/user/         → Find calendar-home-set
4. PROPFIND /user/                    → List calendars (depth: 1)
5. PROPFIND /user/calendar/           → Get calendar properties + ctag
6. REPORT /user/calendar/             → calendar-query or sync-collection

This discovery chain is the most common source of client compatibility issues. If any step returns unexpected results, the client will fail silently or show no calendars.

Minimum Viable Implementation Checklist#

For Apple Calendar + Thunderbird compatibility:

  • OPTIONS: Return DAV: 1, 2, calendar-access header
  • PROPFIND on /: Return current-user-principal
  • PROPFIND on principal: Return calendar-home-set
  • PROPFIND on home: Return list of calendars with resourcetype, displayname, getctag
  • PROPFIND on calendar: Return calendar properties + item list
  • REPORT calendar-query: Filter by component type and time range
  • REPORT calendar-multiget: Fetch multiple items by URL
  • GET individual items: Return valid iCalendar data
  • PUT items: Accept iCalendar data, return ETag
  • DELETE items: Remove items, update ctag
  • ETag handling: Correct ETags on every response for caching
  • Basic Authentication: Username/password over HTTPS

Feature Comparison: CalDAV Server Options#

Server Capabilities#

FeatureRadicale 3.6Xandikos 0.3WsgiDAV 4.3From Scratch
CalDAV (RFC 4791)YesYesNoYour impl
CardDAV (RFC 6352)YesYesNoOptional
Custom storagePlugin APINo (Git only)N/AYes
Database backendVia pluginNoN/AYes
API backendVia plugin (proven)NoN/AYes
Multi-userYesNoYesYour impl
AuthenticationPluggableBasicPluggableYour impl
TLSBuilt-inReverse proxyBuilt-inYour impl
WSGIYesNoYesOptional
ASGINoNoNoOptional
StandaloneYesYesYesYour impl

RFC Compliance Comparison#

RFCDescriptionRadicaleXandikosMin Viable
4791CalDAV corePartialFullRequired
4918WebDAVYesYes (partial LOCK)Required subset
3744ACLNoNoNot needed
5397Current principalYesYesRequired
5689Extended MKCOLNoYesNot needed
6352CardDAVYesYesNot needed
6578Collection syncYesYesRecommended
6638CalDAV schedulingNoNoNot needed
6764SRV discoveryNoNoNot needed
7529QuotaNoNoNot needed
7809TimezonesNoNoNot needed
7953AvailabilityNoYesNot needed

Client Compatibility#

ClientRadicaleXandikosNotes
Apple Calendar (macOS)TestedTestedExpects Apple-specific properties
Apple Calendar (iOS)TestedTestedMore strict than macOS
ThunderbirdTestedTestedVery forgiving, works with most servers
DAVx5 (Android)TestedTestedStandard-compliant client
Evolution (GNOME)TestedTestedGood standard compliance
GNOME CalendarTestedTestedBasic CalDAV support
Google CalendarRead-only CalDAVN/AGoogle’s CalDAV is read-only for external
OutlookNot testedNot testedUses Exchange, not CalDAV natively
tasks.orgTestedTestedVTODO focused
home-assistantTestedTestedRead-only integration
vdirsyncerTestedTestedCLI sync tool

Performance Characteristics#

AspectRadicaleXandikosFrom Scratch
Concurrency modelSingle-threaded + lockSingle-threadedYour choice
Storage I/OFilesystem (default)Git operationsYour choice
Startup timeFast (<1s)Fast (<1s)Depends
Memory footprintLow (~20MB)Low (~30MB)Depends
Max calendarsThousandsHundredsDepends on backend
Max events/calendarTens of thousandsLimited by GitDepends on backend
Sync efficiencyCTag + sync-tokenCTag + sync-tokenMust implement

Deployment Options#

AspectRadicaleXandikosFrom Scratch
pip installYesYesYour packaging
DockerOfficial imageCommunity imageYour Dockerfile
SystemdDocumentedDocumentedYour unit file
Reverse proxyNginx/ApacheNginx/ApacheStandard
Linux packagesDebian, Arch, Fedora, NixOSLimitedNone

License Implications#

LibraryLicenseCan embed in proprietary?Can distribute modified?
RadicaleGPL-3.0No (copyleft)Must open-source
XandikosGPL-3.0No (copyleft)Must open-source
WsgiDAVMITYesYes
icalendarBSDYesYes
vobjectApache-2.0YesYes

GPL-3.0 note: Using Radicale as a library (importing its code) makes your project a derivative work under GPL-3.0. Running Radicale as a separate process (not importing) does not trigger copyleft. For a Radicale storage plugin, the plugin itself must be GPL-3.0 compatible.


Building CalDAV From Scratch: Effort Analysis#

Why Consider From-Scratch?#

Radicale’s storage plugin API is the pragmatic choice, but there are scenarios where building from scratch makes sense:

  • Need ASGI support (Radicale is WSGI only)
  • GPL-3.0 license is unacceptable
  • Need deep control over XML/iCalendar handling
  • Want to embed CalDAV in an existing FastAPI/Django app
  • Need CalDAV scheduling (RFC 6638) which Radicale does not support

Architecture for a Custom CalDAV Server#

┌─────────────────────────────────────────────┐
│ ASGI/WSGI Framework (FastAPI, Flask, Django) │
├─────────────────────────────────────────────┤
│ CalDAV Protocol Layer                        │
│  ├── Method Router (PROPFIND, REPORT, etc.)  │
│  ├── XML Parser/Generator (lxml + defusedxml)│
│  ├── Property Registry (DAV:, CalDAV:, CS:)  │
│  └── Filter Engine (time-range, comp-filter) │
├─────────────────────────────────────────────┤
│ iCalendar Layer                              │
│  ├── Parser (icalendar library)              │
│  ├── Generator (icalendar library)           │
│  └── UID management                          │
├─────────────────────────────────────────────┤
│ Storage Backend (your custom implementation) │
│  ├── Task CRUD operations                    │
│  ├── Query engine (time range, filters)      │
│  └── ETag/CTag management                    │
└─────────────────────────────────────────────┘

Implementation Effort Breakdown#

Phase 1: Minimum Viable (1-2 weeks)#

Goal: Apple Calendar and Thunderbird can discover and read calendars.

ComponentEffortDescription
OPTIONS handler2 hoursReturn DAV capabilities header
PROPFIND handler2-3 daysXML parsing, property resolution, multistatus response
REPORT handler2-3 dayscalendar-query with time-range filter, calendar-multiget
GET handler4 hoursServe individual iCalendar objects
PUT handler1 dayParse incoming iCalendar, validate, store
DELETE handler4 hoursRemove items, update collection ctag
iCalendar conversion1-2 daysMap your data model to VEVENT/VTODO
ETag/CTag management1 dayContent hashing, collection versioning
Basic auth4 hoursHTTP Basic over HTTPS
Client testing2-3 daysDebug Apple Calendar + Thunderbird quirks

Subtotal: ~2 weeks for a senior Python developer

Phase 2: Production Quality (2-4 additional weeks)#

ComponentEffortDescription
sync-collection (RFC 6578)3-5 daysIncremental sync for efficiency
MKCALENDAR1 dayCreate new calendars
PROPPATCH1-2 daysModify calendar properties
Proper XML namespace handling2-3 daysDAV:, CalDAV:, CS:, Apple: namespaces
Error handling2-3 daysProper WebDAV error responses
Client quirk workarounds3-5 daysApple, Google, Thunderbird differences
MOVE support1 dayMove events between calendars
Multi-user2-3 daysPrincipal discovery per user
Rate limiting / caching1-2 daysProtect backend from thundering herd

Subtotal: ~3 weeks additional

Phase 3: Advanced (optional)#

ComponentEffortDescription
RFC 6638 scheduling3-4 weeksMeeting invitations, free/busy
RFC 6764 auto-discovery1 weekDNS SRV/TXT record based discovery
ACL (RFC 3744)2 weeksPer-calendar access control
CalDAV testing suite1-2 weeksAutomated compliance tests

Key Libraries for From-Scratch#

# Core dependencies
icalendar          # iCalendar parsing/generation
lxml               # Fast XML processing
defusedxml         # Secure XML parsing (prevent XXE attacks)

# Framework (pick one)
fastapi + uvicorn  # ASGI, async, modern
flask              # WSGI, simple, proven
django             # Full framework, good for larger apps

# Optional
python-dateutil    # Timezone handling, recurrence rules
pytz               # Timezone database

Critical Implementation Details#

XML Namespace Pitfalls#

CalDAV uses multiple XML namespaces simultaneously. Missing or incorrect namespace handling is the #1 source of client compatibility issues:

NAMESPACES = {
    "D": "DAV:",
    "C": "urn:ietf:params:xml:ns:caldav",
    "CS": "http://calendarserver.org/ns/",
    "ICAL": "http://apple.com/ns/ical/",
}

Apple Calendar expects Apple-specific properties (calendar color, order) in the ICAL: namespace. Thunderbird is more forgiving.

ETag Correctness#

ETags must be:

  • Unique per item version (use content hash)
  • Quoted in HTTP headers: ETag: "abc123"
  • Consistent: same content must produce same ETag
  • Different after any change: clients use ETag to detect modifications

CTag (Collection Tag)#

CTag is an Apple extension (CS: namespace) that acts as a version number for the entire collection. It must change whenever any item in the collection changes. Most clients check CTag first to avoid expensive REPORT queries.

Depth Header#

PROPFIND uses the Depth header:

  • 0 = Only the requested resource
  • 1 = The resource and its direct children
  • infinity = All descendants (CalDAV servers SHOULD refuse this)

Getting depth handling wrong breaks calendar discovery.

Verdict: Radicale Plugin vs. From Scratch#

FactorRadicale PluginFrom Scratch
Time to first working demo2-3 days2 weeks
Client compatibilityInheritedMust test/debug
RFC complianceGood baselineYour responsibility
Maintenance burdenRadicale handles protocolYou maintain everything
LicenseGPL-3.0 (Radicale)Your choice
ASGI supportNoYes (if you choose)
Scheduling (RFC 6638)NoPossible (weeks of work)
FlexibilityLimited to plugin APIUnlimited

Recommendation: Start with a Radicale plugin. Only build from scratch if you hit a hard limitation of the plugin API (most likely: need for ASGI, need for non-GPL license, or need for scheduling).


Radicale Storage Plugin API: Deep Dive#

Architecture Overview#

Radicale v3 uses a two-class storage architecture:

BaseStorage (module-level)
  ├── discover(path) → yields Collection instances
  ├── create_collection(path, ...) → Collection
  ├── acquire_lock(mode) → context manager
  └── Collection (item-level)
       ├── path, owner, is_principal (properties)
       ├── list() → yields (name, etag) tuples
       ├── get(name) → Item or None
       ├── upload(name, item) → Item
       ├── delete(name)
       ├── get_meta() → dict
       ├── set_meta(dict)
       └── last_modified → str

Version History Note#

In Radicale 2.x, there was a single BaseCollection class. In 3.x, this was split into BaseStorage (top-level operations like discover and lock) and BaseCollection (item-level CRUD). Older plugin tutorials may reference the 2.x API.

BaseStorage Class#

The module must export a Storage class that extends radicale.storage.BaseStorage.

Required Methods#

discover(path, depth="0")

  • Entry point for all client requests
  • Must yield Collection instances matching the path
  • depth="0" returns only the exact path; depth="1" returns children too
  • This is the most complex method - must handle principal paths, calendar paths, and item paths

create_collection(href, items=None, props=None)

  • Called when a client creates a new calendar (MKCALENDAR) or address book (MKCOL)
  • href: The collection path (e.g., "user/calendar-name")
  • items: Optional list of vobject items to pre-populate
  • props: Dictionary of collection properties (e.g., {"tag": "VCALENDAR"})
  • Can raise NotImplementedError if collection creation is not supported

acquire_lock(mode, user="")

  • Context manager for read/write locking
  • mode: "r" for shared read lock, "w" for exclusive write lock
  • For simple implementations, always use exclusive locking
  • Must be re-entrant safe

Configuration Access#

The Storage class receives configuration in its constructor, providing access to all Radicale config values. Custom config keys can be added under the [storage] section.

BaseCollection Class#

Each collection (calendar, address book, or principal) is represented as a Collection instance.

Required Properties#

path (str)

  • Collection path without leading/trailing slashes
  • Examples: "user" (principal), "user/calendar" (calendar), "user/calendar/event.ics" (item)

owner (str)

  • Set to the first path component: self.path.split("/", maxsplit=1)[0]

is_principal (bool)

  • True if this is a user’s root collection: bool(self.path) and "/" not in self.path

Required Methods#

list()

  • Yields (name, etag) tuples for all items in the collection
  • name: Filename (e.g., "event-uid.ics")
  • etag: Opaque string that changes when the item changes (use hash or timestamp)

get(href)

  • Returns an Item instance for the given filename, or None if not found
  • The Item must have .serialize() returning valid iCalendar/vCard text

upload(href, item)

  • Stores an item (iCalendar event/todo or vCard contact)
  • item is a vobject-parsed object
  • Returns a tuple (href, item) representing what was stored
  • Must handle both create and update (PUT is idempotent)

delete(href=None)

  • If href is given: delete that item from the collection
  • If href is None: delete the entire collection

get_meta(key=None)

  • If key is None: return all metadata as a dict
  • If key is given: return that specific metadata value
  • Standard keys: "tag" (VCALENDAR/VADDRESSBOOK), "D:displayname", "C:supported-calendar-component-set"

set_meta(props)

  • Store collection metadata (display name, description, color, etc.)
  • props is a dict of key-value pairs

last_modified (str)

  • HTTP-date formatted string of when the collection was last modified
  • Can return current time if real tracking is too expensive

Required Metadata#

For a calendar collection, get_meta() must return at minimum:

{
    "tag": "VCALENDAR",
    "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO"
}

Item Class#

Items returned by get() and upload() must expose:

  • serialize() → str: Returns valid iCalendar text (VCALENDAR with VEVENT/VTODO)
  • uid → str: The iCalendar UID property
  • name → str: The filename (e.g., "event-uid.ics")
  • component_name → str: "VEVENT", "VTODO", etc.
  • etag → str: Content-based hash for caching

Example: API-Backed Storage Flow#

Client: PROPFIND /user/calendar/
  → Radicale calls Storage.acquire_lock("r")
  → Radicale calls Storage.discover("/user/calendar", depth="1")
    → Plugin fetches calendar metadata from API
    → Plugin yields Collection(path="user/calendar")
    → For depth="1", plugin also yields items:
       → Fetches tasks from Vikunja API
       → Converts each task to VTODO iCalendar format
       → Yields Item for each task
  → Radicale formats XML response with properties and items

Client: GET /user/calendar/task-123.ics
  → Radicale calls Collection.get("task-123.ics")
  → Plugin fetches task #123 from Vikunja API
  → Converts to VCALENDAR/VTODO format
  → Returns Item with serialize() → iCalendar text

Client: PUT /user/calendar/task-123.ics (body: updated VTODO)
  → Radicale calls Collection.upload("task-123.ics", parsed_item)
  → Plugin extracts task fields from VTODO (summary, due date, priority)
  → Plugin updates task #123 via Vikunja API
  → Returns updated Item

Real-World Reference: radicale-sirius-plugin#

The Sirius plugin demonstrates API-backed storage:

  1. Fetches events from https://sirius.fit.cvut.cz/api/v1/people/{user}/events.ical
  2. Caches in memory with configurable expiration (default 300s)
  3. Overlays local changes on top of API data
  4. Uses filesystem for storing local modifications (inherits from multifilesystem)
  5. Thread-safe via lock-based concurrency

Key design choice: It does NOT replace the filesystem entirely. Instead, it extends FSCollection (filesystem collection), using the Sirius API as a read-only data source and the filesystem for local edits. This hybrid approach is simpler than a pure API backend.

Concurrency and Locking#

Radicale is inherently single-process. The storage layer’s acquire_lock() provides:

  • File-based locking (default multifilesystem backend)
  • Or custom locking (your plugin)

For an API-backed plugin, locking can be simplified:

  • Read locks: No-op (API handles concurrent reads)
  • Write locks: Simple threading.Lock() for in-process synchronization
  • No need for file-based locks since there is no shared filesystem

Authentication Integration#

Radicale’s auth plugin is separate from storage. The storage plugin receives the authenticated username via the user parameter on acquire_lock() and path routing. For a Vikunja backend, you would:

  1. Use Radicale’s auth plugin to validate credentials
  2. Map the Radicale username to a Vikunja API token
  3. Use that token in API calls within the storage plugin

S2 Recommendation: Technical Analysis#

Summary#

Radicale’s storage plugin API is well-designed and proven for API-backed backends. The radicale-sirius-plugin demonstrates the exact pattern needed for a Vikunja integration. The protocol complexity (XML namespaces, client quirks, ETag management) strongly favors using Radicale over building from scratch.

Key Technical Findings#

  1. Storage plugin API requires ~12 methods - manageable scope, well-documented by existing plugins
  2. The hardest part is iCalendar conversion - mapping Vikunja task fields to VTODO/VEVENT properties
  3. Client quirks are the hidden cost - Apple Calendar, Thunderbird, and DAVx5 each have different expectations
  4. No ASGI CalDAV server exists - if async is required, you must build from scratch
  5. GPL-3.0 is unavoidable for any Radicale-based solution

Architecture Decision#

For the Vikunja use case, the recommended architecture is:

Apple Calendar / Thunderbird / DAVx5
         │
         ▼
    Radicale (CalDAV protocol handling)
         │
         ▼
    Custom Storage Plugin (Python)
         │
         ▼
    Vikunja REST API

The plugin translates between CalDAV operations and Vikunja API calls. Radicale handles all the protocol complexity (XML parsing, client quirks, auth, discovery).

Risk Assessment#

RiskLikelihoodMitigation
Radicale plugin API changesLow (stable since v3.0)Pin Radicale version
Client compatibility issuesMediumTest with Apple Calendar + Thunderbird early
Vikunja API limitationsMediumCheck API supports needed CRUD operations
Performance (API latency)MediumCache Vikunja responses, use CTag for change detection
GPL-3.0 concernsLow (internal use)Run as separate process if needed
S3: Need-Driven

S3 Approach: Need-Driven Discovery#

Pass: S3-need-driven Topic: CalDAV Server Libraries (Python) Focus: WHO needs a Python CalDAV server and WHY? Use cases and personas.

Methodology#

S3 identifies real-world use cases and the personas who would choose each approach. Focus on the “why” behind each decision, not implementation details.

Personas Covered#

  1. Personal productivity hacker - wants tasks in native calendar apps
  2. Small team/startup - needs shared calendar infrastructure
  3. Enterprise integration developer - connecting legacy systems to CalDAV
  4. Open-source self-hoster - replacing cloud calendars
  5. IoT/home automation developer - scheduling device actions
  6. SaaS developer - adding calendar sync as a product feature

S3 Recommendation: Use Case Analysis#

Key Insight#

Every use case except “self-hosted calendar replacement” points to Radicale with a custom storage plugin. The pattern is consistent: developers have data in a custom system and want it visible in standard calendar apps.

Use Case to Library Mapping#

Use CaseRecommended LibraryWhy
Task-to-calendar bridgeRadicale pluginProven API-backed storage pattern
Self-hosted calendarRadicale default or XandikosWorks out of the box
Enterprise integrationRadicale pluginMost flexible, best client compat
IoT schedulingRadicale pluginBidirectional event support
SaaS calendar syncFrom scratch (MIT license)GPL-3.0 unacceptable for SaaS
Multi-source aggregationRadicale pluginPlugin can aggregate multiple APIs

The Common Thread#

CalDAV servers are valuable not because people want another calendar server, but because they want their existing data to appear in calendar apps without building custom UIs. The server is a translation layer, not a destination.

Underserved Need#

No Python CalDAV server supports ASGI natively. This matters for modern Python web apps (FastAPI, Starlette) that want to embed CalDAV as a route alongside their existing API. This gap creates an opportunity for a lightweight CalDAV ASGI middleware library, though none exists today.


Use Case 6: Multi-Source Calendar Aggregation#

Persona: Developer who wants one calendar view combining data from multiple sources (Jira, GitHub issues, on-call schedules, personal tasks)

Problem: Important dates are scattered across 5+ systems. No single view shows everything that demands your time.

Why CalDAV: Create a CalDAV server that aggregates multiple sources into virtual calendars. Each source becomes a calendar in your app. The CalDAV server is the unifying layer.

Solution: Radicale with a custom storage plugin that fetches from multiple APIs on demand.

Why not alternatives:

  • Multiple ICS feeds: No unified sync-token, slow polling, read-only
  • Custom dashboard: Another app to check, not in your existing calendar workflow
  • Zapier/IFTTT: Fragile, limited, costs money at scale

Best library: Radicale with multi-source storage plugin Effort: 3-5 weeks (complexity is in the aggregation logic, not CalDAV)


Use Case 3: Enterprise System Integration#

Persona: Enterprise developer connecting an HR scheduling system to employees’ calendar apps

Problem: The company uses a custom HR/scheduling system (Python/Django) that manages shift schedules. Employees want shifts to appear in their personal calendar apps without manual entry.

Why CalDAV: Employees already have calendar apps on their phones. CalDAV lets you push schedules directly into their existing workflow. No new app to install, no training.

Solution: Radicale storage plugin backed by the HR database, or from-scratch CalDAV on Django.

Why not alternatives:

  • ICS feed: Read-only, employees cannot swap shifts from calendar
  • Email invitations: Spam-like, unreliable, no persistent link
  • Custom mobile app: Expensive to build and maintain, users resent installing another app

Best library: Radicale plugin (fastest) or from-scratch on Django (if GPL is a concern) Effort: 2-4 weeks depending on approach


Use Case 4: IoT / Home Automation Scheduling#

Persona: Home automation developer using Home Assistant who wants to control devices via calendar events

Problem: You want to schedule heating, lighting, irrigation, etc. using a calendar interface. Creating events in Apple Calendar should trigger device actions at the scheduled time.

Why CalDAV: Calendar apps have excellent UX for time-based scheduling. Creating a “Heat living room” event from 6-8pm is more intuitive than programming cron jobs.

Solution: Radicale + storage plugin that translates calendar events to Home Assistant automations, or use Home Assistant’s existing CalDAV integration (read-only).

Why not alternatives:

  • Home Assistant UI: Functional but less intuitive for time scheduling
  • Cron jobs: No visual interface, hard to manage complex schedules
  • Google Calendar API: Vendor dependency, latency, requires internet

Best library: Radicale with custom plugin Effort: 2-3 weeks


Use Case 5: SaaS Calendar Sync Feature#

Persona: SaaS developer adding “sync to calendar” as a product feature

Problem: Your project management SaaS (like Asana, Linear, or Monday) wants to let users see their deadlines in Apple Calendar or Outlook. ICS feeds are read-only and update slowly.

Why CalDAV: Bidirectional sync. Users can reschedule tasks from their calendar app, and changes propagate back to the SaaS. This is a premium feature that competitors lack.

Solution: From-scratch CalDAV implementation (license flexibility) or Radicale plugin (faster to market).

Why not alternatives:

  • ICS subscription: Read-only, no bidirectional sync
  • Google Calendar API: Only works for Google Calendar users
  • Microsoft Graph API: Only works for Outlook users
  • CalDAV: Works with all standards-compliant calendar apps

Best library: From-scratch (avoid GPL) or Radicale plugin (if GPL acceptable) Effort: 4-8 weeks for production-quality from-scratch; 2-3 weeks for Radicale plugin


Use Case 2: Self-Hosted Calendar Replacement#

Persona: Privacy-conscious user replacing Google Calendar with a self-hosted solution

Problem: You want full control over your calendar data. Cloud providers mine your schedule for ad targeting, and you cannot export recurring events reliably.

Why CalDAV: It is the universal calendar protocol. Apple Calendar, Thunderbird, DAVx5, Evolution all speak it natively. One server, all your devices.

Solution: Radicale (default filesystem storage) or Xandikos (Git-backed for version history).

Why not alternatives:

  • Nextcloud: Heavy (PHP + database + web UI), overkill if you only need calendar
  • Google Calendar: Privacy concern, vendor lock-in
  • Local ICS files: No sync between devices

Best library: Radicale (simplest) or Xandikos (if Git versioning appeals) Effort: 30 minutes to deploy


Use Case 1: Task Management to Calendar Bridge#

Persona: Developer using Vikunja/Todoist/Jira who wants tasks visible in Apple Calendar

Problem: Task management apps store rich task data (priorities, labels, due dates, subtasks) but native calendar apps cannot display them. You end up checking two apps, missing deadlines because the task is in Vikunja but your calendar shows a free slot.

Why CalDAV: Native calendar apps already speak CalDAV. Rather than building a custom calendar UI, serve tasks as CalDAV events and let Apple Calendar/Thunderbird display them natively.

Solution: Radicale + custom storage plugin that reads from the task app’s API.

Why not alternatives:

  • ICS feed (read-only): Cannot mark tasks complete from the calendar app
  • Vikunja’s built-in CalDAV: Alpha quality, VTODO only (not visible in all calendar views), iOS bugs
  • Manual sync: Human error, tasks fall through cracks

Best library: Radicale with custom storage plugin Effort: 1-2 weeks for plugin development


Use Cases: Who Needs a Python CalDAV Server?#

Use Case 1: Task Management to Calendar Bridge#

Persona: Developer using Vikunja/Todoist/Jira who wants tasks visible in Apple Calendar

Problem: Task management apps store rich task data (priorities, labels, due dates, subtasks) but native calendar apps cannot display them. You end up checking two apps, missing deadlines because the task is in Vikunja but your calendar shows a free slot.

Why CalDAV: Native calendar apps already speak CalDAV. Rather than building a custom calendar UI, serve tasks as CalDAV events and let Apple Calendar/Thunderbird display them natively.

Solution: Radicale + custom storage plugin that reads from the task app’s API.

Why not alternatives:

  • ICS feed (read-only): Cannot mark tasks complete from the calendar app
  • Vikunja’s built-in CalDAV: Alpha quality, VTODO only (not visible in all calendar views), iOS bugs
  • Manual sync: Human error, tasks fall through cracks

Best library: Radicale with custom storage plugin Effort: 1-2 weeks for plugin development


Use Case 2: Self-Hosted Calendar Replacement#

Persona: Privacy-conscious user replacing Google Calendar with a self-hosted solution

Problem: You want full control over your calendar data. Cloud providers mine your schedule for ad targeting, and you cannot export recurring events reliably.

Why CalDAV: It is the universal calendar protocol. Apple Calendar, Thunderbird, DAVx5, Evolution all speak it natively. One server, all your devices.

Solution: Radicale (default filesystem storage) or Xandikos (Git-backed for version history).

Why not alternatives:

  • Nextcloud: Heavy (PHP + database + web UI), overkill if you only need calendar
  • Google Calendar: Privacy concern, vendor lock-in
  • Local ICS files: No sync between devices

Best library: Radicale (simplest) or Xandikos (if Git versioning appeals) Effort: 30 minutes to deploy


Use Case 3: Enterprise System Integration#

Persona: Enterprise developer connecting an HR scheduling system to employees’ calendar apps

Problem: The company uses a custom HR/scheduling system (Python/Django) that manages shift schedules. Employees want shifts to appear in their personal calendar apps without manual entry.

Why CalDAV: Employees already have calendar apps on their phones. CalDAV lets you push schedules directly into their existing workflow. No new app to install, no training.

Solution: Radicale storage plugin backed by the HR database, or from-scratch CalDAV on Django.

Why not alternatives:

  • ICS feed: Read-only, employees cannot swap shifts from calendar
  • Email invitations: Spam-like, unreliable, no persistent link
  • Custom mobile app: Expensive to build and maintain, users resent installing another app

Best library: Radicale plugin (fastest) or from-scratch on Django (if GPL is a concern) Effort: 2-4 weeks depending on approach


Use Case 4: IoT / Home Automation Scheduling#

Persona: Home automation developer using Home Assistant who wants to control devices via calendar events

Problem: You want to schedule heating, lighting, irrigation, etc. using a calendar interface. Creating events in Apple Calendar should trigger device actions at the scheduled time.

Why CalDAV: Calendar apps have excellent UX for time-based scheduling. Creating a “Heat living room” event from 6-8pm is more intuitive than programming cron jobs.

Solution: Radicale + storage plugin that translates calendar events to Home Assistant automations, or use Home Assistant’s existing CalDAV integration (read-only).

Why not alternatives:

  • Home Assistant UI: Functional but less intuitive for time scheduling
  • Cron jobs: No visual interface, hard to manage complex schedules
  • Google Calendar API: Vendor dependency, latency, requires internet

Best library: Radicale with custom plugin Effort: 2-3 weeks


Use Case 5: SaaS Calendar Sync Feature#

Persona: SaaS developer adding “sync to calendar” as a product feature

Problem: Your project management SaaS (like Asana, Linear, or Monday) wants to let users see their deadlines in Apple Calendar or Outlook. ICS feeds are read-only and update slowly.

Why CalDAV: Bidirectional sync. Users can reschedule tasks from their calendar app, and changes propagate back to the SaaS. This is a premium feature that competitors lack.

Solution: From-scratch CalDAV implementation (license flexibility) or Radicale plugin (faster to market).

Why not alternatives:

  • ICS subscription: Read-only, no bidirectional sync
  • Google Calendar API: Only works for Google Calendar users
  • Microsoft Graph API: Only works for Outlook users
  • CalDAV: Works with all standards-compliant calendar apps

Best library: From-scratch (avoid GPL) or Radicale plugin (if GPL acceptable) Effort: 4-8 weeks for production-quality from-scratch; 2-3 weeks for Radicale plugin


Use Case 6: Multi-Source Calendar Aggregation#

Persona: Developer who wants one calendar view combining data from multiple sources (Jira, GitHub issues, on-call schedules, personal tasks)

Problem: Important dates are scattered across 5+ systems. No single view shows everything that demands your time.

Why CalDAV: Create a CalDAV server that aggregates multiple sources into virtual calendars. Each source becomes a calendar in your app. The CalDAV server is the unifying layer.

Solution: Radicale with a custom storage plugin that fetches from multiple APIs on demand.

Why not alternatives:

  • Multiple ICS feeds: No unified sync-token, slow polling, read-only
  • Custom dashboard: Another app to check, not in your existing calendar workflow
  • Zapier/IFTTT: Fragile, limited, costs money at scale

Best library: Radicale with multi-source storage plugin Effort: 3-5 weeks (complexity is in the aggregation logic, not CalDAV)


Decision Matrix by Persona#

PersonaBest ApproachLibraryEffortKey Constraint
Task-to-calendar bridgeRadicale pluginRadicale1-2 weeksAPI mapping
Self-hosted calendarRadicale defaultRadicale/Xandikos30 minNone
Enterprise integrationRadicale plugin or DjangoRadicale2-4 weeksLicense
IoT schedulingRadicale pluginRadicale2-3 weeksEvent interpretation
SaaS featureFrom scratchicalendar + lxml4-8 weeksLicense, quality
Multi-source aggregationRadicale pluginRadicale3-5 weeksAPI complexity
S4: Strategic

S4 Approach: Strategic Selection#

Pass: S4-strategic Topic: CalDAV Server Libraries (Python) Focus: WHICH to choose long-term? Viability, sustainability, and strategic trade-offs.

Methodology#

S4 evaluates the long-term viability of each option. Focus on maintenance sustainability, community trajectory, and strategic positioning.

Key Questions#

  1. Will Radicale still be maintained in 3-5 years?
  2. What happens if the Radicale maintainer abandons the project?
  3. Is the CalDAV protocol itself future-proof or being replaced?
  4. What is the migration path if your chosen approach fails?

CalDAV Protocol: Strategic Outlook#

Protocol Longevity#

CalDAV (RFC 4791, published 2007) is deeply entrenched:

  • Apple Calendar, iOS, macOS rely on it for iCloud sync
  • Thunderbird uses it as the primary calendar sync protocol
  • Android via DAVx5 has millions of CalDAV users
  • Every major groupware server supports it (Exchange, Google, Nextcloud)

There is no replacement protocol on the horizon. JMAP Calendar (RFC 8984) exists but has near-zero adoption outside Fastmail. CalDAV will remain relevant for at least 10+ years.

Protocol Evolution#

CalDAV development has slowed (the spec is mature):

  • RFC 4791 (2007): Core CalDAV
  • RFC 6638 (2012): Scheduling extensions
  • RFC 6578 (2012): Collection sync
  • RFC 7953 (2016): Calendar availability
  • No new CalDAV RFCs since 2016

This stability is a feature: your implementation will not need frequent updates for spec changes.


Radicale: Strategic Viability#

Sustainability Indicators#

FactorAssessmentEvidence
Maintainer healthStrongKozea (company) + community, not single-maintainer
Release cadenceSteadyRegular releases for 17 years (2009-2026)
Community sizeGrowing4,500 stars, 46 contributors, Linux distro packages
Bus factorGoodCompany-backed (Kozea), multiple contributors
Plugin ecosystemHealthy5+ third-party storage plugins
Python version supportCurrentTracks modern Python versions
Security track recordGoodRegular security updates, TLS support

Risk Factors#

Low risk: Radicale has survived 17 years across Python 2 to Python 3, multiple CalDAV spec revisions, and the rise/fall of competing servers (CalendarServer, Darwin Calendar). It is the default CalDAV server recommendation across self-hosting communities.

Medium risk: The storage plugin API changed between v2 and v3 (BaseCollection split into BaseStorage + BaseCollection). Another major version could break custom plugins.

Mitigation: Pin Radicale version in requirements. The v2 to v3 migration was well-documented and plugins adapted. The API has been stable since v3.0 (2020).

Competitive Position#

Radicale occupies a unique niche: the only actively-maintained, lightweight, pluggable Python CalDAV server. Its competition is either:

  • Dead (Apple CalendarServer, archived 2023)
  • Non-pluggable (Xandikos, Git-only storage)
  • Different language (sabre/dav in PHP, Cyrus in C, Bedework in Java)

This monopoly position means Radicale is unlikely to be displaced unless a new Python CalDAV framework emerges, which shows no signs of happening.

Vendor Lock-in#

ApproachLock-in RiskExit Cost
Radicale pluginLow2-3 weeks to rewrite protocol layer
From scratchNoneN/A (you own everything)
XandikosMediumMust migrate off Git storage
Vikunja built-inMediumCoupled to Vikunja’s CalDAV implementation

S4 Recommendation: Strategic Selection#

Final Verdict#

Radicale with a custom storage plugin is the strategically sound choice.

Why Radicale Wins#

  1. 17-year track record - outlived Apple CalendarServer, multiple competitors
  2. Company-backed - Kozea maintains it professionally, not a hobby project
  3. Monopoly position - only pluggable Python CalDAV server, no credible alternative
  4. Protocol stability - CalDAV spec has not changed since 2016, no forced updates
  5. Low lock-in - storage plugin code (data mapping) is reusable if you migrate

Strategic Risks (All Low)#

  • Radicale abandonment: Low probability (company-backed, 17 years running)
  • CalDAV obsolescence: Very low (Apple and Mozilla depend on it)
  • Plugin API breakage: Low (stable since v3.0 in 2020)
  • GPL-3.0 constraint: Non-issue for internal use

The Only Scenario to Avoid Radicale#

If you are building a commercial SaaS product that must be distributed as proprietary software, the GPL-3.0 license makes Radicale unsuitable. In that case, build from scratch using MIT-licensed components (icalendar, lxml, defusedxml). Budget 4-8 weeks.

3-Year Outlook#

CalDAV server infrastructure in Python will look essentially the same in 2029:

  • Radicale will still be the default recommendation
  • No new Python CalDAV framework will emerge (the market is too small)
  • JMAP Calendar will remain niche (Fastmail only)
  • Apple Calendar and Thunderbird will still speak CalDAV

This stability makes investment in a Radicale plugin safe for the medium to long term.


Strategic Viability Analysis#

Radicale: Long-Term Outlook#

Sustainability Indicators#

FactorAssessmentEvidence
Maintainer healthStrongKozea (company) + community, not single-maintainer
Release cadenceSteadyRegular releases for 17 years (2009-2026)
Community sizeGrowing4,500 stars, 46 contributors, Linux distro packages
Bus factorGoodCompany-backed (Kozea), multiple contributors
Plugin ecosystemHealthy5+ third-party storage plugins
Python version supportCurrentTracks modern Python versions
Security track recordGoodRegular security updates, TLS support

Risk Factors#

Low risk: Radicale has survived 17 years across Python 2 to Python 3, multiple CalDAV spec revisions, and the rise/fall of competing servers (CalendarServer, Darwin Calendar). It is the default CalDAV server recommendation across self-hosting communities.

Medium risk: The storage plugin API changed between v2 and v3 (BaseCollection split into BaseStorage + BaseCollection). Another major version could break custom plugins.

Mitigation: Pin Radicale version in requirements. The v2 to v3 migration was well-documented and plugins adapted. The API has been stable since v3.0 (2020).

Competitive Position#

Radicale occupies a unique niche: the only actively-maintained, lightweight, pluggable Python CalDAV server. Its competition is either:

  • Dead (Apple CalendarServer, archived 2023)
  • Non-pluggable (Xandikos, Git-only storage)
  • Different language (sabre/dav in PHP, Cyrus in C, Bedework in Java)

This monopoly position means Radicale is unlikely to be displaced unless a new Python CalDAV framework emerges, which shows no signs of happening.


Xandikos: Long-Term Outlook#

Sustainability Indicators#

FactorAssessmentEvidence
Maintainer healthSingle maintainerJelmer Vernooij (respected, but sole owner)
Release cadenceSteadyRegular 0.x releases since 2017
Community sizeSmall554 stars
Bus factorConcerning1 primary maintainer
RFC complianceExcellentBest-in-class for Python CalDAV

Risk Factors#

Medium risk: Single maintainer. If Jelmer moves on, Xandikos stalls. The project has been in 0.x for 8 years, suggesting it may never reach 1.0 stability.

Low impact: For users who just need a Git-backed CalDAV server, Xandikos is feature-complete. Even if unmaintained, it would continue to work.


CalDAV Protocol: Future-Proof?#

Protocol Longevity#

CalDAV (RFC 4791, published 2007) is deeply entrenched:

  • Apple Calendar, iOS, macOS rely on it for iCloud sync
  • Thunderbird uses it as the primary calendar sync protocol
  • Android via DAVx5 has millions of CalDAV users
  • Every major groupware server supports it (Exchange, Google, Nextcloud)

There is no replacement protocol on the horizon. JMAP Calendar (RFC 8984) exists but has near-zero adoption outside Fastmail. CalDAV will remain relevant for at least 10+ years.

Protocol Evolution#

CalDAV development has slowed (the spec is mature):

  • RFC 4791 (2007): Core CalDAV
  • RFC 6638 (2012): Scheduling extensions
  • RFC 6578 (2012): Collection sync
  • RFC 7953 (2016): Calendar availability
  • No new CalDAV RFCs since 2016

This stability is a feature: your implementation will not need frequent updates for spec changes.


Strategic Decision Framework#

Decision: Radicale Plugin vs. From Scratch#

Choose Radicale plugin when:

  • Time to market matters (days vs. weeks)
  • GPL-3.0 is acceptable (internal use, open-source project)
  • You do not need CalDAV scheduling (RFC 6638)
  • You do not need ASGI
  • You want proven client compatibility out of the box

Choose from-scratch when:

  • GPL-3.0 is a dealbreaker (commercial SaaS distribution)
  • You need ASGI/async for high concurrency
  • You need scheduling (meeting invitations)
  • You want to embed CalDAV in an existing web framework
  • You want full control over every protocol detail

Migration Path#

If you start with Radicale plugin and later need to migrate:

  1. Your storage code is reusable - the data mapping logic (Vikunja tasks to iCalendar) transfers to any implementation
  2. The CalDAV protocol knowledge transfers - you will have learned PROPFIND, REPORT, ETag semantics
  3. Client test suite transfers - your Apple Calendar + Thunderbird test procedures work with any server
  4. Estimated migration effort: 2-3 weeks to reimplement protocol handling, with most storage code reusable

Vendor Lock-in Assessment#

ApproachLock-in RiskExit Cost
Radicale pluginLow2-3 weeks to rewrite protocol layer
From scratchNoneN/A (you own everything)
XandikosMediumMust migrate off Git storage
Vikunja built-inMediumCoupled to Vikunja’s CalDAV implementation

Final Strategic Recommendation#

Start with Radicale plugin. It is the right first move for almost every use case.

The only exception is commercial SaaS distribution where GPL-3.0 is unacceptable. In that case, budget 4-8 weeks for a from-scratch implementation.

For the Vikunja task-to-calendar use case specifically:

  1. Week 1: Build Radicale storage plugin, map Vikunja tasks to VTODO
  2. Week 2: Test with Apple Calendar + Thunderbird, fix client quirks
  3. Week 3: Add VEVENT support (tasks as calendar events, not just todos)
  4. Ongoing: Iterate based on actual usage, consider caching and sync-token

The CalDAV protocol is stable, Radicale is mature, and the plugin pattern is proven. This is a safe, sustainable architectural choice.


Xandikos: Strategic Viability#

Sustainability Indicators#

FactorAssessmentEvidence
Maintainer healthSingle maintainerJelmer Vernooij (respected, but sole owner)
Release cadenceSteadyRegular 0.x releases since 2017
Community sizeSmall554 stars
Bus factorConcerning1 primary maintainer
RFC complianceExcellentBest-in-class for Python CalDAV

Risk Factors#

Medium risk: Single maintainer. If Jelmer moves on, Xandikos stalls. The project has been in 0.x for 8 years, suggesting it may never reach 1.0 stability.

Low impact: For users who just need a Git-backed CalDAV server, Xandikos is feature-complete. Even if unmaintained, it would continue to work.

Published: 2026-03-09 Updated: 2026-03-09