MAATRIX / Блог / Radicale в Docker Compose: готовый файл

Radicale в Docker Compose: готовый файл

MAATRIX

Если вам нужны только календари и контакты с синхронизацией между телефоном, ноутбуком и рабочим столом — Nextcloud для этого явно избыточен: лишний PHP-стек, база данных, веб-интерфейс, который вы никогда не откроете. Radicale решает ту же задачу одним небольшим Python-процессом и файлами на диске. Разворачиваем его в Docker Compose, настраиваем пользователей, права и HTTPS — рабочий сервер календарей за один заход.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что такое Radicale и кому подходит

Radicale — это лёгкий сервер CalDAV и CardDAV: он умеет хранить и отдавать календари (события, задачи) и адресные книги (контакты) по открытым стандартам, которые понимают все нормальные клиенты — Thunderbird, DAVx5 на Android, встроенные календарь и контакты в iOS и macOS, Evolution, Outlook через плагины.

Ключевое отличие от Nextcloud, Baikal или SOGo — минимализм. Radicale написан на Python, хранит данные в обычных .ics/.vcf файлах на файловой системе (либо в SQLite-кэше для скорости), не требует внешней БД и почти не потребляет ресурсов. На сервере с 512 МБ RAM и одним ядром он работает без напряжения — это буквально несколько мегабайт в простое.

Когда Radicale — правильный выбор:

  • Нужны только календари и контакты, без файлового хранилища, заметок, чатов и прочего «облака».
  • Пользователей немного (от одного до пары десятков) — веб-интерфейса администрирования у Radicale нет, всё через конфиг-файлы.
  • Важна прозрачность: данные лежат в читаемых текстовых файлах, их легко бэкапить rsync-ом или смотреть глазами.
  • Сервер маленький — VPS с 1 vCPU / 512 МБ хватает с запасом.

Когда стоит смотреть в сторону Nextcloud: если параллельно нужны файлы, совместное редактирование документов, приложения — тогда один комбайн выгоднее, чем несколько сервисов. Если же речь только про DAV-синхронизацию — Radicale закрывает вопрос быстрее и с меньшим объёмом обслуживания.

Готовый docker-compose.yml

Официального образа от авторов Radicale нет, но есть надёжный, регулярно обновляемый образ tomsquest/docker-radicale, который уже умеет non-root, healthcheck и корректно работает за reverse proxy. На нём и соберём стек.

Структура каталогов на сервере:

mkdir -p /opt/radicale/{config,data}
cd /opt/radicale

/opt/radicale/docker-compose.yml:

services:
  radicale:
    image: tomsquest/docker-radicale:3.5.4.0
    container_name: radicale
    restart: unless-stopped
    ports:
      - "127.0.0.1:5232:5232"
    volumes:
      - ./config:/config
      - ./data:/data
    environment:
      - TAKE_FILE_OWNERSHIP=true
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5232"]
      interval: 30s
      timeout: 5s
      retries: 3

Обратите внимание на порт: 127.0.0.1:5232:5232 — сервис слушает только локально, наружу его отдаёт reverse proxy с HTTPS (настроим ниже). Открывать 5232 порт напрямую в интернет не стоит — CalDAV/CardDAV без TLS означает, что логин и данные календаря идут открытым текстом.

Версию образа лучше зафиксировать явно (как в примере), а не тянуть latest — так апдейты сервиса становятся управляемым событием, а не сюрпризом при следующем docker compose pull.

Поднимаем:

docker compose up -d
docker compose logs -f radicale

При первом запуске контейнер создаст в ./config файл config со значениями по умолчанию — его и будем редактировать.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Настройка пользователей и прав доступа

По умолчанию Radicale работает без аутентификации — это годится только для быстрого локального теста. Включаем проверку по логину/паролю через htpasswd-файл.

Редактируем ./config/config:

[server]
hosts = 0.0.0.0:5232

[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt

[storage]
filesystem_folder = /data/collections

[rights]
type = owner_only

[logging]
level = info

Секция [rights] type = owner_only — важная деталь: каждый пользователь получает доступ только к своим собственным календарям и контактам, чужие коллекции ему не видны. Это разумный дефолт для многопользовательского сервера.

Создаём файл с пользователями. Если htpasswd не установлен на хосте, проще сделать это прямо через контейнер:

docker compose exec radicale htpasswd -B -c /config/users alice
docker compose exec radicale htpasswd -B /config/users bob

Флаг -B даёт bcrypt-хеш (соответствует htpasswd_encryption = bcrypt в конфиге), -c создаёт файл заново — используйте его только для первого пользователя, иначе перезатрёте остальных.

Применяем настройки:

docker compose restart radicale

Проверка, что аутентификация работает:

curl -u alice:пароль http://127.0.0.1:5232/alice/

Должен вернуться пустой список коллекций (207 Multi-Status с XML) — значит сервер принял логин и создал пространство пользователя. Без верного пароля Radicale ответит 401 Unauthorized.

Если пользователей больше десяти и вручную вести htpasswd-файл неудобно, Radicale умеет аутентификацию через LDAP (type = ldap) — но для небольшой команды htpasswd проще и требует меньше движущихся частей.

Domain, reverse proxy и SSL

CalDAV-клиенты, особенно на iOS и Android, требовательны к HTTPS — без валидного сертификата многие откажутся подключаться вовсе. Заведите A-запись домена (например, dav.example.com) на IP сервера и поставьте перед Radicale reverse proxy с автоматическим TLS. Если у вас ещё нет прокси на сервере, пошагово это описано в статье про установку Caddy с авто-SSL на Ubuntu 24.04 — Caddy для одного сервиса вроде Radicale поднимается буквально в три строки конфига.

Если Caddy уже стоит на хосте, добавьте в Caddyfile:

dav.example.com {
    reverse_proxy 127.0.0.1:5232
}

и перезапустите Caddy:

sudo systemctl reload caddy

Сертификат Let's Encrypt Caddy выпустит и продлит сам, без cron-задач с ваших сторон.

Вариант с Nginx + Certbot чуть многословнее, но так же надёжен — если у вас уже есть Nginx с сертификатами для других сайтов на этом же сервере, логичнее добавить туда ещё один server блок, а не заводить второй прокси. Общие грабли с продлением сертификатов и правами на файлы разобраны в статье про Let's Encrypt на сервере.

Важный нюанс для CalDAV: некоторые клиенты (в первую очередь Apple Calendar/Contacts) ищут сервис на корне домена через well-known редиректы /.well-known/caldav и /.well-known/carddav. Radicale обрабатывает их сам, если проксировать весь путь без изменений — дополнительных редиректов в конфиге reverse proxy добавлять не нужно.

Подключение клиентов: Thunderbird, iOS, Android

После настройки TLS адрес пользователя выглядит так: https://dav.example.com/alice/.

Thunderbird (через встроенный календарь или дополнение TbSync):

  1. Календарь → «Создать новый календарь» → «В сети».
  2. Тип: CalDAV, URL: https://dav.example.com/alice/.
  3. Указать логин alice и пароль от htpasswd.

Контакты аналогично добавляются через TbSync с типом CardDAV на тот же базовый URL.

iOS/macOS (Настройки → Календарь → Учётные записи → Добавить учётную запись → Другая):

  • Сервер: dav.example.com
  • Пользователь: alice
  • Пароль: пароль из htpasswd
  • Отдельно для CalDAV и CardDAV — iOS попросит указать оба, если нужны и календарь, и контакты.

Android — встроенных CalDAV/CardDAV клиентов нет, стандартный вариант — приложение DAVx5 (F-Droid или Google Play):

  1. Добавить аккаунт → «Вход по URL и паролю».
  2. Base URL: https://dav.example.com/alice/.
  3. После входа DAVx5 сам обнаружит доступные календари и адресные книги и предложит синхронизировать их со встроенными приложениями Android.

Если клиент не находит коллекции автоматически, можно указать точный путь до конкретного календаря — Radicale создаёт коллекции при первом обращении клиента, поэтому пустой аккаунт иногда нужно один раз «подтолкнуть», создав календарь через сам клиент (Thunderbird и DAVx5 умеют создавать новые календари/книги на сервере).

Бэкап, обновление и мониторинг

Все данные Radicale — это каталог ./data/collections с файлами .ics и .vcf внутри вложенных папок по пользователям. Бэкапить это даже проще, чем базу данных: обычный rsync или tar справляются без остановки сервиса, потому что Radicale не держит долгих блокирующих транзакций на файлах.

Простой ежедневный бэкап через cron:

0 3 * * * tar -czf /backups/radicale-$(date +\%F).tar.gz -C /opt/radicale data config

Добавьте ротацию (например, хранить последние 14 архивов) и выгрузку копии на внешнее хранилище — локальный бэкап на том же диске не спасёт при отказе сервера целиком. Общие принципы и частые ошибки бэкапа Docker-томов разобраны в статье про бэкап Docker volume — они применимы и к каталогам, смонтированным как bind mount, как в нашем случае.

Обновление сервиса — стандартная процедура для образов с фиксированной версией:

docker compose pull
docker compose up -d

Перед обновлением стоит свериться с changelog образа на предмет breaking changes в формате конфига — Radicale исторически меняет структуру [rights] и [auth] между мажорными версиями нечасто, но проверить перед апдейтом дешевле, чем чинить после.

Мониторинг для маленького сервиса вроде Radicale обычно избыточен, но минимальная проверка «жив ли сервер» полезна:

*/5 * * * * curl -sf -u alice:пароль https://dav.example.com/alice/ > /dev/null || echo "Radicale down" | mail -s "Alert" admin@example.com

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли использовать Radicale без Docker, напрямую через pip?

Да, Radicale — обычное Python-приложение и ставится через pip install radicale, это официально поддерживаемый способ. Docker Compose удобнее тем, что изолирует зависимости и упрощает откат к предыдущей версии — но если на сервере уже есть системный Python-стек под управлением, прямая установка тоже разумный вариант.

Radicale поддерживает несколько календарей и адресных книг на одного пользователя?

Да, ограничений на число коллекций нет. Пользователь создаёт их через клиент (Thunderbird, DAVx5, iOS) — Radicale просто отдаёт то, что клиент запросил, и заводит на диске соответствующую папку.

Что будет, если два устройства одновременно отредактируют одно событие?

Radicale поддерживает ETag и условные запросы (If-Match) по стандарту CalDAV — клиент, который отправил изменение вторым при устаревшем ETag, получит конфликт `412 Precondition Failed» и должен будет подтянуть свежую версию. Обработка конфликта на стороне пользователя зависит от клиента; большинство современных приложений покажут предупреждение, а не молча перезапишут данные.

Нужна ли Radicale отдельная база данных?

Нет, это осознанное архитектурное решение авторов — все данные хранятся как файлы .ics/.vcf, а для скорости поиска Radicale ведёт внутренний SQLite-кэш индексов в том же каталоге data. Внешняя СУБД не нужна и не поддерживается.

Как перенести Radicale на другой сервер?

Достаточно скопировать каталоги config и data целиком и поднять тот же docker-compose.yml на новом хосте — формат хранения не привязан к конкретной машине. Общий процесс переноса Docker-проекта на новый сервер, включая тонкости с правами и сетью, описан в статье про перенос Docker-проекта.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →