Tools: How to Self-Host Baikal with Docker Compose
What Is Baikal?
Prerequisites
Docker Compose Configuration
SQLite Setup (Recommended for Most Users)
MariaDB Setup (Optional — For Large Deployments)
Initial Setup
Creating Users
Creating Additional Calendars and Address Books
Connecting Clients
iOS (Native Calendar and Contacts)
macOS Calendar and Contacts
Android (DAVx5)
Thunderbird
Configuration
CalDAV Auto-Discovery
Changing the Admin Password
File Permissions
Reverse Proxy
Backup
Backing Up Named Volumes
Restoring from Backup
MariaDB Backup
Troubleshooting
Calendar Not Syncing Across Devices
Permission Denied or 403 Errors
Web Wizard Doesn't Load on First Run
Upgrade Issues After Updating the Docker Image
iOS/macOS Shows "Cannot Verify Server Identity"
Verdict
Related Baikal is a lightweight CalDAV and CardDAV server that syncs your calendars and contacts across all your devices. It replaces Google Calendar, Google Contacts, iCloud Calendar, and iCloud Contacts with something you control entirely. Baikal is built on the sabre/dav framework (PHP), includes a clean web-based admin panel for managing users, calendars, and address books, and runs on SQLite or MariaDB. It uses minimal resources — under 50 MB of RAM — and handles multi-user setups without breaking a sweat. Baikal uses a community-maintained Docker image (ckulka/baikal) since there is no official image from the project. The nginx variant is recommended — it is lighter and faster than the Apache variant. SQLite is the default and works perfectly for personal use or small teams (under 50 users). No extra services needed. Create a docker-compose.yml file: If you expect many users or want a database you can query and back up independently, use MariaDB instead of SQLite. You configure the database during the web wizard — this Compose file just makes MariaDB available. Create a docker-compose.yml file: When you reach the database step in the web wizard, select MySQL and enter: Baikal uses a web wizard for initial configuration. There are no environment variables to set — everything happens through the browser. After setup, the admin panel is available at http://your-server-ip:8080/admin/. Each user automatically gets a default calendar and address book. Baikal's CalDAV and CardDAV URLs follow this pattern: The default calendar is named default and the default address book is named default. So for a user called john: Most clients support auto-discovery, so you only need to provide the base URL: For clients that support auto-discovery (iOS, macOS, DAVx5), configure your reverse proxy to handle .well-known URLs: This lets clients find the CalDAV/CardDAV server using just the domain name, without needing the full /dav.php path. Log into the admin panel at /admin/, navigate to Settings, and update the admin password. The nginx variant of the Docker image runs as UID 101 (the nginx user). If you use bind mounts instead of named volumes, ensure the host directories are owned by UID 101: With named volumes (as shown in the Compose examples above), Docker handles permissions automatically. Most CalDAV/CardDAV clients require HTTPS. Set up a reverse proxy with SSL termination in front of Baikal. Caddy (in your Caddyfile): See Reverse Proxy Setup for full configuration with other proxies. Baikal stores all its data in two volumes. Back up both: If using MariaDB instead of SQLite, dump the database separately: See Backup Strategy for a complete 3-2-1 backup approach. Symptom: Events created on one device do not appear on another, even after waiting several minutes.
Fix: Verify that both devices are pointed at the exact same CalDAV URL. The URL is case-sensitive and must include the correct calendar name. In DAVx5, open the account and tap the sync button. On iOS, pull down on the calendar list to force a refresh. Check Baikal logs for errors: If events sync one direction but not the other, the second client may be using a different calendar path. Symptom: Client returns 403 Forbidden when creating or modifying events/contacts.Fix: This usually means the client is trying to write to a calendar or address book that belongs to a different user. Verify the URL includes the correct username. Each user can only access their own collections unless you configure shared access through the admin panel. Also check file permissions if using bind mounts — the container needs write access as UID 101: Symptom: Accessing http://your-server-ip:8080 shows a blank page, a 500 error, or the Baikal dashboard instead of the setup wizard.Fix: The wizard only runs when the config volume is empty. If a previous failed setup left partial configuration files, the wizard won't appear. Remove the config volume and start fresh: If you see a 500 error, check that the baikal_config and baikal_data volumes are writable by the container. With bind mounts, ensure UID 101 owns the directories. Symptom: After pulling a new version of ckulka/baikal, the admin panel shows errors or calendar sync breaks.Fix: Baikal runs database migrations automatically on startup, but occasionally a migration can fail. Check the logs: If you see database migration errors, the safest path is: Pin your image tag (as shown in the Compose examples) so upgrades only happen when you explicitly change the tag. Symptom: Apple devices refuse to connect and show certificate warnings.
Fix: Apple is strict about SSL certificates. Self-signed certificates will not work. You need a valid certificate from Let's Encrypt or another CA. Set up a reverse proxy with automatic SSL (see the Reverse Proxy section above). Also ensure your .well-known redirects are configured — Apple clients rely on these for CalDAV/CardDAV discovery. Baikal is one of the lightest self-hosted services you can run. It fits comfortably on a Raspberry Pi alongside a dozen other containers. Baikal is the best CalDAV/CardDAV server for most people who want calendar and contact sync without the bloat. It has a clean admin panel for managing users and collections, supports both SQLite and MariaDB, works with every major CalDAV/CardDAV client, and uses almost no resources. For a household or small team that just needs reliable calendar and contact sync, Baikal is the right choice. If you want an even more minimal setup and don't need a web admin panel, Radicale stores data as flat files and uses even less memory. If you need file sync, office collaboration, or other groupware features alongside calendars and contacts, Nextcloud is the better (but heavier) option. For pure CalDAV/CardDAV, Baikal hits the sweet spot between simplicity and usability. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse