bolt Valebyte VPS от $4/мес — NVMe, запуск за 60 секунд.

Получить VPS arrow_forward
eco Начальный Туториал

Установка Cal.com на VPS: Docker, PostgreSQL, SSL, SMTP и резервные копии

calendar_month Oct 10, 2026 schedule 20 мин. чтения visibility 51 просмотров
Установка Cal.com на VPS: Docker, PostgreSQL, SSL, SMTP и резервные копии
info

Нужен сервер для этого гайда? Мы предлагаем выделенные серверы и VPS в 50+ странах с мгновенной настройкой.

Нужен сервер для этого гайда?

Разверните VPS или выделенный сервер за минуты.

Установка Cal.com на VPS: Docker, PostgreSQL, SSL, SMTP и резервные копии

TL;DR

В этом руководстве мы развернём Cal.com на собственном VPS с помощью Docker Compose, PostgreSQL и Redis, подключим домен и HTTPS через Caddy, настроим отправку писем по SMTP и автоматические резервные копии базы данных и конфигурации.

  • Используем Ubuntu Server 24.04 LTS, Docker Engine 28.x и Docker Compose v2.
  • Cal.com будет работать в контейнере, а PostgreSQL и Redis — в отдельных контейнерах.
  • Внешний доступ организуем через Caddy с автоматическим сертификатом Let’s Encrypt.
  • Секреты и параметры подключения храним в файле .env, а не в исходном коде.
  • Бэкапы PostgreSQL, Docker-конфигурации и пользовательских данных запускаются по расписанию через cron и Restic.

1. TL;DR

В этом руководстве мы развернём Cal.com на собственном VPS с помощью Docker Compose, PostgreSQL и Redis, подключим домен и HTTPS через Caddy, настроим отправку писем по SMTP и автоматические резервные копии базы данных и конфигурации.

  • Используем Ubuntu Server 24.04 LTS, Docker Engine 28.x и Docker Compose v2.
  • Cal.com будет работать в контейнере, а PostgreSQL и Redis — в отдельных контейнерах.
  • Внешний доступ организуем через Caddy с автоматическим сертификатом Let’s Encrypt.
  • Секреты и параметры подключения храним в файле .env, а не в исходном коде.
  • Бэкапы PostgreSQL, Docker-конфигурации и пользовательских данных запускаются по расписанию через cron и Restic.

2. Содержание

Установка рассчитана на чистый сервер с публичным IPv4-адресом и доменом, например calendar.example.com. Перед началом замените этот домен на собственный во всех командах и конфигурационных файлах.

Команды предназначены для пользователя с правами sudo. Если сервер уже используется для других сайтов, проверьте занятые порты и существующие Docker-сети: Cal.com и Caddy должны иметь изолированную конфигурацию.

3. Что мы настраиваем и зачем

Схема: 3. Что мы настраиваем и зачем
Схема: 3. Что мы настраиваем и зачем

Что такое Cal.com

Cal.com — платформа для планирования встреч и бронирования времени. Пользователь создаёт календарные события, публикует страницу доступности и позволяет другим людям выбрать свободный слот. Система поддерживает рабочие часы, буфер между встречами, разные типы событий, командные календари и интеграции с внешними календарями.

При самостоятельной установке Cal.com приложение, база данных, кэш и обратный прокси находятся под контролем владельца сервера. Это удобно для команды, внутреннего сервиса или SaaS-прототипа, когда требуется собственный домен, независимое хранение данных и возможность менять конфигурацию без ограничений облачного тарифа.

Что будет работать после выполнения инструкции

  • Cal.com будет доступен по адресу вроде https://calendar.example.com.
  • PostgreSQL будет хранить пользователей, события, настройки и интеграции.
  • Redis будет использоваться для очередей и кэширования, если это требуется выбранной версией Cal.com.
  • Caddy будет принимать HTTPS-соединения и проксировать их в контейнер приложения.
  • SMTP-сервер будет отправлять письма подтверждения, приглашения и уведомления.
  • Резервные копии будут передаваться в отдельное хранилище, не расположенное на том же диске.

Self-hosted или облачный Cal.com

Критерий Облачная версия Self-hosted на VPS
Запуск Не требует настройки сервера Нужно самостоятельно настроить ОС, Docker и домен
Контроль данных Данные находятся у оператора облака База и конфигурация находятся на вашем сервере
Обновления Выполняются автоматически или оператором Контролируются администратором
Гибкость Зависит от тарифа и доступных интеграций Можно менять инфраструктуру и сетевую схему
Ответственность Значительная часть задач лежит на провайдере Бэкапы, безопасность и восстановление — ваша зона ответственности

VPS подходит, если вы готовы самостоятельно следить за обновлениями, диском, SMTP и восстановлением из резервной копии. Для нескольких пользователей такая схема обычно проще и дешевле, чем отдельный Kubernetes-кластер. Для критически важного сервиса заранее подготовьте второй сервер или процедуру быстрого восстановления.

Как устроена схема

Публичный трафик поступает на Caddy по портам 80 и 443. Caddy получает сертификат и передаёт запросы контейнеру Cal.com по внутренней Docker-сети. Приложение подключается к PostgreSQL и Redis по именам контейнеров, поэтому базы данных не нужно публиковать в интернет.

Интернет
    |
    | 80/443
    v
Caddy ---- внутренняя сеть ---- Cal.com:3000
                                  |       |
                                  v       v
                            PostgreSQL   Redis

4. Какой VPS-конфиг нужен под эту задачу

Схема: 4. Какой VPS-конфиг нужен под эту задачу
Схема: 4. Какой VPS-конфиг нужен под эту задачу

Минимальная конфигурация

Для небольшого личного календаря или команды до 10–20 человек достаточно двух виртуальных CPU, 4 ГБ RAM и SSD-диска на 40–60 ГБ. Такой сервер подходит для самого приложения, PostgreSQL, Redis и Caddy, но запас памяти будет небольшим во время обновлений и резервного копирования.

Сценарий CPU RAM SSD Сеть
Тестирование и личное использование 2 vCPU 4 ГБ 40 ГБ 100 Мбит/с
Небольшая команда 4 vCPU 8 ГБ 80 ГБ 100–1000 Мбит/с
Несколько организаций или SaaS 8 vCPU 16 ГБ 160 ГБ NVMe 1 Гбит/с

Практичный стартовый вариант — 4 vCPU, 8 ГБ RAM, 80–100 ГБ NVMe, резервная копия диска или отдельное объектное хранилище, а также публичный IPv4. Можно взять VPS с такими характеристиками, если он соответствует требованиям по ОС, сети и резервному копированию.

Диск и резервные копии

Размер базы Cal.com обычно невелик по сравнению с файлами резервных копий и журналами. Тем не менее не рассчитывайте диск впритык. Для сервера с базой на 10 ГБ оставьте минимум 30–50 ГБ свободного пространства. Docker хранит образы, слои и старые контейнеры, а журналы могут расти при ошибках SMTP или внешних интеграций.

Резервные копии лучше хранить не на том же диске. Если злоумышленник удалит сервер или файловая система повредится, локальный архив не поможет. Используйте S3-совместимое объектное хранилище, отдельный VPS по SSH или удалённый сервер с Restic.

Когда нужен dedicated-сервер

Выделенный сервер для одного экземпляра Cal.com обычно избыточен. Он становится оправданным, если на той же машине работают десятки сервисов, требуется гарантированная производительность диска, нужно разместить большой PostgreSQL-кластер или инфраструктура должна быть изолирована от соседних виртуальных машин.

Для обычной установки важнее не физический тип сервера, а быстрый SSD, стабильная сеть, регулярные снимки и понятная процедура восстановления. При росте нагрузки сначала увеличьте VPS вертикально, а затем вынесите PostgreSQL и фоновые задачи на отдельные узлы.

Выбор локации

Локация влияет на задержку запросов и требования к обработке персональных данных. Выбирайте дата-центр ближе к основным пользователям, но учитывайте, где размещены внешние календари, SMTP и объектное хранилище. Для встреч задержка в несколько десятков миллисекунд редко критична, однако быстрый доступ к интерфейсу заметен при работе с большим количеством событий.

5. Подготовка сервера

