---
title: "Deploy Baikal"
description: "Open-source Google Calendar and Contacts alternative"
category: "Other"
url: https://railway.com/deploy/baikal
---

# Deploy Baikal

Open-source Google Calendar and Contacts alternative

**[Deploy Baikal on Railway](https://railway.com/template/baikal)**

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### Postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### baikal https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/baikal.svg

- **Source:** https://github.com/gridalpha/baikal-railway
- **Public domain:** Yes

## Documentation

# Deploy and Host Baïkal on Railway

Baïkal is a lightweight CalDAV and CardDAV server built on sabre/dav: it stores calendars, events, tasks and contacts and syncs them to the apps you already use — iOS and macOS Calendar and Contacts, DAVx⁵ on Android, Thunderbird, vdirsyncer. It is what you need to leave Google Calendar and Contacts without changing how anyone works: the built-in apps keep working, they just talk to a server you own.

Deploy Baïkal on Railway as two services. The `baikal` service is public over HTTPS and builds from [github.com/gridalpha/baikal-railway](https://github.com/gridalpha/baikal-railway), a Dockerfile that unpacks the official `baikal-0.12.1.zip` release onto `php:8.4-apache`, since upstream publishes no image of its own. A managed **PostgreSQL 18** database on the private network holds every calendar, event, task, address book and contact. You self-host Baïkal here without its install wizard: on first boot the container writes `config/baikal.yaml`, provisions a non-superuser database role, loads the schema and closes the public installer, so the deployment arrives installed with the admin password you chose.

![Baïkal Railway architecture](https://res.cloudinary.com/rroe4rtk/image/upload/v1787077079/35aec204-05e6-4ddb-bc50-249c0b6e9c9c.png)

## Getting Started with Baïkal on Railway

Set `BAIKAL_ADMIN_PASSWORD` when you deploy: it becomes the password for the built-in `admin` account, and it is the only credential the template creates. Once both services are green, open the public URL and it lands on the admin login at `/admin/`. Sign in as `admin` and the dashboard reports the running version with Web admin, CalDAV and CardDAV each shown as `On`. That account administers the server and is not a sync account, so the first real step is **Users → + Add user**: give the person a username, display name and password, and Baïkal creates a default calendar and address book for them. Point a client at `https://your-domain.up.railway.app/dav.php` with those credentials; `/.well-known/caldav` and `/.well-known/carddav` redirect there, so iOS and macOS accept the bare domain when adding a CalDAV account. To confirm sync works, save an event on the phone and watch the count rise on the dashboard.

![Baïkal admin dashboard with CalDAV and CardDAV service statistics](https://res.cloudinary.com/rroe4rtk/image/upload/v1787069455/baikal-dashboard-statistics.png)

![Baïkal user account list with calendar and address book actions](https://res.cloudinary.com/rroe4rtk/image/upload/v1787069490/baikal-user-accounts.png)

![Baïkal calendar list for a user showing one stored event](https://res.cloudinary.com/rroe4rtk/image/upload/v1787069498/baikal-user-calendars.png)

## About Hosting Baïkal

CalDAV and CardDAV are how phones and laptops sync calendars and contacts, but most implementations are buried inside something larger: a mail suite, a file-sync platform, a cloud account. Baïkal is the standalone version. Self-host it when a household's or a team's schedules should leave third-party servers while native apps keep working.

Key features:

- CalDAV calendars with events and VTODO tasks, plus CardDAV address books
- Web admin for users, calendars and address books, no config files to edit
- Digest, Basic or Apache-delegated authentication for DAV clients
- Free/busy lookups and optional invitation email over SMTP
- Works with iOS, macOS, DAVx⁵, Thunderbird, Evolution, vdirsyncer and khal

The architecture is flat by design. The `baikal` service is Apache with mod_php serving Baïkal's `html/`, which answers three kinds of request: the admin UI at `/admin/`, the DAV endpoints at `/dav.php`, and the `.well-known` redirects clients probe first. PostgreSQL holds all synced data, and the `/data` volume keeps the YAML config the admin UI rewrites.

## Why Deploy Baïkal on Railway

Railway removes the server-admin work self-hosting Baïkal usually implies:

- PostgreSQL 18 over private networking, with no install wizard to click through
- Admin account, database role and schema created on first boot
- Free HTTPS on the generated domain; custom domains for a stable client URL
- `.well-known` discovery and `Authorization` pass-through already configured
- Volume-backed config, health checks and automatic restarts

## Common Use Cases for Self-Hosted Baïkal

- A household or small team moving calendars and contacts off Google or iCloud while keeping the stock iPhone, Mac and Android apps
- One DAV backend other tools sync against: Thunderbird, vdirsyncer, khal, or any task app that speaks CalDAV
- Shared team calendars for on-call rotations or room bookings, inside infrastructure you control

## Dependencies for Baïkal

- **baikal** — the official `baikal-0.12.1.zip` release on `php:8.4-apache` with `pdo_pgsql` and `msmtp`, built from [github.com/gridalpha/baikal-railway](https://github.com/gridalpha/baikal-railway). Serves port 8080, mounts `/data`.
- **Postgres** — Railway's managed PostgreSQL 18 (`ghcr.io/railwayapp-templates/postgres-ssl:18`), private, holding users, calendars, events, tasks, address books and contacts. The app provisions its own scoped role and database here instead of using the superuser.

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `BAIKAL_ADMIN_PASSWORD` | Password for the `admin` account — required |
| `DATABASE_URL` | `${{Postgres.DATABASE_URL}}`; provisions the app's role and database |
| `BAIKAL_TIMEZONE` | Server timezone, default `UTC`, editable in the admin UI later |
| `BAIKAL_DAV_AUTH_TYPE` | `Digest` (default), `Basic` or `Apache` for DAV clients |
| `BAIKAL_DB_BACKEND` | `pgsql` by default; `sqlite` keeps data on the volume |
| `BAIKAL_ADMIN_PASSWORD_FORCE_RESET` | `true` for one deploy replaces a lost password |
| `BAIKAL_INVITE_FROM`, `SMTP_HOST` and friends | Optional relay for invitations |

### Deployment Dependencies

- Upstream project: [github.com/sabre-io/Baikal](https://github.com/sabre-io/Baikal), docs at [sabre.io/baikal](https://sabre.io/baikal/)
- Image source for this template: [github.com/gridalpha/baikal-railway](https://github.com/gridalpha/baikal-railway)
- Runtime: PHP 8.4 with Apache and mod_php; PostgreSQL 18 (MySQL and SQLite also supported)

## Hardware Requirements for Self-Hosting Baïkal

Baïkal is one of the lightest things you can self-host: a few hundred kilobytes per calendar, and no background jobs.

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 1 vCPU |
| RAM | 256 MB | 512 MB (`PHP_MEMORY_LIMIT` defaults to 256M) |
| Storage | 1 GB volume, small database | 1 GB volume, 2 GB database |
| Runtime | PHP 8.x with Apache or Nginx | PHP 8.4 + Apache, PostgreSQL 18 |

Sync-heavy client fleets need concurrency before memory: raise `BAIKAL_MAX_WORKERS` from its default of 32 first.

## Self-Hosting Baïkal with Docker

There is no official Baïkal image, so the shortest path outside Railway is to build the one this template uses. Cloned locally it runs on SQLite with no database container:

```
git clone https://github.com/gridalpha/baikal-railway.git
cd baikal-railway
docker build -t baikal-railway .
docker run --rm -p 8080:8080 \
  -e BAIKAL_ADMIN_PASSWORD=change-me \
  -e BAIKAL_DB_BACKEND=sqlite \
  -v baikal-data:/data \
  baikal-railway
```

Open `http://localhost:8080/admin/` and sign in as `admin`. For the PostgreSQL setup this template uses, save this as `docker-compose.yml` beside the clone:

```
services:
  baikal:
    build: .
    ports: ["8080:8080"]
    environment:
      BAIKAL_ADMIN_PASSWORD: change-me
      DATABASE_URL: postgresql://postgres:secret@db:5432/postgres
    volumes: ["baikal-data:/data"]
  db:
    image: postgres:18
    environment:
      POSTGRES_PASSWORD: secret
volumes:
  baikal-data:
```

## Is Baïkal Free? Self-Hosting Costs Explained

Baïkal is free and open source under the GPL-3.0 licence, with no paid edition and no seat count. On Railway the only cost is the infrastructure these two services use, and a small PHP process with no background workers sits near the bottom of that range.

## FAQ

**What is Baïkal?**

Baïkal is a lightweight, self-hosted CalDAV and CardDAV server built on sabre/dav. It serves calendars, events, tasks and contacts over the open sync standards, so native apps on iOS, macOS, Android and Thunderbird use it directly. It is GPL-3.0 licensed.

**What does this Railway template deploy?**

Two services: Baïkal 0.12.1 on a public HTTPS domain with a `/data` volume, and a private managed PostgreSQL 18 database for all synced data. The app builds from a public source repository, because upstream ships no Docker image.

**What are the login credentials for self-hosted Baïkal on Railway?**

There is no shipped default password. The admin account is `admin` and its password is the `BAIKAL_ADMIN_PASSWORD` you set at deploy time. Everyone who syncs a device gets a separate DAV user you create under **Users**.

**Why does the Baïkal template include PostgreSQL and a volume?**

PostgreSQL holds every calendar, event, task and contact, so data outlives any container restart. The volume keeps Baïkal's YAML config, which the admin UI rewrites when you change a setting.

**How do I sync self-hosted Baïkal with my iPhone or DAVx⁵ on Android?**

Use `https://your-domain/dav.php` with a DAV user's credentials. On iOS, add a CalDAV account and enter just the domain, since `/.well-known/caldav` redirects to the right path. DAVx⁵ discovers both collections from the same base URL.

**How do I reset a forgotten Baïkal admin password on Railway?**

Set `BAIKAL_ADMIN_PASSWORD` to the new value and `BAIKAL_ADMIN_PASSWORD_FORCE_RESET=true`, redeploy once, then set the flag back to `false`. DAV user passwords are unaffected.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/baikal
