SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

Альтернативи Calendly на власному VPS: порівняння

Порівняйте Cal.com, Easy!Appointments, Rallly і DayOtter на власному VPS: двостороння синхронізація календаря та вихідна пошта, без прихованих обмежень.

Коротка відповідь

Self-hosted альтернатива Calendly має робити те, чого внутрішні інструменти на вашому VPS ніколи не роблять: бути доступною для зовнішніх користувачів. Сторінка бронювання і є продуктом. Вона від першого дня потребує справжнього доменного імені та TLS (transport layer security), а також має надсилати пошту людям, які ніколи не чули про ваш сервер.

Реалістичний вибір охоплюють чотири проєкти. Cal.com найближчий до Calendly і є типовим вибором для консультанта, який працює самостійно. Easy!Appointments — легкий варіант на PHP і MySQL, який добре працює на VPS з 1 GB пам’яті. Rallly — інструмент для групових опитувань і взагалі не має сторінки бронювання. DayOtter — найновіший учасник, платформа планування за AGPLv3 із помічником попереднього підтвердження перед нею.

Який варіант ви справді зможете запустити, визначають два запитання. Чи синхронізується він в обох напрямках із календарем, яким ви вже користуєтеся? І чи може він надсилати пошту? Саме на другому запитанні більшість self-hosted систем бронювання непомітно зазнає невдачі, тому розглянемо його першим.

Вихідна електронна пошта — це та частина, яка не працює

Підтвердження бронювання потрапляє до поштової скриньки сторонньої людини. Це транзакційний лист, який має доставлятися до Gmail або Microsoft 365. Такі одержувачі оцінюють вас за IP-адресою відправника та DNS-записами.

Надсилання безпосередньо з VPS майже ніколи не працює. Більшість провайдерів блокують вихідний TCP-порт 25 для нових облікових записів. Через це з’єднання зависає, а потім завершується за тайм-аутом. Навіть якщо порт 25 відкритий, нова адреса VPS не має історії надсилання. Великі поштові провайдери вважають адреси з невідомих діапазонів хостинг-провайдерів підозрілими. Бронювання записується до бази даних, сторінка повідомляє про підтвердження, але ніхто не отримує листа. На стороні сервера нічого не виглядає зламаним. Тому зазвичай проблему виявляє клієнт через кілька тижнів, коли не приходить на бронювання.

Використовуйте relay. Підійде будь-який провайдер транзакційної пошти. Застосунку потрібні лише hostname, порт, ім’я користувача та пароль. Перш ніж змінювати конфігурацію застосунку, перевірте доступність порту:

nc -vz -w 5 "$SMTP_HOST" 587

Рядок succeeded означає, що мережевий шлях відкритий. Зависання або Connection refused означає, що порт заблокований на мережевому рівні. Жодне редагування .env цього не виправить. Relay-сервіси використовують порти 587 або 465 саме тому, що порт 25 так часто заблокований.

Кожен проєкт підключає relay по-своєму. Cal.com читає EMAIL_FROM, EMAIL_SERVER_HOST, EMAIL_SERVER_PORT, EMAIL_SERVER_USER і EMAIL_SERVER_PASSWORD, а також може використовувати RESEND_API_KEY. Перевірте це уважно: штатний .env.example спрямовує EMAIL_SERVER_HOST до localhost на порт 1025. Це локальна поштова скринька для розробки. Якщо залишити значення за замовчуванням, застосунок надсилатиме листи в нікуди без повідомлення про помилку. Rallly використовує SMTP_HOST, SMTP_PORT, SMTP_USER і SMTP_PWD. DayOtter використовує налаштування SMTP або ключ Resend. Easy!Appointments надсилає сповіщення безпосередньо із застосунку. Тому на сторінці його налаштувань укажіть той самий relay, перш ніж приймати реальне бронювання.

Потім опублікуйте DNS-записи, які надасть relay. Запис SPF (sender policy framework) визначає, яким серверам дозволено надсилати пошту від імені вашого домену. Ключ DKIM (domainkeys identified mail) підписує кожне повідомлення, щоб одержувач міг перевірити, що його не було змінено. Додайте політику DMARC (domain-based message authentication, reporting and conformance), коли обидві перевірки проходитимуть успішно. Надішліть тестове бронювання на реальну адресу у великого провайдера, відкрийте заголовки повідомлення та переконайтеся, що рядки автентифікації мають значення pass. Сторінка бронювання, яка не може надсилати листи, гірша за відсутність сторінки бронювання, оскільки помилка залишається непомітною.

Які календарні бекенди справді синхронізуються в обох напрямках

Синхронізація має два напрямки, і кожен із них може відмовити окремо. Напрямок читання відповідає за доступність: застосунок має бачити наявні зайняті проміжки, інакше запропонує час, на який у вас уже запланована подія. Напрямок запису відповідає за саме бронювання: підтверджена подія має з’явитися в календарі, яким ви фактично користуєтеся, а не лише всередині інструмента бронювання.

Google Calendar і Microsoft 365 підтримують обидва напрямки, але для self-hosted інсталяції є одна умова. OAuth (open authorization) client потрібно створити самостійно, оскільки client ID хостингового продукту не міститься у вихідному коді. Для Cal.com це GOOGLE_API_CREDENTIALS у .env, де зберігається JSON, завантажений із Google Cloud console. DayOtter так само використовує облікові дані Google і Microsoft OAuth.

Тут виникають дві проблеми, про які варто знати до початку налаштування. По-перше, зареєстрований redirect URI має точно відповідати публічній URL-адресі, включно зі схемою та кінцевим шляхом, якщо він є. Інакше Google припиняє підключення з помилкою redirect_uri_mismatch на екрані згоди. По-друге, Google project, для якого статус публікації залишено Testing, видає refresh tokens, що спливають через сім днів. Синхронізація працює весь тиждень, а потім припиняється; під час наступного оновлення журнал застосунку містить invalid_grant. Переведіть consent screen у стан In production або погодьтеся вручну перепідключати його щопонеділка.

CalDAV (calendaring extensions to WebDAV) — це відкритий варіант, але його підтримка менш повна. Cal.com постачається із застосунком CalDAV, який досі має статус beta; його перевірено із серверами, зокрема Baikal, Radicale, Nextcloud і Kerio Connect. Apple iCloud працює через той самий застосунок, але потребує app-specific password, а не пароля Apple ID. DayOtter вказує Apple через CalDAV поряд із Google і Microsoft 365.

ICS feed — це не синхронізація. URL-адреса підписки .ics за задумом доступна лише для читання. Вона може блокувати час на сторінці бронювання, але ніколи не зможе отримати заброньовану подію. Якщо інструмент пропонує для вашого календаря лише ICS, у вас є лише частина інтеграції, і події все одно доведеться копіювати вручну.

Easy!Appointments синхронізується лише з Google Calendar. Rallly взагалі не читає доступність: він збирає голоси за набором запропонованих дат. Це правильний інструмент для запитання «коли ми всі шестеро можемо зустрітися» і неправильний — для запитання «забронювати зі мною 30 хвилин».

Сторінка бронювання є публічною, тому TLS — насамперед

Більшість сервісів, які користувачі розгортають самостійно, є приватними. Вікі, дошка чи панель моніторингу можуть працювати за VPN або входом через SSO і ніколи не бути доступними з відкритого Інтернету. Посилання для бронювання так не працює. Його має відкрити кожен, кому ви його надішлете. Тому налаштування змінюється у 3 конкретних аспектах.

До встановлення будь-яких компонентів потрібно мати доменне ім’я з A-записом, що вказує на VPS. Сертифікат потрібен уже в перший день, оскільки браузери позначають звичайну HTTP-форму як небезпечну, а клієнт вводить у неї своє ім’я та адресу електронної пошти. Також потрібно правильно вказати публічну URL-адресу застосунку в його конфігурації. Це значення використовується в посиланнях у вихідних листах і в URI перенаправлення OAuth. Установіть NEXT_PUBLIC_WEBAPP_URL у Cal.com, DOMAIN у Rallly, BASE_URL у Easy!Appointments або DAYOTTER_DOMAIN під час встановлення, а значенням задайте адресу https://, яку ви справді використовуватимете.

