How to Self-Host EteSync with Docker Compose
EteSync started as a privacy-focused alternative to Google Calendar and Contacts, adding end-to-end encryption that no other CalDAV server offers. The current version — Etebase (EteSync 2.0) — encrypts all data client-side before it reaches the server, so even a compromised server reveals nothing. If zero-knowledge sync is your requirement, this is the only self-hosted option that delivers it.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 512 MB of free RAM
- 500 MB of free disk space
- A domain name with HTTPS (required — CalDAV clients reject plaintext connections)
Docker Compose Configuration
Etebase uses an INI configuration file rather than environment variables. You’ll need to create the config file before starting the container.
First, create the configuration file etebase-server.ini:
[global]
secret_file = /data/secret.txt
debug = false
static_root = /srv/etebase/static
media_root = /srv/etebase/media
[allowed_hosts]
; CHANGE THIS — your actual domain name
allowed_host1 = etebase.example.com
[database]
engine = django.db.backends.sqlite3
name = /data/db.sqlite3
For production with PostgreSQL, replace the [database] section:
[database]
engine = django.db.backends.postgresql
name = etebase
[database-options]
host = etebase-db
port = 5432
user = etebase
password = change-this-db-password
Create the docker-compose.yml:
services:
etebase:
image: victorrds/etebase:0.14.2
container_name: etebase
restart: unless-stopped
ports:
- "3735:3735"
environment:
SERVER: "asgi"
# Auto-generate admin account on first run
AUTO_SIGNUP: "false"
SUPER_USER: "admin"
SUPER_PASS: "change-this-admin-password"
SUPER_EMAIL: "[email protected]"
volumes:
- etebase_data:/data
- ./etebase-server.ini:/etc/etebase-server/etebase-server.ini:ro
depends_on:
etebase-db:
condition: service_healthy
etebase-db:
image: postgres:16-alpine
container_name: etebase-db
restart: unless-stopped
environment:
POSTGRES_USER: "etebase"
POSTGRES_PASSWORD: "change-this-db-password"
POSTGRES_DB: "etebase"
volumes:
- etebase_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U etebase"]
interval: 10s
timeout: 5s
retries: 5
volumes:
etebase_data:
etebase_db:
Start the stack:
docker compose up -d
Initial Setup
- Open
https://etebase.example.comin a browser (HTTPS required) - The admin account was created automatically via the
SUPER_USER/SUPER_PASSenvironment variables on first run - Log into the Django admin at
https://etebase.example.com/admin/to manage users - Disable
AUTO_SIGNUPin the environment if you don’t want open registration
Connecting Client Apps
EteSync uses its own protocol — not standard CalDAV directly. You have two options:
Option 1: EteSync native apps (recommended)
- Android: EteSync app on Google Play
- iOS: EteSync app on App Store
- Web: EteSync web client
- Desktop: Use the web client or Thunderbird with EteSync add-on
Option 2: CalDAV/CardDAV bridge (for standard clients)
- Install DAVx⁵ on Android with the EteSync plugin
- Use the
etesync-davbridge for desktop CalDAV clients
Enter your server URL (https://etebase.example.com), username, and password in the client app. The app handles encryption key generation transparently.
Configuration
Security Settings
| Setting | Value | Purpose |
|---|---|---|
AUTO_SIGNUP | false | Prevent open registration on public servers |
debug | false | Never enable in production — exposes internal state |
allowed_host1 | Your domain | Django rejects requests from unlisted hosts |
Secret Key
Etebase auto-generates a secret.txt file in /data/ on first run. This file is critical — it signs authentication tokens and encrypts session data. Back this file up. Losing it makes the database inaccessible.
SQLite vs PostgreSQL
SQLite works for personal use (1-3 users, under 10,000 entries). Switch to PostgreSQL for:
- Multiple concurrent users
- Large calendar/contact collections
- Better backup reliability (pg_dump vs file copy)
Reverse Proxy
The Etebase container serves on port 3735. Put it behind a reverse proxy with HTTPS — CalDAV clients require TLS.
For Caddy:
etebase.example.com {
reverse_proxy localhost:3735
}
For Nginx Proxy Manager, forward port 3735 with SSL enabled and WebSocket support (not strictly required but doesn’t hurt). Set proxy_set_header Host $host; — Etebase validates the Host header against allowed_hosts.
See the Reverse Proxy Setup guide for detailed instructions.
Backup
| Data | Location | Priority |
|---|---|---|
| Secret key | /data/secret.txt | Critical — losing this makes database unrecoverable |
| Database | PostgreSQL volume or /data/db.sqlite3 | Critical — all user data and encrypted blobs |
| Media files | /srv/etebase/media (inside container) | Critical — uploaded attachments |
# PostgreSQL backup
docker exec etebase-db pg_dump -U etebase etebase > etebase_backup.sql
# SQLite backup (if using SQLite)
docker cp etebase:/data/db.sqlite3 ./etebase-db-backup.sqlite3
# Secret key backup
docker cp etebase:/data/secret.txt ./etebase-secret.txt.backup
See the Backup Strategy guide for automated workflows.
Troubleshooting
”Bad Request (400)” on Every Request
Symptom: Browser shows 400 error, logs show DisallowedHost.
Fix: The allowed_hosts setting in etebase-server.ini must include your exact domain. If accessing via IP, add the IP as another allowed_host2 = 192.168.1.x. Restart the container after changes.
CalDAV Clients Can’t Connect
Symptom: Thunderbird or iOS Calendar refuses to connect.
Fix: Standard CalDAV clients don’t support the Etebase protocol directly. You need either the EteSync native app or the etesync-dav bridge to translate between protocols. DAVx⁵ on Android supports EteSync natively with the plugin.
Admin Account Not Created
Symptom: Can’t log into /admin/ after first run.
Fix: The SUPER_USER and SUPER_PASS environment variables only take effect on the very first container start. If the database already exists, create an admin manually:
docker exec -it etebase python manage.py createsuperuser
Data Not Syncing Between Devices
Symptom: Changes on one device don’t appear on another. Fix: Verify both devices point to the same server URL. EteSync uses conflict-free sync — changes may take a few minutes to propagate. Check the sync log in the client app. If using the DAV bridge, ensure the bridge is running and connected to the Etebase server.
Resource Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 256 MB | 512 MB |
| CPU | 1 core | 2 cores |
| Disk | 500 MB (app) + data | 1 GB + data |
Etebase is lightweight for a Django application. The encryption operations are handled client-side, so the server’s CPU load is minimal. With PostgreSQL, add another 256 MB for the database.
Verdict
EteSync (Etebase) is the only self-hosted calendar/contacts server that provides true end-to-end encryption. If you need zero-knowledge guarantees — where even the server admin can’t read your calendar events — this is it. The encryption is properly implemented and the protocol is well-documented.
The trade-off is real though: limited CalDAV client compatibility (you need native apps or bridges), a smaller community than Radicale or Baikal, and slower development pace. For most self-hosters who already control their server and trust their disk encryption, Radicale offers a simpler path. But for privacy-first users, EteSync is worth the extra setup.
FAQ
What’s the difference between EteSync and Etebase?
Etebase is the rewritten protocol and server (EteSync 2.0). The old EteSync 1.x protocol is deprecated. New deployments should always use Etebase. The two protocols are not compatible — you can’t upgrade an EteSync 1.x server to Etebase without a fresh install.
Does EteSync work with Apple Calendar on iPhone?
Not directly. iOS Calendar only supports standard CalDAV, not the Etebase protocol. Use the official EteSync iOS app for native encrypted sync, or set up the etesync-dav bridge on your server to expose a CalDAV interface.
Can I use EteSync for notes and tasks too?
Yes. Etebase supports encrypted calendar events, contacts, tasks (VTODO), and notes. The EteSync Notes app provides encrypted note sync across devices.
How is the encryption different from just using HTTPS?
HTTPS encrypts data in transit. EteSync encrypts data at rest on the server too — using keys that only exist on your devices. Even with full server access and database dumps, your calendar events are encrypted blobs without the device key.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments