MAATRIX / Блог / Cal.com в Docker Compose: готовый файл

Cal.com в Docker Compose: готовый файл

MAATRIX

Если вы устали платить за Calendly подписку и отдавать данные о встречах с клиентами чужому сервису — логичный шаг переехать на Cal.com, открытый планировщик встреч с тем же набором функций: публичные страницы бронирования, синхронизация с календарями, видеоконференции, вебхуки. Ниже — рабочий docker-compose.yml, который поднимает Cal.com на своём VPS от первого запуска до HTTPS-домена, плюс нюансы, о которые реально спотыкаются на self-hosting.

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

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

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

Что нужно подготовить перед стартом

Cal.com — это Next.js-приложение с PostgreSQL в качестве базы данных. Официально готового образа на Docker Hub команда не публикует: часть переменных окружения (например, публичный URL приложения) в Next.js «запекается» в сборку на этапе build, а не читается в рантайме, поэтому единственный надёжный способ — собирать образ самостоятельно из исходников по их Dockerfile. Это не страшно, но важно понимать заранее, иначе первое недоумение будет именно тут.

Что понадобится:

  • VPS с минимум 2 vCPU и 4 ГБ RAM — сборка Next.js-приложения (шаг pnpm build) сама по себе прожорлива по памяти, на 2 ГБ сборка может падать по OOM даже если рантайм потом ужимается;
  • Docker и Docker Compose plugin;
  • домен (или поддомен), который вы уже готовы направить на сервер — Cal.com жёстко завязан на NEXT_PUBLIC_WEBAPP_URL, менять его «на лету» без пересборки образа не получится;
  • почтовый сервер (SMTP) для отправки приглашений на встречи и уведомлений — это может быть как внешний сервис, так и свой почтовый сервер на этом же VPS;
  • git на сервере, чтобы склонировать репозиторий github.com/calcom/cal.com — из него мы будем собирать образ, а не тянуть готовый.

Если вы ещё не разворачивали Docker и домен на сервере с нуля — сначала настройте DNS-записи и убедитесь, что домен резолвится на IP сервера, и только потом переходите к сборке.

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

Клонируйте репозиторий и положите файл docker-compose.yml рядом с исходниками (или в подпапке deploy/, указав правильный context):

git clone https://github.com/calcom/cal.com.git
cd cal.com

Сам файл:

version: "3.9"

services:
  database:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: calcom
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: calcom
    volumes:
      - calcom_db:/var/lib/postgresql/data
    networks:
      - calcom_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U calcom"]
      interval: 10s
      timeout: 5s
      retries: 5

  calcom:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_WEBAPP_URL: https://cal.ваш-домен.ru
        DATABASE_URL: postgresql://calcom:${POSTGRES_PASSWORD}@database:5432/calcom
    image: cal-selfhosted:latest
    restart: unless-stopped
    depends_on:
      database:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgresql://calcom:${POSTGRES_PASSWORD}@database:5432/calcom
      - NEXTAUTH_URL=https://cal.ваш-домен.ru
      - NEXT_PUBLIC_WEBAPP_URL=https://cal.ваш-домен.ru
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - CALENDSO_ENCRYPTION_KEY=${CALENDSO_ENCRYPTION_KEY}
      - EMAIL_FROM=${EMAIL_FROM}
      - EMAIL_SERVER_HOST=${SMTP_HOST}
      - EMAIL_SERVER_PORT=${SMTP_PORT}
      - EMAIL_SERVER_USER=${SMTP_USER}
      - EMAIL_SERVER_PASSWORD=${SMTP_PASSWORD}
    ports:
      - "127.0.0.1:3000:3000"
    networks:
      - calcom_net

volumes:
  calcom_db:

networks:
  calcom_net:

Ключевой момент: NEXT_PUBLIC_WEBAPP_URL и DATABASE_URL продублированы и в build.args, и в environment — первое нужно, чтобы Next.js правильно собрал клиентский бандл, второе — чтобы Prisma подключалась к базе уже в рантайме. Порт наружу не публикуйте напрямую (127.0.0.1:3000:3000, а не 0.0.0.0) — перед приложением встанет reverse-proxy с HTTPS.

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

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

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

Переменные окружения и секреты

Создайте .env рядом с docker-compose.yml:

POSTGRES_PASSWORD=$(openssl rand -hex 24)
NEXTAUTH_SECRET=$(openssl rand -base64 32)
CALENDSO_ENCRYPTION_KEY=$(openssl rand -base64 24)
EMAIL_FROM=noreply@ваш-домен.ru
SMTP_HOST=smtp.ваш-домен.ru
SMTP_PORT=587
SMTP_USER=noreply@ваш-домен.ru
SMTP_PASSWORD=пароль_от_smtp

Проще всего сгенерировать значения и сразу записать их в файл одной командой:

{
  echo "POSTGRES_PASSWORD=$(openssl rand -hex 24)"
  echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)"
  echo "CALENDSO_ENCRYPTION_KEY=$(openssl rand -base64 24)"
} >> .env
chmod 600 .env

NEXTAUTH_SECRET и CALENDSO_ENCRYPTION_KEY — это не «для галочки»: первый подписывает сессии, второй шифрует чувствительные данные в базе (токены календарей, ключи интеграций). Потеряете их после первого запуска — пользователи разлогинятся, а расшифровать старые токены интеграций не получится, придётся переподключать календари заново. Храните .env вне git и делайте его частью бэкапа сервера.

Если приглашения на встречи и уведомления должны уходить с собственного почтового домена, а не через сторонний SMTP-сервис — пригодится статья про настройку своего почтового сервера, там разобраны типичные причины, почему письма не доходят или падают в спам.

Reverse-proxy и HTTPS

Cal.com слушает 3000 порт внутри контейнера и не занимается TLS сам — это задача reverse-proxy перед ним. Минимальный конфиг nginx:

server {
    listen 80;
    server_name cal.ваш-домен.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name cal.ваш-домен.ru;

    ssl_certificate     /etc/letsencrypt/live/cal.ваш-домен.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cal.ваш-домен.ru/privkey.pem;

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
    }
}

Подробный разбор, как правильно поднять такую связку и что обычно ломается (504 на долгих запросах, вебсокеты, кеш заголовков), — в статье nginx как reverse proxy на VPS. Сертификат выпускается стандартно через certbot — если ещё не настраивали Let's Encrypt на этом сервере, шаги есть в статье Let's Encrypt SSL на VPS. Если предпочитаете Caddy вместо nginx — он получает сертификат автоматически, конфиг из трёх строк, но с чуть меньшей гибкостью в тонкой настройке заголовков.

Первый запуск и создание аккаунта

Собираем и поднимаем стек:

docker compose build
docker compose up -d
docker compose logs -f calcom

Сборка образа — самый долгий шаг, на слабом VPS может занять 10-15 минут (ориентир, у вас будет зависеть от CPU и диска), не пугайтесь, если в логах долго нет вывода. После старта Prisma-миграции применяются автоматически при первом запуске контейнера. Если нужно прогнать их вручную (например, после ручного апдейта схемы):

docker compose exec calcom npx prisma migrate deploy

Дальше открываете https://cal.ваш-домен.ru — Cal.com показывает мастер первичной настройки, где создаётся аккаунт администратора: email, пароль, название организации. Дальше из интерфейса подключаются календари (Google, Outlook, CalDAV), настраиваются типы событий и рабочие часы. Проверьте отдельно, что письмо-приглашение реально доходит — часть проблем с SMTP вылезает только на этом шаге, а не на этапе старта контейнера.

Обновление и бэкап

Cal.com обновляется часто — новые версии выходят с заметной регулярностью, и в них закрывают как баги, так и уязвимости. Процесс обновления с собственной сборкой такой:

cd cal.com
git fetch --tags
git checkout v-нужная-версия   # смотрите Releases в репозитории
docker compose build calcom
docker compose up -d calcom

Перед апдейтом обязательно бэкапьте базу — миграции Prisma необратимы без отдельного плана отката:

docker compose exec database pg_dump -U calcom calcom | gzip > calcom_$(date +%F).sql.gz

Восстановление — тем же путём в обратную сторону:

gunzip -c calcom_2026-08-20.sql.gz | docker compose exec -T database psql -U calcom calcom

Volume базы (calcom_db) стоит бэкапить и целиком на случай проблем с самим Docker, а не только логическим дампом. Не забывайте и про .env — без него зашифрованные в базе данные интеграций восстановить не получится, даже если сам дамп базы на месте.

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

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

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

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

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

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

Можно ли просто скачать готовый образ Cal.com с Docker Hub?

Официального образа с полным набором функций команда не публикует именно из-за переменных, которые запекаются в сборку на этапе build (NEXT_PUBLIC_*). Собирать из исходников по их Dockerfile — рекомендованный путь для self-hosting.

Хватит ли 2 ГБ RAM на VPS?

Для рантайма — на грани, для самой сборки (docker compose build) обычно нет: процесс сборки Next.js прожорлив по памяти и может падать по OOM. Если сервер маленький, соберите образ на более мощной машине и перенесите его, либо временно добавьте swap на время сборки.

Нужен ли CALCOM_LICENSE_KEY?

Нет, для базового self-hosting он не требуется — часть функций уровня Enterprise (SSO, брендирование под организацию и т.д.) в открытой версии просто недоступна без лицензии, но планировщик встреч полностью рабочий и без неё.

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

Логика та же, что и для любого Docker-проекта: дамп базы, копирование .env, повторная сборка образа на новом сервере — общий алгоритм переноса разобран в статье перенос Docker-проекта на другой сервер.

Почему после смены домена приложение открывает старые ссылки?

Потому что NEXT_PUBLIC_WEBAPP_URL зашит в сборку — после смены домена нужно пересобрать образ (docker compose build calcom) с новым значением, простой правки .env недостаточно.

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

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

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