Rallly і DayOtter налаштовують TLS автоматично. Вбудований стек Rallly містить Traefik і випускає сертифікати Let's Encrypt, використовуючи адресу з ACME_EMAIL. Інсталятор DayOtter запускає Caddy з автоматичним HTTPS. Cal.com і Easy!Appointments цього не роблять. Тому перед ними потрібно налаштувати nginx і самостійно випустити сертифікат так само, як для сертифіката Let's Encrypt у nginx за допомогою Certbot. Прив’яжіть контейнер застосунку до 127.0.0.1, щоб єдиним шляхом доступу був проксі, який ви контролюєте. Якщо на тому самому сервері вже працює self-hosted альтернатива Trello для внутрішніх дошок, залиште її за наявною автентифікацією, а публічний server block створіть лише для хоста бронювання.

Cal.com на вашому VPS

Конфігурація Docker зберігається в окремому репозиторії, а образи попередньо зібрані та опубліковані на Docker Hub, тому їх потрібно завантажувати, а не збирати.

git clone --recursive https://github.com/calcom/cal.diy.git
cd cal.diy
cp .env.example .env
openssl rand -base64 32
openssl rand -base64 24
docker compose pull
docker compose up -d

Перше випадкове значення вкажіть у NEXTAUTH_SECRET, а друге — у CALENDSO_ENCRYPTION_KEY. Потрібні обидва значення. Установіть DATABASE_URL і вкажіть у NEXT_PUBLIC_WEBAPP_URL свою публічну адресу. Комплектний стек містить вебзастосунок, PostgreSQL і Prisma Studio. Документація описує docker compose up -d calcom для запуску лише застосунку з базою даних, розміщеною в іншому місці. Саме цей варіант потрібен після завершення інсталяції.

Завантажте образ, не збирайте його на VPS. Власні інструкції проєкту вимагають експортувати NODE_OPTIONS="--max-old-space-size=16384" під час складання з вихідного коду. Це виділяє 16 GB heap лише для Node. На ARM-додайте суфікс -arm до тегу образу. Проєкт не вказує мінімальні вимоги для запуску попередньо зібраного образу, тому 2 GB для застосунку разом із PostgreSQL — це моя робоча оцінка, а не задокументоване значення. Протягом першого тижня контролюйте використання пам’яті.

Перевірте, що застосунок запустився:

docker compose ps
docker compose logs -f calcom
curl -sI https://cal.example.com | head -n 1

Команда curl має вивести HTTP/2 200. Відповідь 502 Bad Gateway від nginx, коли контейнер має стан running, зазвичай означає, що під час першого запуску ще застосовуються міграції бази даних. Зачекайте кілька хвилин і перегляньте журнали, перш ніж робити висновок, що застосунок не працює. Вебхуки Cal.com спрацьовують після кожного підтвердженого бронювання, тому бронювання може запускати будь-яку автоматизацію, яку ви вже використовуєте, наприклад екземпляр n8n, доступний через HTTPS на вашому VPS.

Основний код поширюється за ліцензією AGPLv3. Деякі функції розміщені в каталозі enterprise і поширюються за окремою комерційною ліцензією. Прочитайте цю ліцензію, перш ніж будувати платний бізнес-процес на функціях команд.

Easy!Appointments на сервері з 1 GB RAM

Потрібні Apache або Nginx, PHP 8.2 або новішої версії та MySQL. Офіційний image доступний за адресою alextselegidis/easyappointments.

Спочатку важливе застереження. docker-compose.yml у репозиторії — це середовище розробки. Воно передбачає, що ви відкриєте shell у контейнері та виконаєте npm install && composer install && npm start. Це не варіант для розгортання. Натомість використовуйте опублікований image:

services:
  easyappointments:
    image: alextselegidis/easyappointments  # pin the current tag from Docker Hub
    environment:
      - BASE_URL=https://book.example.com
      - DB_HOST=mysql
      - DB_NAME=easyappointments
      - DB_USERNAME=easyapp
      - DB_PASSWORD=change-me
    ports:
      - '127.0.0.1:8080:80'
    depends_on:
      - mysql
  mysql:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=change-me-too
      - MYSQL_DATABASE=easyappointments
      - MYSQL_USER=easyapp
      - MYSQL_PASSWORD=change-me
    volumes:
      - ./mysql:/var/lib/mysql

BASE_URL має містити публічну HTTPS-адресу. Якщо вказати її неправильно, посилання для запису в листах-підтвердженнях вестимуть на host, до якого ваш клієнт не має доступу. Image обслуговує звичайний HTTP на порту 80 і не має власного сертифіката, тому порт прив’язано до 127.0.0.1, а Nginx завершує TLS перед застосунком. Якщо синтаксис Compose для вас новий, почніть із Основи Docker Compose на VPS, а потім поверніться.

Це беззаперечно найлегший варіант у цьому огляді. Два контейнери — PHP-застосунок і MySQL — без проблем працюють на VPS із 1 GB RAM. Компроміс полягає у функціональності: єдиним календарним бекендом є Google Calendar, а інтерфейс — це традиційна панель адміністратора, а не сучасний процес запису. Якщо ви використовуєте Microsoft 365, Fastmail або Nextcloud як календар, цей варіант вам не підходить.

Rallly для групових опитувань

Rallly відповідає на інше запитання. Він не публікує вашу доступність. Сервіс пропонує групі набір можливих часових інтервалів і збирає голоси. Це підходить для засідання правління, але не для посилання на бронювання клієнтом.

curl -fsSL https://get.rallly.co | bash

Прочитайте будь-який скрипт, перш ніж передавати його до shell через pipe. Замініть bash на less, перегляньте, що робить скрипт, і лише потім запустіть його. Ручний спосіб виконує ту саму роботу покроково, і кожен крок можна перевірити:

git clone https://github.com/lukevella/rallly-selfhosted.git
cd rallly-selfhosted
./rallly.sh setup
./rallly.sh start

Згідно з документацією, потрібні щонайменше 2 GB RAM, Docker версії 19.03 або новішої з Compose v2, вільні порти 80 і 443 та домен, спрямований на сервер. До складу стека входять Traefik для HTTPS, вебзастосунок, PostgreSQL і Garage для S3-сумісного об’єктного сховища. Установіть DOMAIN, SECRET_PASSWORD довжиною щонайменше 32 символи, SUPPORT_EMAIL і INITIAL_ADMIN_EMAIL. Якщо ви вже використовуєте reverse proxy, установіть PROXY_MODE=external і WEB_PORT, і Traefik не втручатиметься в роботу. Якщо ви вже використовуєте self-hosted S3-сумісне об’єктне сховище з MinIO, укажіть його в змінних S3_* і видаліть контейнер Garage.

SMTP тут обов’язковий, оскільки вхід виконується за magic link. Якщо робочого relay немає, ніхто не зможе ввійти, зокрема щойно створений обліковий запис адміністратора. Це сприятливий варіант збою електронної пошти: він зупиняє вас одразу, а не призводить до втрати бронювання клієнта через три тижні.

DayOtter, найновіший учасник

DayOtter — це платформа планування з ліцензією AGPLv3 і вбудованим асистентом. Інсталяція у production виконується однією командою:

curl -fsSL https://raw.githubusercontent.com/Dayotter/dayotter/main/deploy/install.sh \
  | sudo DAYOTTER_DOMAIN=cal.example.com bash

Перед запуском прочитайте її, як описано вище. Інсталятор налаштовує Docker, генерує секрети й запускає весь стек: вебзастосунок Next.js, фоновий worker для обробки нагадувань, синхронізації календарів і webhooks, PostgreSQL, Redis та Caddy з автоматичним HTTPS.

Підтримка календарів тут найширша серед чотирьох рішень. Підтримуються Google, Microsoft 365, Apple через CalDAV і ICS feeds з описаним вище застереженням щодо ICS. Усі інші інтеграції вмикаються окремо через змінні середовища, зокрема SMTP або Resend для пошти, ANTHROPIC_API_KEY для асистента, Twilio для SMS і Stripe для платежів. Асистент працює за принципом підтвердження перед виконанням: він пропонує дію, ви її підтверджуєте, і без явного підтвердження нічого не додається до календаря. Якщо залишити API key порожнім, ця частина продукту просто не запускається.

Ліцензування зручне для self-hosting. Ядро має ліцензію AGPLv3, а каталог ee/ містить комерційну ліцензію лише для cloud-версії, яка залишається неактивною, якщо не встановлено DAYOTTER_CLOUD=1. Отже, функції для команд, за які в hosted-плані станом на August 2026 стягується $9 за seat щомісяця, доступні на власному сервері.

Це також найважчий стек у цьому огляді та наймолодший проєкт. Запустіть його поруч із наявним booking link на два тижні, приймайте реальні бронювання через обидві системи та прочитайте журнали worker перед перенесенням клієнтів.

Скільки насправді коштує кожен стек

Кількість контейнерів — надійний показник того, скільки ресурсів стек вимагатиме від невеликого VPS, оскільки кожен сервіс має власний мінімальний обсяг пам’яті. Нижче наведено кількість контейнерів у Docker-стеках, опублікованих самими проєктами; дані перевірено в серпні 2026 року.

ChartServices in each project's documented Docker stack
The data behind this chart
[
  {
    "tool": "Easy!Appointments",
    "containers": 2,
    "database": "MySQL"
  },
  {
    "tool": "Cal.com",
    "containers": 3,
    "database": "PostgreSQL"
  },
  {
    "tool": "Rallly",
    "containers": 4,
    "database": "PostgreSQL"
  },
  {
    "tool": "DayOtter",
    "containers": 5,
    "database": "PostgreSQL and Redis"
  }
]

Easy!Appointments потребує 2 контейнерів і працює на сервері з 1 GB пам’яті. Готовий стек Rallly містить 4 контейнерів, а в документації для нього рекомендовано 2 GB пам’яті. Інсталятор DayOtter запускає 5 контейнерів, тому для нього потрібен найбільший сервер серед 4 наведених тут варіантів. Cal.com і DayOtter не публікують мінімальних вимог до пам’яті, тому для обох я беру за початкове значення 2 GB, а не за офіційно підтримувану вимогу.

Два з цих показників зменшуються, якщо інфраструктура вже працює. Контейнери Traefik і Garage у стеку Rallly можна прибрати, якщо вказати власний проксі та сховище об’єктів. Prisma Studio у Cal.com — це інструмент для розроблення, який не слід залишати запущеним на публічному сервері.

Яку self-hosted альтернативу Calendly вибрати

Індивідуальному консультанту варто використовувати Cal.com. Це єдиний проєкт у цьому огляді, який поєднує сторінку бронювання, яку легко впізнати, готові образи, що позбавляють потреби збирати Node на VPS, і підтримку CalDAV для тих, хто не використовує календар Google або Microsoft. Одна база даних PostgreSQL і один контейнер застосунку — це обсяг обслуговування, який можна підтримувати роками. Заплануйте пів дня на OAuth client і mail relay. Врахуйте, що застосунок CalDAV усе ще перебуває в beta-версії, тому перед публікацією посилання перевірте одне реальне бронювання від початку до кінця.

Невеликій команді варто звернути увагу на DayOtter. Weighted round robin і collective booking входять до ядра AGPLv3, тому self-hosting дає функції, за які hosted seat стягує плату. Worker process розрахований на нагадування та webhooks, які команда справді використовує. Компроміс полягає в рівні зрілості: це найновіший проєкт у цьому списку. Спочатку запустіть його паралельно та залиште старе посилання активним, доки не відстежите повний місяць бронювань.

