While I still struggle to move out Gmail, I decided to self-host my calendar using the CalDAV protocol. I have been relying on Google Calendar since 2008 and it has been really useful but I think it would be better to own the data.
TLDR: I am happy with Radicale, Etar and DAVx⁵ on Android and an AgenDAV instance for the web client.
Several open source CalDAV/CardDAV (address book) exists. One of the more known is the integrated server of Nextcloud. I hosted a Nextcloud instance years ago and while I acknowledge the quality of the solution, the ease of using it, I've had issues with upgrades. In the end, I stopped my instance as I did not needed it anymore. This time, I wanted to rely on a simple software, well documented, well known for a long time and easy to deploy and maintain. I decided to rely on Radicale. A python server that seems to fit my needs.
The server is the easiest part. I also needed clients. I wanted an Android client to use, something that would work offline. I chose Etar and DAVx⁵. The flow was not complex even if two software's must be configured. By default, DAVx⁵ don't synchronise event older than 90 days. It allowed the first sync to be quite fast.
Having an Android application is convenient but I also need a nice web interface. Browsing the calendar is often easier on bigger screens. This is where the difficulties appeared.
When things are not that simple
Firstly, I looked for a web client that could integrate with the server directly. I wanted to avoid configuring a database and handle upgrades. My usecase was to add the server url, prompt for user and password and have access to a nice web page. As the protocol is not exactly modern, there are few options.
Open Calendar
One of the most promising web client is Open Calendar. It is developed by Algoo. A nice company based in Grenoble. I had already met them in 2018, and I kept following their work from a distance. I've read about it in a LinuxFR article. It seemed easy to use, and easy to configure. Unfortunately, I have encountered performance issues.
What I tried
My issue is explained in the GitHub issue. Loading the interface with my calendars takes several minutes. I have three large calendar containing around 1300, 3400 and 11K items for the biggest ones. Most of the events are nonrecurring and there are a few recurring events. A few dozen of them have been finished for a long time and 8 of them are still running and have events in the future. Parsing all these recurring events and the future on takes most of the time. Quickly, I've seen that deactivated calendars would still trigger requests to the CalDAV server even if the events are not displayed. I compared the time spent displaying events for the current week by a python script and the Open-Calendar implementation.
The python script takes around 5 seconds to fetch all the information.
#!/usr/bin/env python3
from datetime import datetime, timedelta, timezone
import caldav
CALDAV_URL = "https://test.example.com"
USERNAME = "sample_user"
PASSWORD = "pass"
def current_week():
"""Return the start and end of the current week (Monday -> next Monday)."""
now = datetime.now(timezone.utc)
# Monday = 0
start = now - timedelta(days=now.weekday())
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=7)
return start, end
def main():
week_start, week_end = current_week()
print(f"Week start: {week_start.isoformat()}")
print(f"Week end: {week_end.isoformat()}")
print()
with caldav.DAVClient(
url=CALDAV_URL,
username=USERNAME,
password=PASSWORD,
) as client:
principal = client.principal()
print(f"Principal: {principal.url}")
print()
calendars = principal.calendars()
print(f"Found {len(calendars)} calendar(s)")
print("=" * 80)
for calendar in calendars:
print()
print(f"CALENDAR: {calendar.name}")
print(f"URL: {calendar.url}")
print("-" * 80)
try:
events = calendar.date_search(
start=week_start,
end=week_end,
expand=True,
)
print(f"Events: {len(events)}")
print()
for event in events:
print(f"Event URL: {event.url}")
# Parsed iCalendar object
ical = event.icalendar_instance
for component in ical.walk():
if component.name == "VEVENT":
print(f"UID: {component.get('UID')}")
print(f"Summary: {component.get('SUMMARY')}")
print(f"Start: {component.get('DTSTART')}")
print(f"End: {component.get('DTEND')}")
# Raw iCalendar response
print("\nRaw iCalendar:")
print(event.data)
print("-" * 80)
except Exception as exc:
print(f"ERROR reading calendar: {exc}")
if __name__ == "__main__":
main()
As I like the project, I decided to modify it to avoid fetching data of hidden events. It needed to modify several classes and methods and it works. But it would still require to move all the future events in a dedicated calendar that would remain small and deactivate all the other calendars. It worked but when I reactivated the other small calendars, it was still too slow for my expectations. I thought that maybe I was not the target user of Open-Calendar and decided to find another solution.
Calino
Another alternative I encountered is Calino. I dismissed it because it seems to be mainly vibe coded. Since the first of march, it has 1700 commits and it seems to create custom cryptography logic. It may have the same performance issues than Open-Calendar and it is too young. If it's still actively developed in a year, I could have a look but for now, I will pass.
AgenDAV
Another nice client is AgenDAV. At first, I did not wanted a new database but I have already a MariaDB instance running and a server supporting PHP 8.5. It is mainly used to run Leed a RSS/Atom aggregator that I self host. The documentation is clear enough and I could manage to make it run in my setup.
Loading the events of the current week takes around 5 or 6 seconds. It is sufficient for my need.
Requests
AgenDAV:
17 requests 264.17 kB / 31.75 kB transferred Finish: 5.27 s
Google Calendar:
116 requests 11.34 MB / 1.35 MB transferred Finish: 9.17 s
It is an approximation because Google Calendar takes a lot of time to finish, some scripts are blocked by µblock origin and the interface is doing a lot of fetch. My point is that 5 seconds is not ridiculous even if it feels horrible, it is in the average.
Last Words
Self-hosting a calendar is not difficult and I am really happy with my choices so far. Please note that once again, backup are super important, especiall if you don't want to lose data.