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 50 переглядів
Установка 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. Що ми налаштовуємо і навіщо

Що таке 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 потрібна для цього завдання

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

Для невеликого особистого календаря або команди до 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. Конфігурація

Файл змінних середовища

Відкрийте файл /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. Бекапи та обслуговування

Що необхідно зберігати

  • Дамп 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. Висновки та наступні кроки

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

Наступним кроком налаштуйте моніторинг доступності, диска, пам'яті та строку дії сертифіката. У разі зростання навантаження винесіть PostgreSQL на окремий вузол, додайте другий екземпляр застосунку та протестуйте процедуру відновлення в чистому середовищі. Перед кожним великим оновленням фіксуйте поточний тег образу, виконуйте бекап і перевіряйте міграції на staging-сервері.

Чи був цей гайд корисним?

Ваш відгук допомагає нам покращувати гайди.

Share this post:

Надішліть гайд тому, кому він може стати в пригоді.

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.