Host your own contacts and calendars and share them across devices

I’m trying to learn ways to minimize my reliance upon large companies for handling my day-to-day personal data. So I figured calendar and contacts should be on my list of things to self-host. This post is about how I migrated all my Google calendars and phone contacts to my own server without losing any features I was using. I’m doing this mostly for fun.

My self-hosted calendar in the web client
My self-hosted calendar on the phone

Installing Radicale CalDAV/CardDAV server behind an Apache Reverse Proxy

First step is to choose a CalDAV/CardDAV server. These are standard file formats that hold calendar and contact info, and there are lots of choices. I found Radicale, which I liked because it was small, simple, open source, and written in Python, my favorite programming language! In its simplest form, it just runs on a port of your server, and you can test it out like that.

I wanted the extra security of running it behind the more robust web server I already had running (Apache) with authentication, so I had to set up a Reverse Proxy, where Apache passes traffic to the Radicale instance and back. The instructions work but are specific to nginx, so I had to figure out what to do for Apache. Here’s what I got:

This authenticates users that hit the /radicale/ folder and then sends everything to the Radicale server chilling on port 8822. I put two users in my htpasswd file, one for me and the other to be shared across the family.

I also have an older server that’s still running upstart so I had to come up with the upstart version of the startup script for Radicale:

With that all in place, I could get to the server’s web interface, log in with the htpasswd info, and add some empty calendars and addressbooks.

Radicale web interface working

I created a one calendar in my personal folder and another in the family account and then symbolically linked the family one into my folder in the Radicale storage area so I could share it easily.

Importing, Organizing, and Merging Contacts

Now to populate the server! I had several overlapping contact lists on various devices:

  • Some old phone numbers on my SIM card on my phone. I decided to ignore most of these because the majority of them are duplicated elsewhere.
  • More recent phone numbers and a few email addresses in my Google Contacts, gathered from my phone. I exported these all with the Export function in vCard format to my PC.
  • A bunch of email addresses on my Thunderbird email client on my laptop. If you use a web-based email, just export your contacts from there to a file.

I cleaned these all up and merged them by first connecting CardBook to my contact URL shown on the Radicale web interface and then right-clicking the address book and selecting “Import contacts from a file.” Import however many files you exported. Then I ran the CardBook merge tool to combine all duplicates and do other cleanups. It was pretty satisfying.

I had a few configuration things wrong and just ran a tail -f  on the Radicale log. When syncing wasn’t working, I saw exceptions there when blank FN (Family Name) entries weren’t allowed by Radicale and any contact that was missing FN was crashing the sync.

Cardbook screenshot with contacts listed
Cardbook with contacts loaded up show in Thunderbird

Transferring calendars to Radicale

Moving my calendars was straightforward using the Lightning Thunderbird plugin. I just exported them from where they were (Google Calendar) and imported them in Lightning into my new calendar. To get access to the family account as well as my personal one from Thunderbird, I had to activate the calendar.network.multirealm setting in Thunderbird advanced config so it would ask me for multiple credentials.

Syncing to Android phones and iPhones

Obviously the calendars and contacts need to be shared with the phone. For Android, I installed the open-source DAVDroid which you can get for a few bucks on the Play Store or for free on F-Droid. Just type in the URL from the Radicale web interface and you’ll be off and running with Calendar and Contact Sync. The calendar shows up in any calendar app (including Google Calendar) and the contacts show up in any contacts app. I then filtered out and/or deleted the contacts and calendars stored on other people’s servers.

For the my wife’s iPhone, I just synced the family calendar. It was as easy as going to Settings->Accounts and adding a new CalDAV account pointing to the URL from the Radicale web interface and typing in the htpasswd credentials. Then the calendar showed up in any/all calendar apps.

Web calendar client

I wasn’t sure if I was ready to just use Thunderbird Lightning as my calendar client because I’m so used to a web interface. Quick searching led me to AgenDAV, an open-source CalDAV web client. One more quick Apache config and we were off and running. (You could also just put this in its own subdomain).

Looks pretty nice! (Screenshot at top of page)

You can’t directly add an external link to an external calendar (like a public rec soccer league one) so in those scenarios just import it into the server first and it will show up here.

Summary

All in, this was less than a day of work. Not bad for me since I had been really wanting to do some cleanup on my contacts for a long time anyway. Granted, doing all this means I have to maintain it and try to secure it myself (something I’ll surely suffer the consequences of someday). At least I saved backups of everything in the process. Normal people might want to just consider a more in-the-box solution such as Nextcloud but I personally just enjoy learning and doing these kinds of things.

Discuss on Hacker News

11 thoughts on “Host your own contacts and calendars and share them across devices”

  1. This looks quite good. I have always wanted to do something like this, but gave up pretty quickly. Have you hosted this on the Internet, or do you run this on your home, and have your phones sync up only when you are home?

  2. GREAT! Will install it tomorrow morning for my confused calendar life… ;O) The testinstall was successfull.

    Thx
    The ruebenmaster

  3. Nice work, thank you for documenting this, this has been on my todo list for a while. Have you tried Calendar on OS X (High Sierra)? I am having trouble syncing with both Radicale and Baikal, although other clients including iOS work as expected. Am wondering if this is my config or a quirk of the Apple apps… I can’t get Contacts on OS X to login either (although it works on iOS).

    Also, any idea how to make that Apache config work for a subdomain? Thanks

    1. Hey thanks a lot.

      I did get it synced to an iPhone but I don’t think it was on OS X (High Sierra) so I may not have gotten any farther than you. See anything useful in logs?

      For an apache subdomain, I suspect you’d just have to put the config entries above in the subdomain section with a:

      ServerName sub.domain.com

      Is that not working? I wonder what else you have to tweak.

      Good luck mate.

  4. Dear friend,
    I read your article with interest and it allowed me to complete the creation of a personal server for contacts and the calendar that corresponded to my desire not to leave my personal data to the big corporations anymore.
    I did not understand well and I was unable to carry out the authentication part for which I followed the simpler instructions of the project site (bcrypt).
    But having opened the specific door I would like to strengthen security.
    Could you give me a little more detailed instructions than the online article.
    My server runs on raspberry pi 4; initially I had opened both door 80 and 5232 and now I have left only the latter active. It’s correct?
    Thanks in advance for the help you can give me.
    Carlo

  5. Appreciating the time and effort you put into your blog and in depth information you provide. It’s awesome to come across a blog every once in a while that isn’t the same old rehashed information. Fantastic read! I’ve bookmarked your site and I’m adding your RSS feeds to my Google account.

Leave a Reply

Your email address will not be published. Required fields are marked *