Sync calendars and address books between the Linux console and Android

Posted by Eric Scheibler at October 14, 2014

Calendars and address books contain sensitive data. It is therefore advisable not to upload them to the cloud. To keep the datasets synchronized between multiple devices nonetheless, you need to operate your own calendar and address book server.

This article first covers the setup of the calendar and address book server and then presents calendar and address book applications for the Linux console and the Android operating system.

The article was last updated on August 13, 2025.

Table of contents

  1. Calendar and Address Book Server
    1. Baïkal
    2. DynDNS
  2. Linux Console
    1. Prerequisites
    2. Vdirsyncer
    3. Khard
    4. Khal
    5. Importing Calendars
  3. Android

Calendar and Address Book Server

When choosing the server application, you can decide between OwnCloud, Baïkal and Darwin Calendar Server, among others.

I chose Baïkal because it’s easy to set up and comes with a simple and largely accessible web interface where you can conveniently manage your calendars and address books.

Baïkal

Operating Baïkal requires a web server. I use nginx. The project website contains detailed tutorials for common Linux distributions. Therefore, I won’t go into the nginx setup in detail here, but will only provide my configuration file for pim.example.org as an example:

server {
    listen               *:443      ssl;
    listen               [::]:443   ssl;
    server_name pim.example.org;
    root /var/www/baikal/html;
    index index.php;
    charset utf-8;

    # ssl
    ssl_certificate /etc/letsencrypt/live/USER_NAME/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/USER_NAME/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # php
    location ~ ^(.+\.php)(.*)$ {
        # php socket
        fastcgi_pass             unix:/run/php/php8.2-fpm.sock;
        # regex to split $uri to $fastcgi_script_name and $fastcgi_path
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        # Check that the PHP script exists before passing it
        try_files $fastcgi_script_name =404;

        # Bypass the fact that try_files resets $fastcgi_path_info
        # see: http://trac.nginx.org/nginx/ticket/321
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;

        fastcgi_index index.php;
        include fastcgi.conf;
    }
}

The rest of this tutorial assumes that the web server accepts encrypted connections via SSL and can execute PHP scripts.

Baïkal also requires sqlite:

sudo apt install sqlite3 php-sqlite3

Now download the current release from the Baïkal Releases website and extract it to the web server directory. Then follow the installation guide on the Baïkal website.

By visiting https://pim.example.org/baikal/html/ you then start Baïkal’s setup wizard and set an admin password, among other things. After completing the wizard, you log in as admin and create a new user. For this user, you can then create any number of address books and calendars.

For later: Baïkal Upgrade Tutorial

DynDNS

If the server is operated at home, setting up a DynDNS domain is advantageous. This way the server can also be reached from on the go.

I use my domain registrar’s inwx.de DynDNS service and ddclient for this.

An excerpt from my ddclient config file "/etc/ddclient.conf with the relevant details for inwx.de:

# general
protocol=dyndns2
server=dyndns.inwx.com
ssl=yes

# pim.example.org
login='DYNDNS_USER_NAME'
password='DYNDNS_PASSWORD'
pim.example.org

You can of course also use another DynDNS provider or client, or skip DynDNS entirely by installing your CalDAV server directly on a rented cloud server with a fixed IP, e.g. from Hetzner.

Those who have no possibility to operate their own CalDAV/CardDAV server can fall back on privacy-friendly providers such as mailbox.org.

Linux Console

This section contains instructions for the Linux console. This article primarily refers to Debian, but the presented programs should also work under most other distributions.

First, the calendars and address books are transferred from the server to the client using the vdirsyncer program. This is followed by the presentation of an address book and a calendar application. Both programs are exclusively intended for the console and don’t require a GUI.

More information at pimutils.org.

Prerequisites

All programs used below require python3 and pip.

I recommend installing the programs in a python virtual environment. This way, different versions of a Python module can be used on one system without interfering with each other. Furthermore, an installed program can be removed without leaving traces. You only need to delete the folder with the virtual Python environment.

First, create a directory for the virtual environments:

mkdir ~/.virtualenvs

You also need a subdirectory for the executable files:

mkdir ~/.virtualenvs/bin

The directory must finally be added to the shell’s path variable. Example for ZSH:

vim ~/.zshrc
[...]
# add ~/.virtualenvs/bin to the path variable
if [ -d "$HOME/.virtualenvs/bin" ] ; then
    PATH="$HOME/.virtualenvs/bin:$PATH"
fi
[...]

Then create the virtual environment:

python3 -m venv ~/.virtualenvs/pim

You now install programs as follows:

source ~/.virtualenvs/pim/bin/activate
pip install MODULE_NAME
deactivate

If you forget to activate the virtual environment, you install the desired Python module system-wide instead.

Vdirsyncer

Vdirsyncer serves to synchronize appointments and contacts between client and server. Installation is done by:

source ~/.virtualenvs/pim/bin/activate
pip install Vdirsyncer
# create the symlink (path variable see above)
ln -s ~/.virtualenvs/pim/bin/vdirsyncer ~/.virtualenvs/bin
deactivate

Next, you need to download the example configuration file:

mkdir ~/.config/vdirsyncer/
wget -O ~/.config/vdirsyncer/config "https://raw.githubusercontent.com/pimutils/vdirsyncer/refs/heads/main/config.example"

and configure the program:

vim ~/.config/vdirsyncer/config

The content of the config file should look something like this:

# An example configuration for vdirsyncer.

[general]
# A folder where vdirsyncer can store some metadata about each pair.
status_path = "~/.config/vdirsyncer/status/"

# CARDDAV
[pair contacts]
a = "contacts_local"
b = "contacts_remote"
collections = ["familie", "freunde"]
conflict_resolution = "a wins"

[storage contacts_local]
type = "filesystem"
path = "~/.contacts/"
fileext = ".vcf"
encoding = "utf-8"

[storage contacts_remote]
type = "carddav"
url = "https://pim.example.org/dav.php"
auth = "digest"
verify = "/etc/ssl/certs/ca-certificates.crt"
username = "YOURUSERNAME"
password = "YOURPASSWORD"

# CALDAV
[pair calendars]
a = "calendar_local"
b = "calendar_remote"
collections = ["calendar", "feiertage", "geburtstage"]
conflict_resolution = "a wins"

[storage calendar_local]
type = "filesystem"
path = "~/.calendars/"
fileext = ".ics"
encoding = "utf-8"

[storage calendar_remote]
type = "caldav"
url = "https://pim.example.org/dav.php"
auth = "digest"
verify = "/etc/ssl/certs/ca-certificates.crt"
username = "YOURUSERNAME"
password = "YOURPASSWORD"

In the above example configuration, two additional calendars (holidays and birthdays) have already been created in addition to the standard calendar. For the URLs, you only need to replace the hostname and the username used. A different calendar and address book server may also require a slightly different URL scheme.

If a self-signed SSL certificate is used to secure the calendar server, it must be provided on the client side. To do this, the certificate is copied to the client and stored in the /etc/ssl/local folder (see “verify” option in the vdirsyncer configuration file). The folder may need to be created first.

After configuration, manual synchronization is done using:

vdirsyncer discover
vdirsyncer sync

Finally, it’s recommended to create a cron job that synchronizes the calendars and address books hourly:

crontab -e
[...]
0   *   *   *   * $HOME/.virtualenvs/bin/vdirsyncer sync

More information about vdirsyncer can be found here.

Khard

Khard is a console application for managing contact information in vCard format. With khard, you can create new contacts and display, edit and delete existing contacts. Furthermore, khard can be integrated as an external address book into the email program Mutt.

Khard is installed as follows:

source ~/.virtualenvs/pim/bin/activate
pip install khard
# create the symlink (path variable see above)
ln -s ~/.virtualenvs/pim/bin/khard ~/.virtualenvs/bin
deactivate

Next, khard’s example configuration file must be copied to ~/.config/khard/khard.conf and adapted.

The “addressbooks” section contains the paths to all address books specified in the vdirsyncer config. Additionally, you need to specify the path to a frequently used text editor, among other things. This is needed for editing contacts.

With the following command, all available contacts can now be displayed in an overview:

khard list

Without additional parameters, contacts from all address books are displayed. Filtering is possible as follows:

khard list -a familie,freunde

You can also search for contact details like name, email addresses and phone numbers:

khard list max

The overview only shows the first phone number and email address. With the details command, all supported information can be retrieved:

khard details max

A new contact is created as follows:

khard new -a freunde

The form for creating the new contact opens in the specified text editor. After filling out and saving, the new contact is created. To cancel the process, simply don’t save the changes when closing the file.

With the commands

khard edit

and

khard remove

the created contact can be edited or deleted. After each change, synchronization must be executed manually using vdirsyncer sync.

Khard can also serve as an external address book for the email client Mutt, among other things. You can find everything else about this in khard’s documentation.

Khal

Khal is a calendar application for the Linux console. With khal, appointments in CalDAV format can be displayed, created and deleted. Khal accesses the local files in .ics format, which were previously transferred by vdirsyncer.

Installation:

source ~/.virtualenvs/pim/bin/activate
pip install khal
# create the symlink (path variable see above)
ln -s ~/.virtualenvs/pim/bin/khal ~/.virtualenvs/bin
# configure
mkdir ~/.config/khal
khal configure
deactivate

The structure of the configuration file is similar to that of khard and should be self-explanatory. After successful configuration, appointments for the next week can be displayed as follows:

khal list today 7D

Or just one calendar:

khal list today 7D -a feiertage

The following command creates a one-time appointment on November 9th of the current year, which starts at 14:00 and ends at 16:00:

khal new -a calendar -l "optional location" 09.11. 14:00 16:00 Title of the appointment

This creates an all-day, annually recurring appointment on February 19th:

khal new -a geburtstage -r yearly 19.02. Max's Birthday

More information about khal is available at https://lostpackets.de/khal/.

Importing Calendars

Some websites offer their calendars in CalDAV format as subscriptions. These can then be added under Android or iOS, for example. However, they are then not managed by the home calendar server and are therefore not available under Linux. The following section describes the import of entire calendars using the Linux console, using a holiday calendar subscription for Germany as an example.

At www.feiertage-kalender.de, all German holidays for the coming years can be downloaded in CalDAV format. However, the result is a single file containing all appointments. Since the calendar server only supports one appointment per file, the downloaded file must first be split. You can do this here. Select 1 for “Number of events per file:” and check the checkbox “Export as single ZIP file (instead of multiple .ics files)”.

Now change to the local directory of your holiday calendar, extract the downloaded .zip archive and start synchronization:

cd ~/.calendars/feiertage
unzip ~/downloads/feiertage.zip
vdirsyncer sync

Android

Android doesn’t come with CalDAV and CardDAV support out of the box. I use the app DAVx⁵ from the Google Play Store for this.

After installation, open the app and add your CalDAV/CardDAV synchronization account set up above.

  1. Log in with URL and username
  2. Base URL: https://pim.example.org/dav.php \ Username and password as specified above
  3. After successfully creating the account, you still need to specify which calendars and address books should be automatically synchronized from now on.

The already installed calendar app and the associated widget for the home screen are suitable for displaying the synchronized appointments. For optimal use with the Talkback screen reader, it’s recommended to switch the calendar layout to “Agenda view”.