Ниже используется Ubuntu Server 24.04 LTS с IPv4-адресом. В панели DNS создайте A-запись calendar.example.com, указывающую на IP сервера. Для автоматического выпуска сертификата домен должен быть доступен из интернета, а порты 80 и 443 не должны блокироваться внешним firewall.

Создание администратора

Подключитесь к серверу первоначальным пользователем, обычно root, и создайте отдельную административную учётную запись. Не запускайте рабочие контейнеры из-под root без необходимости.

# Создаём пользователя для администрирования
adduser deploy

# Добавляем его в группу sudo
usermod -aG sudo deploy

# Проверяем наличие пользователя
id deploy

На локальном компьютере сгенерируйте SSH-ключ, если его ещё нет, и установите публичную часть на сервер.

# Выполняется на локальном компьютере
ssh-keygen -t ed25519 -C "deploy@calendar-server"

# Копируем ключ на сервер
ssh-copy-id deploy@SERVER_IP

# Проверяем вход по ключу
ssh deploy@SERVER_IP

Обновление Ubuntu и базовые пакеты

# Обновляем индексы пакетов и устанавливаем исправления
sudo apt update && sudo apt full-upgrade -y

# Устанавливаем инструменты для администрирования и репозиториев
sudo apt install -y ca-certificates curl gnupg git jq unzip \
  htop vim ufw fail2ban unattended-upgrades

# Проверяем версию операционной системы
. /etc/os-release && echo "$PRETTY_NAME"

После обновления проверьте, не требуется ли перезагрузка из-за нового ядра.

# Показываем необходимость перезагрузки
if [ -f /var/run/reboot-required ]; then echo "Reboot required"; fi

# Перезагружаем сервер при необходимости
sudo reboot

Настройка SSH

Перед отключением парольного входа убедитесь, что новый SSH-ключ работает в отдельном окне терминала. Затем создайте отдельный фрагмент конфигурации.

# Запрещаем вход root и вход по паролю
sudo tee /etc/ssh/sshd_config.d/ hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF

# Проверяем синтаксис конфигурации SSH
sudo sshd -t

# Применяем настройки без разрыва существующей сессии
sudo systemctl reload ssh

В первой строке команды выше не должно быть пробела внутри пути. Корректный вариант, если оболочка не принимает запись с пробелом, выглядит так:

sudo tee /etc/ssh/sshd_config.d/hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF

Firewall и fail2ban

Откройте SSH, HTTP и HTTPS. Если SSH работает на нестандартном порту, замените 22/tcp на нужное значение. Docker может обходить некоторые правила UFW для опубликованных портов, поэтому мы не будем публиковать PostgreSQL и Redis наружу.

# Разрешаем необходимые входящие соединения
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Включаем межсетевой экран
sudo ufw --force enable

# Проверяем правила
sudo ufw status verbose

# Включаем защиту SSH от перебора паролей
sudo systemctl enable --now fail2ban
sudo fail2ban-client status

После отключения парольного входа стандартный фильтр fail2ban для SSH остаётся полезным, хотя основной защитой становится авторизация по ключу. Периодически проверяйте журналы:

# Смотрим последние события SSH-защиты
sudo fail2ban-client status sshd

# Проверяем ошибки авторизации
sudo journalctl -u ssh --since "24 hours ago" --no-pager

6. Установка ПО — пошагово

Шаг 1. Установка Docker Engine

В 2026 году используйте актуальную стабильную ветку Docker Engine 28.x или более новую совместимую версию. Пакеты устанавливаются из официального репозитория Docker, а не из случайного скрипта или старого системного пакета.

# Создаём каталог для ключей репозиториев
sudo install -m 0755 -d /etc/apt/keyrings

# Загружаем ключ официального репозитория Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Разрешаем чтение ключа пакетным менеджером
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Добавляем репозиторий для Ubuntu 24.04
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list >/dev/null

# Устанавливаем Docker Engine и Compose v2
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Добавляем deploy в группу Docker
sudo usermod -aG docker "$USER"

# Проверяем версии
docker --version
docker compose version

После добавления в группу Docker выйдите из SSH и подключитесь заново. Членство в группе фактически даёт права root через Docker API, поэтому добавляйте туда только доверенных администраторов.

Шаг 2. Создание каталогов проекта

# Создаём каталог приложения и каталог резервных копий
sudo mkdir -p /opt/calcom/{caddy,data,backups,db-init}

# Передаём каталог проекта административному пользователю
sudo chown -R "$USER":"$USER" /opt/calcom

# Переходим в рабочий каталог
cd /opt/calcom

Шаг 3. Получение официального Docker-шаблона

Структура Docker-файлов Cal.com меняется между релизами. Для production используйте Compose-файл из официального репозитория проекта и фиксируйте конкретный стабильный тег, а не непредсказуемый nightly-образ. Ниже приведён самостоятельный минимальный вариант, который подходит для базовой установки.

# Создаём файл переменных окружения
touch /opt/calcom/.env

# Ограничиваем права доступа к секретам
chmod 600 /opt/calcom/.env

# Переходим в каталог проекта
cd /opt/calcom

Шаг 4. Генерация секретов

Cal.com использует секреты для сессий и шифрования токенов интеграций. Значение CALENDSO_ENCRYPTION_KEY нельзя менять после создания рабочих интеграций без понимания последствий: зашифрованные токены могут стать недоступными.

# Генерируем секрет сессий длиной 64 hex-символа
openssl rand -hex 32

# Генерируем ключ шифрования Cal.com
openssl rand -hex 32

# Генерируем пароль PostgreSQL
openssl rand -base64 32

Скопируйте три результата во временный защищённый файл или сразу в .env. Не отправляйте этот файл в Git, мессенджеры и системы мониторинга.

Шаг 5. Подготовка Docker Compose

В примере используется образ Cal.com из официального реестра проекта, PostgreSQL 16 и Redis 7. В 2026 году перед обновлением проверьте рекомендуемый Cal.com тег и совместимость переменных окружения в документации соответствующего релиза.

# Создаём Compose-файл
cat > /opt/calcom/compose.yml <<'EOF'
services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - internal
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - internal
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10

  calcom:
    image: ${CALCOM_IMAGE}
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      REDIS_URL: redis://redis:6379
      NODE_ENV: production
      PORT: 3000
      HOSTNAME: 0.0.0.0
    expose:
      - "3000"
    networks:
      - internal
      - proxy

networks:
  internal:
    internal: true
  proxy:

volumes:
  postgres_data:
  redis_data:
EOF

# Проверяем синтаксис и подстановку переменных
docker compose config

Шаг 6. Первый запуск базы и приложения

# Загружаем образы PostgreSQL, Redis и Cal.com
docker compose pull

# Запускаем контейнеры в фоне
docker compose up -d

# Проверяем состояние всех сервисов
docker compose ps

# Смотрим последние логи приложения
docker compose logs --tail=100 calcom

При первом запуске Cal.com может выполнить миграции базы данных. Не перезапускайте контейнер несколько раз подряд, пока не проверите журналы. Если образ требует отдельной команды миграции, выполните её согласно документации конкретного релиза, например через docker compose exec calcom.

Шаг 7. Установка Caddy

Caddy будет установлен на хосте, а не в контейнере. Это упрощает получение сертификатов и позволяет проксировать несколько Docker-проектов через один reverse proxy.

# Устанавливаем Caddy из официального репозитория
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update
sudo apt install -y caddy

# Проверяем установленную версию
caddy version

Шаг 8. Создание администратора Cal.com

Откройте домен в браузере после настройки Caddy из следующего раздела. Первый зарегистрированный пользователь обычно становится владельцем инсталляции. Используйте рабочий адрес электронной почты, поскольку без SMTP часть функций восстановления и приглашений работать не будет.

7. Конфигурация

Схема: 7. Конфигурация
Схема: 7. Конфигурация

Файл переменных окружения

Откройте файл /opt/calcom/.env и заполните его. Значение CALCOM_IMAGE замените на конкретный стабильный тег, опубликованный для вашей версии Cal.com. Не используйте latest в критической production-среде: плавающий тег может неожиданно изменить схему базы или набор переменных.

sudo -u "$USER" tee /opt/calcom/.env >/dev/null <<'EOF'
# Версия образа Cal.com. Зафиксируйте стабильный тег текущего релиза.
CALCOM_IMAGE=calcom/cal.com:v5

# Основные параметры PostgreSQL
POSTGRES_DB=calcom
POSTGRES_USER=calcom
POSTGRES_PASSWORD=REPLACE_WITH_LONG_RANDOM_PASSWORD

# URL приложения
NEXTAUTH_URL=https://calendar.example.com
NEXT_PUBLIC_WEBAPP_URL=https://calendar.example.com

# Секрет сессий и ключ шифрования интеграций
NEXTAUTH_SECRET=REPLACE_WITH_64_HEX_CHARACTERS
CALENDSO_ENCRYPTION_KEY=REPLACE_WITH_64_HEX_CHARACTERS

# Настройки электронной почты
[email protected]
EMAIL_SERVER_HOST=smtp.example.net
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=smtp-user
EMAIL_SERVER_PASSWORD=REPLACE_WITH_SMTP_PASSWORD
EMAIL_SERVER_SECURE=false

# Параметры окружения
NODE_ENV=production
NEXT_PUBLIC_LICENSE_CONSENT=agree
EOF

# Закрываем файл от чтения другими пользователями
chmod 600 /opt/calcom/.env

# Проверяем, что Docker видит обязательные переменные
docker compose config --environment

В некоторых релизах имена SMTP-переменных или обязательных настроек могут отличаться. Сверьте их с примером .env.example из того же тега Cal.com. Это особенно важно после перехода между крупными версиями.

Настройка Caddy и HTTPS

Создайте Caddyfile. Caddy автоматически запросит сертификат Let’s Encrypt, если DNS уже указывает на сервер, порты 80 и 443 открыты, а домен не закрыт дополнительной авторизацией.

# Создаём конфигурацию reverse proxy
sudo tee /etc/caddy/Caddyfile >/dev/null <<'EOF'
calendar.example.com {
    encode gzip zstd

    reverse_proxy 127.0.0.1:3000 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }

    log {
        output file /var/log/caddy/calcom-access.log
        format json
    }
}
EOF

# Создаём каталог для журнала Caddy
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy

# Проверяем синтаксис Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile

# Запускаем и добавляем Caddy в автозагрузку
sudo systemctl enable --now caddy

# Перечитываем конфигурацию без остановки сервиса
sudo systemctl reload caddy

В текущем Compose-файле контейнер Cal.com не публикует порт 3000 на хост. Чтобы Caddy на хосте мог обратиться к приложению, добавьте публикацию порта только на loopback-интерфейс.

# Показываем фрагмент текущего Compose-файла
grep -A5 -B3 "expose" /opt/calcom/compose.yml

Замените блок expose внутри сервиса calcom на следующий:

ports:
  - "127.0.0.1:3000:3000"

После изменения перезапустите контейнер:

# Пересоздаём только контейнер приложения с новым портом
cd /opt/calcom
docker compose up -d calcom

# Проверяем локальный ответ приложения
curl -I http://127.0.0.1:3000

# Проверяем статус Caddy
sudo systemctl status caddy --no-pager

# Проверяем HTTPS с сервера
curl -I https://calendar.example.com

Проверка базы и Redis

# Проверяем готовность PostgreSQL
docker compose exec db pg_isready -U calcom -d calcom

# Проверяем Redis
docker compose exec redis redis-cli ping

# Проверяем состояние контейнеров
docker compose ps

# Просматриваем ошибки приложения за последние минуты
docker compose logs --since=10m calcom | grep -iE "error|warn|migration"

SMTP

Для стандартного SMTP-порта 587 обычно используется STARTTLS: EMAIL_SERVER_SECURE=false. Для SMTPS на порту 465 чаще требуется EMAIL_SERVER_SECURE=true. Не путайте шифрование SMTP с HTTPS: сертификат сайта не отвечает за доставку писем.

После изменения SMTP-переменных пересоздайте контейнер, чтобы он получил новые значения:

# Пересоздаём приложение с обновлёнными SMTP-настройками
cd /opt/calcom
docker compose up -d --force-recreate calcom

# Проверяем журнал отправки и ошибок SMTP
docker compose logs --since=15m calcom | grep -iE "smtp|email|mail|error"

Для production используйте SMTP-провайдера с отдельными учётными данными приложения и ограничением частоты отправки. Не запускайте собственный почтовый сервер на том же VPS без необходимости: для него потребуются SPF, DKIM, DMARC, обратная DNS-запись, мониторинг очереди и контроль репутации IP.

Проверка автоматического запуска

# Проверяем, что контейнеры автоматически перезапускаются
docker inspect -f '{{.Name}}: {{.HostConfig.RestartPolicy.Name}}' \
  $(docker compose ps -q)

# Имитируем перезапуск Docker
sudo systemctl restart docker

# Убеждаемся, что сервисы вернулись
sleep 15
cd /opt/calcom
docker compose ps

8. Бэкапы и обслуживание

Схема: 8. Бэкапы и обслуживание
Схема: 8. Бэкапы и обслуживание

Что необходимо сохранять

  • Дамп PostgreSQL: пользователи, события, настройки, команды и интеграции.
  • Файл .env: без него нельзя восстановить некоторые подключения и секреты.
  • compose.yml и конфигурацию Caddy.
  • Docker volumes, если выбранная версия Cal.com хранит загруженные файлы или дополнительные данные.
  • Ключи шифрования и сведения о процедуре восстановления.

База данных является главным источником состояния Cal.com. Простого копирования контейнера недостаточно: контейнеры можно пересоздать из образа, а данные PostgreSQL нужно экспортировать согласованным способом через pg_dump.

Установка Restic

Restic шифрует файлы до отправки в удалённое хранилище. В примере используется S3-совместимое хранилище. Получите отдельный bucket и ключ с минимальными правами: приложению резервного копирования не нужны права на весь аккаунт.

# Устанавливаем Restic из пакетов Ubuntu
sudo apt update
sudo apt install -y restic

# Проверяем версию
restic version

# Создаём каталог для временных дампов
sudo mkdir -p /opt/calcom/backup-work
sudo chown -R "$USER":"$USER" /opt/calcom/backup-work

Скрипт резервного копирования

Создайте файл /usr/local/sbin/calcom-backup. Пароль репозитория Restic и параметры S3 лучше хранить в отдельном файле с правами 600, а не прописывать прямо в скрипте.

# Создаём файл секретов Restic
sudo tee /root/.config/calcom-restic.env >/dev/null <<'EOF'
export RESTIC_REPOSITORY=s3:https://s3.example.net/calcom-backups
export AWS_ACCESS_KEY_ID=REPLACE_WITH_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=REPLACE_WITH_SECRET_KEY
export RESTIC_PASSWORD=REPLACE_WITH_LONG_REPOSITORY_PASSWORD
EOF

# Ограничиваем доступ к секретам
sudo chmod 600 /root/.config/calcom-restic.env

# Создаём скрипт резервного копирования
sudo tee /usr/local/sbin/calcom-backup >/dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail

PROJECT=/opt/calcom
WORK="$PROJECT/backup-work"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
DUMP="$WORK/calcom-$STAMP.sql.gz"

source /root/.config/calcom-restic.env
mkdir -p "$WORK"

# Создаём согласованный сжатый дамп PostgreSQL
cd "$PROJECT"
docker compose exec -T db \
  pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --no-owner --no-acl \
  | gzip -9 > "$DUMP"

# Инициализируем репозиторий при первом запуске
restic snapshots >/dev/null 2>&1 || restic init

# Сохраняем дамп, конфигурацию и Compose-файл
restic backup "$DUMP" "$PROJECT/.env" "$PROJECT/compose.yml" \
  /etc/caddy/Caddyfile

# Удаляем локальные дампы старше двух дней
find "$WORK" -type f -name '.sql.gz' -mtime +2 -delete

# Удаляем старые удалённые снимки по политике хранения
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF

# Делаем скрипт исполняемым
sudo chmod 750 /usr/local/sbin/calcom-backup

Имя базы и пользователя берутся из переменных контейнера. Если в скрипте запускается pg_dump с пустыми переменными, передайте их явно или загрузите значения из защищённого файла. Для данного Compose можно заменить соответствующую часть на:

docker compose exec -T db \
  pg_dump -U calcom -d calcom --no-owner --no-acl \
  | gzip -9 > "$DUMP"

Первый запуск и проверка архива

# Запускаем резервное копирование вручную
sudo /usr/local/sbin/calcom-backup

# Проверяем список снимков
sudo bash -c 'source /root/.config/calcom-restic.env && restic snapshots'

# Проверяем целостность последних данных
sudo bash -c 'source /root/.config/calcom-restic.env && restic check'

Планировщик cron

Запускайте бэкап ночью, когда вероятность активного изменения данных ниже. PostgreSQL-дамп остаётся согласованным и при работающем приложении, поэтому останавливать Cal.com каждый день не требуется.

# Создаём ежедневное задание в cron
sudo tee /etc/cron.d/calcom-backup >/dev/null <<'EOF'
17 03    root /usr/local/sbin/calcom-backup >> /var/log/calcom-backup.log 2>&1
EOF

# Проверяем права и содержимое задания
sudo chmod 644 /etc/cron.d/calcom-backup
cat /etc/cron.d/calcom-backup

Периодически выполняйте тестовое восстановление в отдельном временном окружении. Бэкап, который ни разу не восстанавливали, является только предположением о наличии данных.

Обновление Cal.com

Не обновляйте production автоматически до плавающего тега. Сначала прочитайте release notes, проверьте требования к Node.js, PostgreSQL и переменным окружения, затем сделайте полный бэкап.

# Сохраняем текущий список образов
cd /opt/calcom
docker compose images

# Делаем резервную копию перед обновлением
sudo /usr/local/sbin/calcom-backup

# Меняем CALCOM_IMAGE на новый проверенный тег
sudo vim /opt/calcom/.env

# Загружаем новый образ и пересоздаём приложение
docker compose pull calcom
docker compose up -d calcom

# Наблюдаем за миграциями и ошибками
docker compose logs -f --tail=200 calcom

Для небольшой команды используйте короткое maintenance window и заранее сообщите пользователям о возможной недоступности. Rolling-обновление с двумя экземплярами приложения возможно, но требует отдельного балансировщика, проверки совместимости миграций и контроля фоновых задач. Для одного VPS безопаснее последовательное обновление с готовым откатом.

Мониторинг ресурсов

# Показываем загрузку контейнеров
docker stats --no-stream

# Проверяем заполнение диска
df -h

# Проверяем размер Docker-данных
sudo du -sh /var/lib/docker

# Смотрим использование памяти
free -h

# Проверяем ошибки ядра и диска
sudo journalctl -p warning..alert --since "24 hours ago" --no-pager

Настройте уведомление при заполнении диска выше 80–85 процентов. Полный диск PostgreSQL может привести не только к остановке записи, но и к повреждению рабочих процессов, если система не сможет создать временные файлы.

9. Troubleshooting и FAQ

Cal.com отвечает ошибкой 502 Bad Gateway. Что проверить?

Сначала проверьте docker compose ps и журналы командой docker compose logs --tail=200 calcom. Затем убедитесь, что порт 3000 опубликован на 127.0.0.1 и приложение действительно слушает его: curl -I http://127.0.0.1:3000. Если контейнер постоянно перезапускается, проверьте обязательные переменные в .env, состояние PostgreSQL и результат миграций. В Caddy журнал доступен через journalctl -u caddy.

Сертификат HTTPS не выпускается. Почему?

Проверьте, что A-запись домена указывает на правильный IPv4, а AAAA-запись не ведёт на недоступный IPv6-адрес. Порты 80 и 443 должны быть открыты в UFW, панели провайдера и внешнем firewall. Убедитесь, что другой веб-сервер не занимает эти порты: sudo ss -ltnp | grep -E ':80|:443'. После исправления DNS посмотрите журнал sudo journalctl -u caddy -n 100 и повторите reload Caddy.

Письма не отправляются, хотя сайт открывается. Что делать?

