How to Self-Host Davis with Docker Compose

What Is Davis?

Davis is a self-hosted CalDAV and CardDAV server built on the sabre/dav library with a Symfony backend. It provides calendar and contact synchronization across all your devices — phones, tablets, desktops — without relying on Google, Apple, or Microsoft. Davis includes a clean web dashboard for managing users, calendars, and address books, plus support for LDAP and IMAP authentication. It replaces Google Calendar, Google Contacts, and iCloud calendar/contacts sync.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 256 MB of free RAM
  • 500 MB of free disk space
  • A domain name (recommended for CalDAV/CardDAV client configuration)

Docker Compose Configuration

Davis offers two Docker images: davis (requires an external web server like nginx) and davis-standalone (includes nginx — easier to set up). This guide uses the standalone image.

Create a docker-compose.yml file:

services:
  davis:
    image: ghcr.io/tchapi/davis-standalone:5.3.0
    container_name: davis
    restart: unless-stopped
    ports:
      - "9000:8080"
    environment:
      APP_ENV: prod
      APP_SECRET: ${APP_SECRET}
      APP_TIMEZONE: ${APP_TIMEZONE:-America/New_York}
      DATABASE_DRIVER: mysql
      DATABASE_URL: mysql://davis:${DB_PASSWORD}@davis-db:3306/davis?serverVersion=10.11.6-MariaDB&charset=utf8mb4
      ADMIN_LOGIN: ${ADMIN_LOGIN:-admin}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      CALDAV_ENABLED: "true"
      CARDDAV_ENABLED: "true"
      WEBDAV_ENABLED: "false"
      AUTH_REALM: SabreDAV
      AUTH_METHOD: Basic
      INVITE_FROM_ADDRESS: [email protected]
    depends_on:
      davis-db:
        condition: service_healthy

  davis-db:
    image: mariadb:10.11
    container_name: davis-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: davis
      MYSQL_USER: davis
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - davis_db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  davis_db:

Create a .env file:

# Generate with: openssl rand -hex 32
APP_SECRET=CHANGE_ME_TO_A_RANDOM_HEX_STRING

# Timezone — see https://www.php.net/manual/en/timezones.php
APP_TIMEZONE=America/New_York

# Admin dashboard credentials — CHANGE THESE
ADMIN_LOGIN=admin
ADMIN_PASSWORD=CHANGE_ME_TO_A_STRONG_PASSWORD

# Database passwords — CHANGE THESE
DB_PASSWORD=CHANGE_ME_DAVIS_DB_PASSWORD
DB_ROOT_PASSWORD=CHANGE_ME_ROOT_DB_PASSWORD

Start the stack:

docker compose up -d

After first start, run the database migrations:

docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"

Initial Setup

  1. Open http://your-server-ip:9000 in your browser
  2. Log in with the admin credentials from your .env file
  3. The dashboard shows the server status — CalDAV and CardDAV should both show as enabled
  4. Create user accounts via the dashboard — each user gets their own calendars and address books

Connecting Clients

Davis uses standard CalDAV/CardDAV protocols. Connection URLs:

ProtocolURL
CalDAVhttps://your-domain.com/dav/calendars/USERNAME/
CardDAVhttps://your-domain.com/dav/addressbooks/USERNAME/
Auto-discoveryhttps://your-domain.com/.well-known/caldav

iOS/macOS: Settings → Calendar → Accounts → Add Account → Other → CalDAV. Enter your server URL, username, and password. iOS auto-discovers both calendars and contacts from the CalDAV URL.

Android: Use DAVx⁵ (F-Droid or Play Store). Add a new account with your server URL. DAVx⁵ handles both CalDAV and CardDAV.

Thunderbird: Calendar → New Calendar → Network → CalDAV. Contacts → New Address Book → CardDAV.

Configuration

Key Environment Variables

VariableDefaultDescription
APP_SECRETRequired. Symfony secret for session encryption
APP_TIMEZONEServer timezone for calendar display
DATABASE_DRIVERmysqlDatabase backend: mysql, postgresql, or sqlite
DATABASE_URLRequired. Database connection string
ADMIN_LOGINadminDashboard admin username
ADMIN_PASSWORDtestDashboard admin password — change this
CALDAV_ENABLEDtrueEnable calendar support
CARDDAV_ENABLEDtrueEnable contacts support
WEBDAV_ENABLEDfalseEnable file storage via WebDAV
AUTH_METHODBasicAuthentication method: Basic, IMAP, or LDAP
PUBLIC_CALENDARS_ENABLEDtrueAllow calendars to be shared publicly
INVITE_FROM_ADDRESSSender address for calendar invitations

Using SQLite (Simpler Setup)

For small deployments (1-5 users), SQLite eliminates the need for a separate database container:

services:
  davis:
    image: ghcr.io/tchapi/davis-standalone:5.3.0
    container_name: davis
    restart: unless-stopped
    ports:
      - "9000:8080"
    environment:
      APP_ENV: prod
      APP_SECRET: ${APP_SECRET}
      DATABASE_DRIVER: sqlite
      DATABASE_URL: "sqlite:///%kernel.project_dir%/var/davis.db"
      ADMIN_LOGIN: admin
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      CALDAV_ENABLED: "true"
      CARDDAV_ENABLED: "true"
    volumes:
      - davis_data:/var/www/davis/var
volumes:
  davis_data:

LDAP Authentication

Connect Davis to an LDAP server (like LLDAP or OpenLDAP) for centralized user management:

AUTH_METHOD=LDAP
LDAP_AUTH_URL=ldap://your-ldap-server:3890
LDAP_DN_PATTERN=uid=%u,ou=people,dc=yourdomain,dc=com
LDAP_MAIL_ATTRIBUTE=mail
LDAP_AUTH_USER_AUTOCREATE=true

Reverse Proxy

CalDAV and CardDAV clients rely on .well-known auto-discovery URLs. Your reverse proxy must forward these paths:

/.well-known/caldav  → http://davis:8080
/.well-known/carddav → http://davis:8080

In Nginx Proxy Manager, set the scheme to http, hostname to davis, and port to 8080. Enable SSL with Let’s Encrypt.

See Reverse Proxy Setup for full configuration.

Backup

Back up the database volume:

  • MariaDB: docker exec davis-db mysqldump -u davis -p davis > davis_backup.sql
  • SQLite: Back up the davis_data volume directly

Calendar and contact data lives entirely in the database — there are no separate data files to back up.

See Backup Strategy for automated backup workflows.

Troubleshooting

500 error on first access

Symptom: Browser shows a 500 Internal Server Error after starting the containers. Fix: You likely forgot to run the database migrations. Execute:

docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"

“Bad timezone configuration env var” on the dashboard

Symptom: Dashboard shows a timezone warning. Fix: The APP_TIMEZONE value is not a valid PHP timezone. Check the official list and update your .env file.

Clients cannot discover calendars

Symptom: iOS or DAVx⁵ says “no calendars found.” Fix: Ensure your reverse proxy forwards /.well-known/caldav and /.well-known/carddav to the Davis container. Also verify the user has at least one calendar created via the admin dashboard.

LDAP connection failures

Symptom: Users cannot log in with LDAP credentials. Fix: Ensure Davis and the LDAP server share a Docker network. Test connectivity from inside the container:

docker exec -it davis sh -c "apk add openldap-clients && ldapsearch -H ldap://your-ldap:3890 -D 'uid=testuser,ou=people,dc=domain,dc=com' -W"

Check that LDAP_DN_PATTERN matches your LDAP directory structure. LLDAP uses ou=people, while OpenLDAP often uses ou=users.

Resource Requirements

  • RAM: ~80 MB idle, ~150 MB under load
  • CPU: Very low — CalDAV/CardDAV is lightweight
  • Disk: ~200 MB for the application, database grows slowly (calendars and contacts are small)

Verdict

Davis is the most polished CalDAV/CardDAV server for self-hosters who want a web dashboard and modern authentication options. The standalone Docker image makes deployment straightforward, and LDAP support integrates cleanly with existing identity infrastructure. For users who only need basic calendar/contact sync without a web UI, Radicale is lighter — it runs in a single Python process with file-based storage. Baikal sits between the two, offering a simpler admin interface but less flexibility on authentication. Davis is the right choice when you need LDAP/IMAP auth, multiple users, or public calendar sharing.