Baikal on AWS User Guide
Overview
Baikal is an open source self-hosted CalDAV and CardDAV server built on the proven Sabre/dav library. It lets you sync calendars and contacts across all of your devices using the open CalDAV and CardDAV standards, while keeping that data private on infrastructure you control. This image runs Baikal behind nginx, with PHP FPM 8.3 and OPcache enabled.
Baikal stores its data in SQLite, so the appliance is fully self-contained: there is no separate database server to install, secure or back up. The configuration, the SQLite database and the DAV data live under /var/www/baikal on a dedicated, independently resizable EBS volume, so you can grow your calendar and contact store without touching the operating system disk.
The browser setup wizard is already completed in the image, so there is no first-run configuration to step through. The Baikal administrator password and the application encryption key are generated on the first boot of every deployed instance, and both the admin web console and the CalDAV/CardDAV account are set to that same per instance password. Two instances launched from the same Amazon Machine Image never share credentials. The initial administrator password is written to /root/baikal-credentials.txt with mode 0600 so that only the root user can read it.

Prerequisites
Before you deploy this image you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound ports 80 and 443 from the networks your client devices will use
- The AWS CLI (version 2) installed locally if you plan to deploy from the command line
Step 1: Launch the Instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for Baikal. Select the cloudimg listing and choose Select, then Continue on the subscription summary.
Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and either create or select a security group that allows inbound port 22 from your management network and inbound ports 80 and 443 from the networks your client devices use. Leave the root and data volumes at their default sizes or larger.
Select Launch instance. First boot initialisation takes approximately one minute after the instance state becomes Running and the status checks pass.
Step 2: Launch the Instance from the AWS CLI
The following block launches an instance from the cloudimg Baikal Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22, 80, and 443 as described above.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":12,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=baikal-01}]'
The command prints a JSON document on success. Note the instance ID, then retrieve its public address once it is running with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.
Step 3: Connect and Retrieve Initial Credentials
Connect over SSH with the key pair you selected and the public IP address from step 2. The SSH login user depends on the operating system of the AMI variant you launched:
| AMI variant | SSH login user |
|---|---|
| Baikal 0.11 on Ubuntu 24.04 | ubuntu |
For the Ubuntu 24.04 variant, connect with ssh -i <path-to-key.pem> ubuntu@<instance-public-ip>.
The administrator credentials generated on first boot are stored in a root only file. Retrieve them with the following command, which prints the Baikal administrator user name and password for this specific instance:
sudo cat /root/baikal-credentials.txt
The file lists the administrator user name (admin), the generated password, the admin console URL and the CalDAV/CardDAV endpoint URL. Store the password in your password manager and treat the file as sensitive.
Step 4: Verify the Baikal Service
The web stack is two systemd services: nginx and PHP FPM. Confirm both are active:
systemctl is-active nginx php8.3-fpm
Both lines should read active. Confirm nginx is listening on port 80:
ss -tlnp 2>/dev/null | grep ':80 ' || sudo ss -tlnp | grep ':80 '
Confirm the dedicated data volume holding the configuration and SQLite database is mounted:
df -h /var/www/baikal | tail -1
Confirm the Baikal admin console is being served locally:
curl -s -o /dev/null -w 'Baikal admin HTTP %{http_code}\n' http://127.0.0.1/admin/
An HTTP 200 response confirms the application is up. You can also confirm the DAV endpoint requires authentication and accepts the generated administrator password. The first command should report 401 and the second 207:
curl -s -o /dev/null -w 'anonymous PROPFIND HTTP %{http_code}\n' -X PROPFIND -H 'Depth: 0' http://127.0.0.1/dav.php/
PASS=$(sudo grep '^baikal.admin.pass=' /root/baikal-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authenticated PROPFIND HTTP %{http_code}\n' -X PROPFIND --digest -u admin:$PASS -H 'Depth: 0' http://127.0.0.1/dav.php/
Step 5: First Login to the Admin Console
Open a web browser and navigate to http://<instance-public-ip>/admin/. You are presented with the Baikal Web Admin sign-in page. Sign in with the user name admin and the password from /root/baikal-credentials.txt.
The dashboard summarises the running system: the Baikal version, which of the Web admin, CalDAV and CardDAV services are enabled, and statistics for users, calendars, events, address books and contacts.

Step 6: Create Users, Calendars and Address Books
Open Users and resources in the top navigation to manage Baikal accounts. The image ships with a single administrator account. Use + Add user to create an account for each person who will sync calendars and contacts, and use the Calendars and Address Books buttons on each user row to create and manage their resources.

Each user authenticates to the CalDAV and CardDAV endpoints with the user name and password you set here.
Step 7: Connect CalDAV and CardDAV Clients
Point any CalDAV or CardDAV client (the calendar and contacts apps on iOS, macOS, Android with DAVx5, Thunderbird, Evolution, and others) at your instance to sync calendars and contacts. The base DAV endpoint is:
http://<instance-public-ip>/dav.php/
Most clients also support automatic service discovery through the well-known URLs /.well-known/caldav and /.well-known/carddav, which this image redirects to the DAV endpoint. Enter the user name and password of the Baikal account you want to sync. Because this is your own self-hosted instance, your calendar and contact data never leaves infrastructure you control.
Step 8: Enable HTTPS with Let's Encrypt
For production use you should serve Baikal over HTTPS so that client credentials and your calendar and contact data are encrypted in transit. Point a DNS A record at the instance's public IP address, then install a certificate with Certbot. Replace the placeholders with your domain and email address.
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
Run Certbot against your domain name and follow the prompts to obtain and install the certificate, then point your CalDAV and CardDAV clients at the https:// URL instead. After enabling HTTPS, restrict inbound port 80 to redirect-only or close it at the security group, leaving port 443 open to your client networks.
Step 9: Backup and Maintenance
All Baikal state lives under /var/www/baikal on the dedicated data volume: the configuration in config/baikal.yaml and the SQLite database under Specific/db. Because the data volume is a separate EBS volume, the simplest backup is a point-in-time EBS snapshot:
aws ec2 create-snapshot --volume-id <data-volume-id> --description "Baikal data backup"
To find the data volume ID, describe the instance's block device mappings. For a file-level backup you can instead copy the config directory and the Specific/db/db.sqlite database file to object storage on a schedule.
Apply operating system security updates with sudo apt-get update && sudo apt-get upgrade. The nginx and PHP FPM services restart cleanly, and Baikal resumes serving immediately because all state is on the persistent data volume.
Support
This image is published and supported by cloudimg. For deployment help, client configuration questions, upgrade guidance, or storage administration, contact cloudimg support by email or chat. Baikal itself is open source software licensed under the GNU GPL version 3.