Skip to main content

CalDAV Sync

CalDAV is the industry-standard protocol for accessing calendar and task data. tududi implements it in both directions:

  • As a server — connect Apple Reminders, tasks.org, Thunderbird, or Evolution directly to tududi
  • As a client — sync two-way with Nextcloud, Baikal, or any CalDAV-compatible server

Key capabilities:

  • Bidirectional sync — changes flow both ways
  • Recurring tasks via RRULE (RFC 5545)
  • Automatic conflict detection with configurable resolution
  • Background periodic sync
  • Standards-compliant: RFC 4791 (CalDAV) and RFC 5545 (iCalendar)

Enabling CalDAV​

CalDAV is off by default. Enable it with environment variables, then restart tududi.

# Enable the CalDAV feature (FF_ENABLE_CALDAV is an accepted alias)
CALDAV_ENABLED=true

# Encryption key for stored remote-calendar passwords (32 bytes)
ENCRYPTION_KEY=$(openssl rand -hex 32)

# Optional defaults
CALDAV_DEFAULT_SYNC_INTERVAL=15 # Minutes between syncs
CALDAV_MAX_RECURRING_INSTANCES=365 # Future recurring instances
CALDAV_CONFLICT_RESOLUTION=last_write_wins # Default strategy

Restart:

docker compose restart   # Docker
npm start # Standalone

Then configure calendars in Profile → Settings → CalDAV. The tab only appears once the feature flag is on.

Environment variable reference​

VariableRequiredDefaultDescription
CALDAV_ENABLEDYesfalseEnable the CalDAV feature. FF_ENABLE_CALDAV=true works too
ENCRYPTION_KEYRecommendedfalls back to SECRET_KEYAES-256-GCM key for encrypting remote calendar passwords
CALDAV_PROJECTS_AS_CALENDARSNofalseServe one calendar per project (plus a "(No Project)" calendar) instead of a single combined list. With this on, Apple Reminders shows one list per project
CALDAV_DEFAULT_SYNC_INTERVALNo15Default sync interval in minutes
CALDAV_MAX_RECURRING_INSTANCESNo365Maximum future recurring instances to expand
CALDAV_CONFLICT_RESOLUTIONNolast_write_winsDefault conflict strategy
CALDAV_RATE_LIMITNo60Requests per minute per IP
CALDAV_MAX_SYNC_TASKSNo1000Maximum tasks per sync operation
CALDAV_REQUEST_TIMEOUTNo30000Request timeout in milliseconds
CALDAV_LOG_LEVELNoinfoerror, warn, info, or debug
CALDAV_LOG_REQUESTSNofalseLog every CalDAV HTTP request
Set ENCRYPTION_KEY explicitly

Remote calendar passwords are encrypted with a key derived from ENCRYPTION_KEY, falling back to SECRET_KEY. If that value ever changes, previously stored remote passwords become undecryptable and you will have to re-enter them.


Connecting a Client to tududi​

In this mode tududi is the CalDAV server. Clients authenticate with HTTP Basic Auth using your tududi email and password.

Base URL: https://your-tududi-domain.com/caldav/

Use HTTPS

CalDAV uses HTTP Basic Auth, which transmits your credentials on every request. Always serve tududi over HTTPS in production.

tasks.org (Android/iOS)​

  1. Open tasks.org → ☰ Menu → Settings → Synchronization
  2. Select CalDAV
  3. Enter:
    • URL: https://your-tududi-domain.com/caldav/
    • Username: your tududi email
    • Password: your tududi password
  4. Tap Add Account, select the tasks calendar, then Sync

Supports full task CRUD, RRULE recurrence, due and start dates, priorities, status, and subtasks via RELATED-TO. Habit mode and tags are stored in custom fields and may not display.

Apple Reminders (iOS)​

  1. Settings → Reminders → Accounts → Add Account → Other
  2. Select Add CalDAV Account
  3. Enter:
    • Server: your-tududi-domain.com
    • Username: your tududi email
    • Password: your tududi password
    • Description: Tududi Tasks
  4. Next → Save, then open Reminders

Apple Reminders (macOS)​

  1. System Settings → Internet Accounts
  2. + → Add Other Account → CalDAV Account
  3. Set Account Type to Manual, then enter your email, password, and Server Address: https://your-tududi-domain.com/caldav/
  4. Sign In, then open Reminders

Supports task creation and editing, due dates and reminders, priorities, completion, and limited recurrence patterns.

One list per project

Set CALDAV_PROJECTS_AS_CALENDARS=true to make Apple Reminders show each tududi project as its own list instead of one combined list.