Є ще 2 вузькі випадки. Якщо вам потрібне лише опитування, щоб визначити час зустрічі групи, встановіть Rallly і на цьому зупиніться. Якщо у вас VPS на 1 GB, ви користуєтеся Google Calendar і хочете найменший застосунок для приймання бронювань, Easy!Appointments переживе будь-який складніший варіант, який можна встановити на цей сервер. Ширше питання про те, які ще сервіси варто розмістити на тому самому сервері, розглянуто в матеріалі що варто self-hosting у 2026.

FAQ

Чи можна запустити self-hosted сторінку бронювання без доменного імені?

Ні. Кожен із цих застосунків записує публічну URL-адресу в посиланнях у листах із підтвердженням, а Google і Microsoft порівнюють OAuth redirect URI з тим самим значенням. Тому вказування лише IP-адреси спричиняє redirect_uri_mismatch на екрані згоди. Let’s Encrypt також не видає сертифікат для IP-адреси. Через це сторінка завантажується через звичайний HTTP, а браузер позначає форму як незахищену. Спочатку придбайте домен, спрямуйте A-запис на VPS, а потім виконайте встановлення.

Чому листи з підтвердженням бронювання ніколи не надходять?

Майже завжди причина в тому, що сервер намагається самостійно доставляти пошту. Більшість VPS-провайдерів блокують вихідний порт 25 для нових облікових записів, тому з’єднання зависає. Навіть якщо порт відкритий, нова адреса не має репутації відправника, і великі поштові системи відхиляють такі листи. Налаштуйте застосунок на transactional mail relay через порт 587, перевірте доступність порту за допомогою nc -vz -w 5 "$SMTP_HOST" 587, а потім опублікуйте SPF- і DKIM-записи, які надасть relay. Якщо ви використовуєте Cal.com, перевірте, що замінили стандартні значення EMAIL_SERVER_HOST=localhost і EMAIL_SERVER_PORT=1025, які вказують на локальну поштову скриньку для розроблення.

Чи синхронізується self-hosted Cal.com із CalDAV, чи лише з Google?

Із обома, але рівень готовності різний. Застосунок CalDAV має статус beta і перевірений із серверами, зокрема Baikal, Radicale, Nextcloud і Kerio Connect. Apple iCloud також працює через нього за допомогою пароля для конкретного застосунку. Google Calendar і Microsoft 365 синхронізують дані в обох напрямках. Але в self-hosted інсталяції потрібно створити власний OAuth client і передати його через GOOGLE_API_CREDENTIALS, оскільки облікові дані hosted service відсутні у вихідному коді.

Чому синхронізація з Google Calendar припиняє працювати через тиждень?

Тому що для проєкту Google Cloud досі встановлено статус публікації Testing. Google видає refresh tokens застосункам із таким статусом, але вони припиняють діяти через сім днів. Тому з’єднання спочатку працює, а потім переривається під час наступного оновлення token. У журналі застосунку з’являється invalid_grant. Переведіть OAuth consent screen у статус In production і один раз підключіть календар повторно. Повторне підключення без зміни статусу дасть ще сім днів роботи, але не більше.

Які з цих застосунків працюватимуть на VPS із 1 GB оперативної пам’яті?

Easy!Appointments працюватиме, оскільки це PHP-застосунок із MySQL. У документації Rallly вказано мінімум 2 GB, а його комплектний стек запускає чотири сервіси. Cal.com і DayOtter не вказують мінімальних вимог, але Next.js-застосунок із PostgreSQL, а у випадку DayOtter також Redis і worker process, означає, що слід планувати щонайменше 2 GB. Ніколи не збирайте Cal.com із вихідного коду на малопотужному сервері: власні інструкції проєкту вимагають 16 GB heap для Node, тому використовуйте готовий image.

#scheduling#calendly#cal-com#self-hosted#booking