Проверьте SMTP-хост, порт, логин, пароль и режим TLS. Для порта 587 обычно нужен STARTTLS и значение EMAIL_SERVER_SECURE=false, для 465 — настройки SMTPS, рекомендованные вашим провайдером. Посмотрите логи Cal.com после пересоздания контейнера. Также проверьте, не блокирует ли провайдер исходящие SMTP-соединения и разрешён ли адрес отправителя. SPF, DKIM и DMARC влияют на доставляемость, но не исправляют неверную авторизацию SMTP.

Какая ошибка означает, что Cal.com не подключается к PostgreSQL?

Сообщения вроде ECONNREFUSED, password authentication failed или database does not exist указывают на проблему с подключением или переменными. Внутри Compose хостом должен быть сервис db, а не localhost. Проверьте docker compose exec db pg_isready -U calcom -d calcom, затем сравните имя базы, пользователя и пароль в Compose и .env. После изменения пароля существующий volume PostgreSQL не переинициализируется автоматически.

Какой VPS-конфиг минимально подойдёт?

Для тестовой или личной установки достаточно 2 vCPU, 4 ГБ RAM и 40–60 ГБ SSD. Для небольшой команды лучше взять 4 vCPU, 8 ГБ RAM и не менее 80 ГБ NVMe, особенно если на сервере будут мониторинг, резервные копии или дополнительные сервисы. Нужны Ubuntu 24.04 LTS, публичный IPv4, открытые порты 80 и 443 и возможность выполнять исходящие HTTPS и SMTP-соединения. Резервные копии храните отдельно от VPS.

Что выбрать — VPS или dedicated для этой задачи?

Для одного Cal.com или небольшой команды выбирайте VPS: ресурсов достаточно, а масштабирование выполняется изменением тарифа. Dedicated оправдан, если сервер одновременно обслуживает много проектов, требуется гарантированная дисковая производительность или есть требования к физической изоляции. Сам по себе dedicated не решает вопросы безопасности и бэкапов. В обоих вариантах нужны обновления, мониторинг, отдельное хранилище резервных копий и план восстановления.

После обновления пропали интеграции календаря. Как восстановить работу?

Проверьте, не менялся ли CALENDSO_ENCRYPTION_KEY. Если ключ отличается от того, который использовался при создании интеграций, Cal.com может не расшифровать сохранённые токены. Верните прежнее значение из защищённого хранилища и перезапустите контейнер. Если ключ утрачен, восстановите базу вместе с исходным ключом из совместимого бэкапа. Не удаляйте PostgreSQL volume до завершения диагностики и обязательно сохраните текущие логи.

Диск быстро заполняется. Какие действия безопасны?

Проверьте df -h, docker system df, размер каталогов Docker и журналов Caddy. Удаляйте только неиспользуемые образы после проверки, что нужный релиз уже запущен: docker image prune не удаляет работающие образы, но всё равно выполняйте его осознанно. Настройте log rotation для Docker и ограничьте срок хранения локальных дампов. Не удаляйте вручную PostgreSQL volume и каталог /var/lib/docker/volumes.

10. Выводы и следующие шаги

Схема: 10. Выводы и следующие шаги
Схема: 10. Выводы и следующие шаги

В результате Cal.com работает в Docker на VPS, данные хранятся в PostgreSQL, запросы защищены HTTPS через Caddy, а уведомления отправляются через внешний SMTP-сервис. Отдельный Restic-бэкап сохраняет базу, конфигурацию и секреты в зашифрованном удалённом репозитории.

Следующим шагом настройте мониторинг доступности, диска, памяти и срока действия сертификата. При росте нагрузки вынесите PostgreSQL на отдельный узел, добавьте второй экземпляр приложения и протестируйте процедуру восстановления в чистой среде. Перед каждым крупным обновлением фиксируйте текущий тег образа, выполняйте бэкап и проверяйте миграции на staging-сервере.

Был ли этот гайд полезен?

Ваш отзыв помогает нам улучшать гайды.

Поделиться записью:

Отправьте гайд тому, кому он может пригодиться.

Telegram VKVK WhatsApp Facebook LinkedIn XX

установка cal.com на vps: docker, postgresql, ssl, smtp и резервные копии
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.