Thunderbird (Desktop)​

  1. Open the Calendar tab
  2. Right-click the calendar list → New Calendar → On the Network → CalDAV
  3. Location: https://your-tududi-domain.com/caldav/{your-username}/tasks/
  4. Enter credentials when prompted, select Tasks, click Finish

Thunderbird has the most complete support: full task management, advanced recurrence, all date fields, priority, status, descriptions, and subtask hierarchy.

Evolution (Linux)​

  1. File → New → Task List → CalDAV
  2. URL: https://your-tududi-domain.com/caldav/{your-username}/tasks/
  3. Enter your tududi email and password, click Apply

Syncing with a Remote Server​

In this mode tududi acts as a CalDAV client, periodically fetching remote changes and pushing local ones. This is useful for cloud backup, keeping multiple tududi instances in sync, or integrating with existing calendar infrastructure.

Nextcloud​

  1. Profile → Settings → CalDAV → Add Remote Calendar
  2. Select Nextcloud
  3. Enter:
    • Name: My Nextcloud Tasks
    • Server URL: https://your-nextcloud-domain.com
    • Calendar Path: /remote.php/dav/calendars/{username}/tasks/
    • Username / Password: your Nextcloud credentials
  4. Choose a sync direction and interval
  5. Save, then Sync Now to test

Use an app password. In Nextcloud go to Settings → Security → Devices & sessions, create a new app password named Tududi, and use that instead of your account password.

Baikal​

Same flow, selecting Baikal as the server type:

  • Server URL: https://your-baikal-domain.com
  • Calendar Path: /dav.php/calendars/{username}/tasks/

Generic CalDAV server​

Select Generic CalDAV and supply the full server URL, calendar path, credentials, and auth type (Basic by default).

Most servers follow the pattern /calendars/{username}/{calendar-name}/. Check your server's documentation for the exact format.

Testing before saving​

The remote calendar form includes a Test Connection action that validates the URL and credentials without creating the calendar.


Managing Calendars​

Profile → Settings → CalDAV lists your local and remote calendars with their sync status.

Each calendar card shows the last successful sync time, current status (Synced / Syncing / Error), and any error details.

  • Add Calendar — create a local calendar served at https://your-domain.com/caldav/{username}/tasks/
  • Add Remote Calendar — connect an external server
  • Edit — change name, sync interval, or direction
  • Sync Now — trigger an immediate sync
  • Delete — removes the CalDAV configuration only. Your tasks are not deleted.

Sync direction​

DirectionBehavior
BidirectionalChanges sync both ways (default)
Pull onlyImport from remote into tududi
Push onlyExport from tududi to remote

Sync interval​

5, 15 (default), 30, or 60 minutes, or Manual only to disable automatic sync while keeping the Sync Now button available.


Conflict Resolution​

A conflict occurs when the same task changes both in tududi and remotely between syncs.

StrategyBehavior
last_write_winsMost recent change wins (default, recommended)
local_winsAlways keep the tududi version
remote_winsAlways keep the remote version
manualFlag the conflict for you to resolve

With manual, a red badge appears on the CalDAV tab and a conflict count on the affected calendar card. Click View Conflicts to see a side-by-side comparison of the local and remote versions, then choose Keep Local or Keep Remote. Merging is not yet implemented.


How Sync Works​

tududi uses a three-phase sync algorithm:

  1. Pull — fetch remote changes, parse VTODO items, buffer them
  2. Merge — compare versions using ETags, detect conflicts, apply the resolution strategy
  3. Push — serialize local changes to VTODO and PUT them to the remote server, then update sync state

ETags identify each task version and let clients detect changes efficiently; If-Match headers prevent conflicting updates. CTags cover the whole calendar collection, enabling a cheap "has anything changed?" check that avoids unnecessary full syncs.


Field Mappings​

tududi fieldVTODO propertyNotes
NameSUMMARYTask title
NoteDESCRIPTIONTask description
Due dateDUEISO 8601 UTC
Defer untilDTSTARTStart date/time
Completed atCOMPLETEDCompletion timestamp
StatusSTATUSSee below
PriorityPRIORITYInverse scale, see below
RecurrenceRRULERFC 5545
SubtasksRELATED-TOParent task UID

Status mapping​

tududi statusVTODO status
Not startedNEEDS-ACTION
In progressIN-PROCESS
DoneCOMPLETED
ArchivedCOMPLETED
WaitingNEEDS-ACTION
CancelledCANCELLED
PlannedNEEDS-ACTION

Seven tududi statuses collapse into four CalDAV statuses, so some nuance is lost in external clients.

Priority mapping​

CalDAV uses an inverse scale where 1 is highest:

tududi priorityVTODO priority
High3
Medium5
Low7

Custom fields​

