Eugene Ciurana Official Site

Robust Calendar Service Deployment with Baikal / SabreDAV in 10 Minutes

Calendar services are an organization's lifeblood next to email.  This guide shows how to ready and deploy a robust, enterprise-class calendar service compliant with all vendor and Internet calendaring, scheduling, and messaging standards --  enable CalDAV and CardDAV for OS X, iOS, Android, Windows, and Linux calendar clients in 10 minutes or less.  It leverages the power of Baïkal/SabreDAV, Postfix, Docker, and Linux.

About this implementation

  • This is a complete deployment set up, ready for production systems
  • There are lots of Baïkal and other calendar server images out there, but all the ones tested in preparing this deployment were missing important features like scheduling, message delivery, etc.
  • It works with the latest stable SabreDAV/Baïkal versions
  • iOS, OS X, Android, Thunderbird Lightning, and Windows calendar, tasks, and address book clients work well with this set up
  • It supports full scheduling and messaging to event participants and resources across any calendaring service (Baïkal to Google Calendar or Exchange)

Deploying to production in less than 10 Minutes


  1. Linux running Docker or OS X with Docker Machine
  2. A Docker account where to push the images


  • Clone the pr3d4t0r/calendar repository from GitHub
  • Change all instances of "" to your domain name
    • bin/runapache2
    • resources/baikal.apache2
    • resources/Server.php
      • Check your relay hostname - this file assumes
    • Dockerfile
      • Check your relay hostname - this Dockerfile assumes
  • Build the image and push it to Docker Hub
imageName="yourdockerhubname/calendar"; \
docker build -t "$imageName" --rm=true . && \
docker push "$imageName"

On the target server:

  • Create a service account to run the calendar (e.g. /home/calendar or /var/calendar or whatever); these examples assume /home/calendar
  • Create a data directory and an empty database:
# This is the simplest mechanism to get around the permissions mismatch between Docker instances and the local file system
# This could've been resolved with a data volume; that's beyond the scope of this setup
mkdir -p /home/calendar/db && \
chmod 777 /home/calendar/db && \
touch /home/calendar/db/db.sqlite && \
chmod 666 /home/calendar/db/db.sqlite
  • Start a new instance of the service in the /home/calendar directory of the server
docker run --name "calendar.service" \
    --privileged=true \
    -h "calendar" \
    -e "" \
    -v "$(pwd)/db":"/var/www/calendar_server/Specific/db" \
    -p "8800:80" \
    -d "yourdockerhubname/calendar"
  • List the current instances and check if the service is running on the server
docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
83d209fefa91        pr3d4t0r/calendar   "/runapache2"       2 minutes ago       Up 2 minutes>80/tcp   calendar.service
  • Configure the Baikal server by connecting to the instance on port 8800

The installation is complete.  Users, calendars, address books, etc. can all be managed now, like in this production instance.

For information on how to manage the Baïkal instance, client endpoints, principals, etc. please refer to the Baïkal documentation.

Exposing the Baïkal server to Internet

This set up is not designed for direct exposure to the Internet.  This deployment is intended for a private network, behind the firewall, with calendar client access only through a public HTTPS termination reverse proxy server.

Port selection is up to the system administrators and can be changed when the images are instantiated.  The Baïkal server was assigned to port 8800 because there may be other HTTP servers running in the same Docker instance.  Whatever works.

Firewall and routing rules are used for enforcing data flows.  Scheduling messages must only be allowed to flow out of the SMTP calendar server to the corporate relay, never back.  The Baïkal server must support two-way communication to handle the clients requests and updates.

Dockerfile implementation notes

This section addresses know-how and steps required that aren't documented in the Baïkal or SabreDAV documentation and that must be reflected in the Dockerfile.

Postfix installation on Debian-like systems

debconf-set-selections (1) must be automated because the installer requires user interaction.  The configuration in the Dockerfile is specific to an email relay.  Don't forget to change to the actual domain name.  Watch also the bounce email and SMTP relay name.

### "configure-postfix"
# These parameters are specific to your own Postfix relay!  Use your host and domain
# names.
RUN echo "postfix postfix/mailname string" | debconf-set-selections && \
    echo "postfix postfix/main_mailer_type string 'Satellite system'" | debconf-set-selections && \
    echo "postfix postfix/relayhost string" | debconf-set-selections && \
    echo "postfix postfix/root_address string" | debconf-set-selections

Patching the Baïkal Server.php

Baïkal is a reference implementation of the SabreDAV project.  The SabreDAV documentation indicates that the scheduling, and email delivery plug-ins must be installed by hand "on the server".  The Baïkal documentation is very scarce, and has no references to the server or how to do this in Baïkal.  Some tinkering revealed that Baïkal defines its own server, separate from SabreDAV's.

# Scheduling and email delivery.  See:
# This needs to be patched on the Baikal start up Server.php, NOT in the SabreDAV server.
COPY resources/Server.php /var/www/calendar_server/Core/Frameworks/Baikal/Core/Server.php

The updated lines:

        if ($this->enableCalDAV) {
            $this->server->addPlugin(new \Sabre\CalDAV\Plugin());
            $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
            $this->server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin());
            // Scheduling and email delivery.  See:
            // This needs to be patched on the Baikal start up Server.php, NOT in the SabreDAV server.
            $this->server->addPlugin(new \Sabre\CalDAV\Schedule\IMipPlugin(''));

Starting the services

Docker containers have a single entry point.  The version of SabreDAV/Baïkal at the time of this writing requires that the email relay or actual email server run in the same box because it uses PHP's mail() function.

The Dockerfile instruction ENTRYPOINT refers to a single command or service.  This set up requires that three different services start:

  • Apache 2
  • rsyslog to enable logging to /var/log/syslog
  • The Postfix server and all its related services

Apache Server has some conflicts when launched from the service (8) command in Docker containers.  The recommended mechanism is to launch using the /usr/sbin/apache2 executable.

The ENTRYPOINT is at the custom script runapache2:


export APACHE_LOG_DIR="/var/log/apache2"
export APACHE_LOCK_DIR="/var/lock/apache2"
export APACHE_RUN_USER="www-data"
export APACHE_RUN_GROUP="www-data"
export APACHE_PID_FILE="/var/run/apache2/apache"
export APACHE_RUN_DIR="/var/run/apache2"


service rsyslog start
service postfix start

/usr/sbin/apache2 -DFOREGROUND

Questions or comments?

Do you want to customize this Calendar Service?  Fork the project on GitHub!

Contact me on @ciurana or follow my tech page on Facebook.

Written by Eugene Ciurana on Tuesday August 2, 2016
Permalink -

« Gijón Style Cherry Ice Cream - Improved: Baikal / SabreDAV Calendar in Less Than 10 Minutes »