tududi-specific data is preserved in extended properties that external clients generally will not display:

  • X-TUDUDI-HABIT-MODE — habit tracking settings
  • X-TUDUDI-PROJECT-UID — project association
  • X-TUDUDI-TAGS — task tags

Recurring Tasks​

Recurring tasks are stored once in tududi with a recurrence rule — there are no duplicate rows for future occurrences. For CalDAV, the parent is expanded on demand into virtual instances, each with a unique RECURRENCE-ID, so clients see discrete entries.

PatternRRULE
DailyFREQ=DAILY
Every N daysFREQ=DAILY;INTERVAL=3
Weekly on set daysFREQ=WEEKLY;BYDAY=MO,WE,FR
Monthly by dateFREQ=MONTHLY;BYMONTHDAY=15
Monthly by weekdayFREQ=MONTHLY;BYDAY=2TH (2nd Thursday)
YearlyFREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1

Editing a single instance creates an override. Deleting the parent removes all instances. Expansion is capped by CALDAV_MAX_RECURRING_INSTANCES (default 365).

See Recurring Tasks for how recurrence behaves inside tududi.


Security​

Password storage. Remote calendar passwords are encrypted with AES-256-GCM using a key derived from ENCRYPTION_KEY (or SECRET_KEY). Prefer app-specific passwords over main account passwords wherever the remote server supports them.

Authentication. CalDAV endpoints use HTTP Basic Auth. Use HTTPS in production — Basic Auth sends credentials on every request.

Rate limiting. CalDAV protocol endpoints allow 60 requests per minute by default (CALDAV_RATE_LIMIT); manual sync triggers are limited to 5 per minute.


Troubleshooting​

Authentication fails (401)​

  1. Verify your email and password are correct
  2. Confirm CALDAV_ENABLED=true and the server was restarted
  3. Make sure your client supports HTTP Basic Auth
  4. Check that your reverse proxy is not stripping the Authorization header

Tasks not syncing​

  1. Check the sync status and last sync time on the calendar card
  2. Click Sync Now to force a sync
  3. Confirm sync direction is Bidirectional rather than pull- or push-only
  4. Check the conflict list for unresolved conflicts
  5. Review the backend logs — set CALDAV_LOG_LEVEL=debug for detail

Only the first recurring instance appears​

  1. Confirm your client supports RRULE
  2. Check CALDAV_MAX_RECURRING_INSTANCES (default 365)
  3. Some clients need a manual refresh to pick up new instances

Sync is slow or times out​

  1. Archive completed tasks to reduce volume
  2. Raise CALDAV_REQUEST_TIMEOUT (e.g. 60000)
  3. Lower the sync frequency from 5 minutes to 15
  4. Check server CPU, memory, and disk I/O

"Invalid VTODO" or parse errors​

  1. Verify the client is sending valid iCalendar data
  2. Enable CALDAV_LOG_REQUESTS=true and review the logs
  3. Test with a different client to isolate whether it is client-specific

Known Limitations​

  1. Subtasks — supported via RELATED-TO, but not every client renders the hierarchy
  2. Habit mode — stored in custom fields, invisible externally
  3. Tags — exported as CATEGORIES; colors and metadata stay in tududi
  4. Projects — association stored in X-TUDUDI-PROJECT-UID, not shown externally unless CALDAV_PROJECTS_AS_CALENDARS is on
  5. Status granularity — 7 tududi statuses map onto 4 CalDAV statuses
  6. Timezones — all dates stored as UTC; clients convert locally
  7. Large recurring sequences — expanding far into the future creates many VTODOs

FAQ​

Can I use multiple CalDAV clients at once? Yes. Changes from any client sync to all the others.

What happens if I delete a task in a CalDAV client? With bidirectional sync it is deleted in tududi on the next sync.

Can I sync multiple remote calendars? Yes, and each syncs independently.

Do I need a separate CalDAV server? No. tududi is itself a CalDAV server — clients can connect to it directly.

Can I disable automatic sync? Yes, set the interval to Manual only. The Sync Now button still works.


Protocol references:

Technical Implementation Files:

  • CalDAV protocol routes: /backend/modules/caldav/routes.js
  • REST API routes: /backend/modules/caldav/api/routes.js
  • Sync engine: /backend/modules/caldav/sync/ (pull, merge, push, conflict resolver)
  • iCalendar parsing: /backend/modules/caldav/icalendar/
  • Sync scheduler: /backend/modules/caldav/services/sync-scheduler.js
  • Password encryption: /backend/modules/caldav/services/encryption-service.js
  • Settings UI: /frontend/components/Profile/tabs/CalDAVTab.tsx
  • Setup wizard and conflict resolver: /frontend/components/CalDAV/