SSD Nodes Learn 🎉 VPS từ $4.99/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

So sánh 4 lựa chọn Calendly tự host trên VPS

So sánh Cal.com, Easy!Appointments, Rallly và DayOtter trên VPS, tập trung vào đồng bộ calendar hai chiều và email gửi đi đến Gmail, Microsoft 365.

Câu trả lời ngắn gọn

Một giải pháp thay thế Calendly tự host phải làm được một việc mà các công cụ nội bộ trên VPS của bạn không bao giờ làm: phục vụ người dùng công khai. Trang đặt lịch chính là sản phẩm. Ngay từ ngày đầu, trang này cần một tên miền thực và TLS (bảo mật tầng truyền tải), đồng thời phải gửi được email đến những người chưa từng biết đến server của bạn.

Bốn dự án đáp ứng các lựa chọn thực tế. Cal.com là lựa chọn gần Calendly nhất và là phương án mặc định cho tư vấn viên làm việc một mình. Easy!Appointments nhẹ, dùng PHP và MySQL, phù hợp với VPS 1 GB. Rallly là công cụ tạo poll cho nhóm và hoàn toàn không có trang đặt lịch. DayOtter là dự án mới nhất, một nền tảng lập lịch theo AGPLv3 với assistant xác nhận trước ở phía trước.

Hai câu hỏi quyết định bạn có thể thực sự chạy giải pháp nào. Nó có đồng bộ hai chiều với calendar bạn đang sử dụng không? Và nó có gửi được email không? Phần thứ hai là nơi hầu hết các hệ thống đặt lịch tự host âm thầm thất bại, nên sẽ được xử lý trước.

Email gửi đi là phần hay lỗi nhất

Email xác nhận đặt lịch lại vào hộp thư của một người không liên quan. Đây là email giao dịch gửi đến Gmail hoặc Microsoft 365, và các hệ thống nhận thư đó đánh giá bạn dựa trên địa chỉ IP gửi mail cùng các bản ghi DNS.

Gửi mail trực tiếp từ VPS gần như không bao giờ hoạt động. Hầu hết nhà cung cấp đều chặn TCP port 25 gửi ra trên tài khoản mới, nên kết nối sẽ treo rồi timeout. Ngay cả khi port 25 được mở, địa chỉ VPS mới chưa có lịch sử gửi mail, còn các hệ thống nhận thư lớn thường xem địa chỉ thuộc dải hosting chưa được biết đến là đáng ngờ. Lịch đặt được ghi vào database, trang hiển thị đã xác nhận, nhưng không ai nhận được email. Phía server không có dấu hiệu lỗi rõ ràng, nên vấn đề này thường chỉ được phát hiện sau vài tuần, khi khách hàng không bao giờ đến.

Hãy dùng relay. Nhà cung cấp email giao dịch nào cũng được; ứng dụng chỉ cần hostname, port, user và password. Kiểm tra port có thể truy cập được trước khi sửa cấu hình ứng dụng:

nc -vz -w 5 "$SMTP_HOST" 587

Dòng succeeded cho biết đường kết nối đang mở. Kết nối bị treo hoặc xuất hiện Connection refused nghĩa là port bị chặn ở cấp mạng; chỉnh sửa .env bao nhiêu cũng không khắc phục được. Relay lắng nghe trên port 587 hoặc 465 chính xác vì port 25 thường bị chặn.

Mỗi project nhận cấu hình relay theo cách riêng. Cal.com đọc EMAIL_FROM, EMAIL_SERVER_HOST, EMAIL_SERVER_PORT, EMAIL_SERVER_USEREMAIL_SERVER_PASSWORD, đồng thời cũng chấp nhận RESEND_API_KEY. Hãy chú ý biến này: .env.example mặc định được ship kèm sẽ trỏ EMAIL_SERVER_HOST đến localhost trên port 1025, tức là mailbox local dùng cho môi trường development. Nếu giữ nguyên giá trị mặc định, ứng dụng sẽ gửi mail vào nơi không tồn tại mà không báo lỗi. Rallly dùng SMTP_HOST, SMTP_PORT, SMTP_USERSMTP_PWD. DayOtter dùng cấu hình SMTP hoặc Resend key. Easy!Appointments gửi thông báo từ chính ứng dụng, nên hãy trỏ ứng dụng đến cùng relay trong trang settings trước khi nhận booking thật.

Sau đó, publish các bản ghi DNS mà relay cung cấp. Bản ghi SPF (sender policy framework) chỉ rõ server nào được phép gửi mail cho domain của bạn. Key DKIM (domainkeys identified mail) ký từng message để hệ thống nhận thư xác minh message không bị thay đổi. Thêm policy DMARC (domain-based message authentication, reporting and conformance) sau khi cả SPF và DKIM đều pass. Gửi một booking thử đến địa chỉ thật tại một nhà cung cấp lớn, mở headers của message và xác nhận các dòng authentication hiển thị pass. Trang đặt lịch không thể gửi email còn tệ hơn việc không có trang đặt lịch, vì lỗi xảy ra âm thầm.

Những backend lịch nào thực sự đồng bộ hai chiều

Đồng bộ có hai chiều và mỗi chiều có thể hỏng riêng. Chiều đọc là chiều kiểm tra thời gian rảnh: app phải thấy các khoảng thời gian bạn đã bận, nếu không app sẽ cấp một khung giờ mà bạn đang có lịch. Chiều ghi là thao tác đặt lịch: sự kiện đã xác nhận phải xuất hiện trên lịch bạn thực sự theo dõi, không chỉ nằm bên trong công cụ đặt lịch.

Google Calendar và Microsoft 365 hỗ trợ cả hai chiều, với một điều kiện khi self-host. Bạn phải tự tạo client OAuth (ủy quyền mở), vì client ID của sản phẩm hosted không có trong source code. Với Cal.com, đó là GOOGLE_API_CREDENTIALS trong .env, chứa file JSON bạn tải xuống từ Google Cloud console. DayOtter cũng dùng thông tin xác thực OAuth của Google và Microsoft theo cách tương tự.

Có hai vấn đề thường làm hỏng kết nối này, và bạn nên biết cả hai trước khi bắt đầu. Thứ nhất, redirect URI bạn đăng ký phải khớp chính xác với URL public, bao gồm scheme và cả path ở cuối nếu có. Nếu không, Google sẽ dừng kết nối với redirect_uri_mismatch tại màn hình consent. Thứ hai, một Google project vẫn ở trạng thái phát hành Testing sẽ cấp refresh token hết hạn sau bảy ngày. Đồng bộ hoạt động cả tuần rồi dừng, và log của app hiển thị invalid_grant ở lần refresh tiếp theo. Chuyển consent screen sang In production, hoặc chấp nhận phải kết nối lại thủ công vào mỗi thứ Hai.

CalDAV (phần mở rộng lịch của WebDAV) là lựa chọn mở, nhưng mức độ hỗ trợ hạn chế hơn. Cal.com cung cấp app CalDAV vẫn được đánh dấu là beta, đã được kiểm thử với các server như Baikal, Radicale, Nextcloud và Kerio Connect. Apple iCloud hoạt động qua cùng app này nhưng cần app-specific password thay vì mật khẩu Apple ID. DayOtter liệt kê Apple qua CalDAV cùng với Google và Microsoft 365.

ICS feed không phải là đồng bộ. URL .ics đã đăng ký chỉ được thiết kế để đọc, nên có thể chặn các khoảng thời gian bận trên trang đặt lịch nhưng không bao giờ nhận được lịch đặt. Nếu một công cụ chỉ cung cấp ICS cho lịch của bạn, bạn mới có một nửa hệ thống và vẫn phải sao chép sự kiện thủ công.

Easy!Appointments chỉ đồng bộ Google Calendar, không hỗ trợ dịch vụ nào khác. Rallly hoàn toàn không đọc thời gian rảnh: nó thu thập phiếu bầu cho một nhóm ngày được đề xuất. Đây là công cụ phù hợp cho câu hỏi "sáu người chúng ta có thể gặp nhau vào lúc nào" và không phù hợp cho "đặt 30 phút với tôi".

Trang đặt lịch công khai, nên TLS phải được cấu hình trước

Phần lớn dịch vụ tự host là riêng tư. Wiki, board, dashboard đều có thể nằm sau VPN hoặc đăng nhập SSO và không bao giờ xuất hiện trên Internet công cộng. Link đặt lịch thì không thể như vậy. Bất kỳ ai bạn gửi link đều phải truy cập được link đó, nên cách triển khai thay đổi theo 3 điểm cụ thể.

Bạn cần một tên miền có A record trỏ đến VPS trước khi cài bất cứ thứ gì. Bạn cần certificate ngay từ ngày đầu tiên, vì trình duyệt đánh dấu form HTTP thuần là không an toàn, trong khi client đang nhập tên và email vào đó. Bạn cũng cần đặt đúng public URL của app trong config, vì giá trị này được ghi vào các link trong email gửi đi và các OAuth redirect URI. Đặt NEXT_PUBLIC_WEBAPP_URL trong Cal.com, DOMAIN trong Rallly, BASE_URL trong Easy!Appointments hoặc DAYOTTER_DOMAIN trong lúc cài đặt, rồi đặt giá trị đó thành địa chỉ https:// mà bạn thực sự sẽ dùng.

Rallly và DayOtter tự xử lý TLS. Stack đi kèm Rallly có Traefik và cấp certificate Let's Encrypt bằng địa chỉ trong ACME_EMAIL. Trình cài đặt của DayOtter khởi chạy Caddy với HTTPS tự động. Cal.com và Easy!Appointments không làm việc này, nên bạn đặt nginx phía trước và tự cấp certificate, giống như khi cấu hình certificate Let's Encrypt trên nginx bằng Certbot. Bind container của app vào 127.0.0.1 để chỉ có thể truy cập app thông qua proxy do bạn kiểm soát. Nếu máy đó đang chạy một giải pháp thay thế Trello tự host cho các board nội bộ, hãy giữ dịch vụ đó sau lớp xác thực hiện có và chỉ tạo public server block cho booking host.

Cal.com trên VPS

Cấu hình Docker nằm trong một repository riêng, còn các image đã được build sẵn trên Docker Hub, nên bạn chỉ cần pull thay vì build.

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

Giá trị ngẫu nhiên thứ nhất đặt vào NEXTAUTH_SECRET và giá trị thứ hai đặt vào CALENDSO_ENCRYPTION_KEY. Cả hai đều bắt buộc. Đặt DATABASE_URL và trỏ NEXT_PUBLIC_WEBAPP_URL đến địa chỉ public của bạn. Stack đi kèm gồm web app, PostgreSQL và Prisma Studio; tài liệu cung cấp docker compose up -d calcom để chạy riêng app và kết nối đến database do bạn host ở nơi khác. Đây là cách bạn nên dùng sau khi cài đặt hoàn tất.

Pull image, không build image trên VPS. Hướng dẫn chính thức của dự án yêu cầu export NODE_OPTIONS="--max-old-space-size=16384" khi build từ source. Giá trị này dành riêng 16 GB heap cho Node. Trên phần cứng ARM, thêm hậu tố -arm vào image tag. Dự án không công bố mức tối thiểu để chạy image đã build sẵn, nên hãy xem 2 GB cho app và PostgreSQL là mức ước tính thực tế của tôi, không phải con số được tài liệu hóa. Theo dõi memory trong tuần đầu tiên.

Kiểm tra service đã khởi động:

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

Lệnh curl phải in ra HTTP/2 200. Nếu nginx trả về 502 Bad Gateway trong khi container vẫn hiển thị trạng thái đang chạy, thường là lần boot đầu tiên vẫn đang áp dụng database migration. Chờ vài phút và đọc log trước khi kết luận rằng service bị lỗi. Webhook của Cal.com được kích hoạt sau mỗi booking được xác nhận, nên một booking có thể kích hoạt bất kỳ automation nào bạn đang chạy, chẳng hạn như một instance n8n có thể truy cập qua HTTPS trên VPS của bạn.

Phần lõi dùng AGPLv3. Một số tính năng nằm trong thư mục enterprise và được cấp phép theo một commercial licence riêng. Đọc licence đó trước khi xây dựng quy trình kinh doanh có thu phí dựa trên các tính năng dành cho team.

Easy!Appointments trên máy 1 GB

Yêu cầu gồm Apache hoặc Nginx, PHP 8.2 trở lên và MySQL. Có image chính thức tại alextselegidis/easyappointments.

Trước hết, cần lưu ý một điểm. docker-compose.yml trong repository là môi trường development. Nó yêu cầu bạn mở shell trong container rồi chạy npm install && composer install && npm start. Đây không phải cấu hình triển khai. Hãy dùng image đã publish thay thế:

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 phải là địa chỉ HTTPS public. Nếu nhập sai, các liên kết đặt lịch trong email xác nhận sẽ trỏ đến một host mà khách hàng không thể truy cập. Image cung cấp HTTP thuần trên port 80 và không có certificate riêng. Vì vậy, port được bind vào 127.0.0.1 và nginx đảm nhiệm TLS termination ở phía trước. Nếu bạn chưa quen với cú pháp compose, hãy bắt đầu với Kiến thức cơ bản về Docker Compose trên VPS rồi quay lại.

Đây là lựa chọn nhẹ nhất với khoảng cách rất lớn. Hai container, một ứng dụng PHP và MySQL, chạy ổn định trên VPS 1 GB. Đổi lại, khả năng tích hợp bị hạn chế: Google Calendar là calendar backend duy nhất, còn giao diện là một admin panel truyền thống thay vì quy trình đặt lịch hiện đại. Nếu calendar của bạn là Microsoft 365, Fastmail hoặc Nextcloud, hãy loại lựa chọn này ngay từ đầu.

Rallly cho khảo sát nhóm

Rallly giải quyết một nhu cầu khác. Nó không công bố thời gian bạn rảnh. Nó đưa ra một tập các khung giờ để nhóm lựa chọn và thu thập phiếu bầu. Đây là cách phù hợp để chọn thời gian họp ban quản trị, nhưng không phù hợp với link đặt lịch cho khách hàng.

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

Hãy đọc mọi script trước khi pipe script đó vào shell. Thay bash bằng less, đọc nội dung script thực hiện, rồi mới chạy. Cách cài đặt thủ công thực hiện cùng công việc theo các bước mà bạn có thể kiểm tra:

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

Tài liệu yêu cầu tối thiểu 2 GB RAM, Docker 19.03 trở lên với Compose v2, cổng 80 và 443 chưa được sử dụng, và một domain trỏ đến server. Stack đi kèm gồm Traefik để xử lý HTTPS, web application, PostgreSQL và Garage để lưu trữ object tương thích với S3. Thiết lập DOMAIN, một SECRET_PASSWORD có độ dài ít nhất 32 ký tự, SUPPORT_EMAILINITIAL_ADMIN_EMAIL. Nếu bạn đã chạy reverse proxy, hãy thiết lập PROXY_MODE=externalWEB_PORT để Traefik không can thiệp. Nếu bạn đã chạy object storage tương thích với S3 tự host bằng MinIO, hãy trỏ các biến S3_* đến đó và bỏ container Garage.

SMTP không phải tùy chọn trong trường hợp này, vì việc đăng nhập sử dụng magic link. Nếu không có relay hoạt động, không ai có thể đăng nhập, kể cả tài khoản admin bạn vừa tạo. Đây là dạng lỗi email ít nghiêm trọng hơn: nó chặn bạn ngay tại bước đăng nhập thay vì làm mất booking của khách hàng sau 3 tuần.

DayOtter, nền tảng mới nhất

DayOtter là nền tảng lập lịch theo AGPLv3, có tích hợp assistant. Cài đặt production chỉ cần một lệnh:

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

Hãy đọc lệnh trước khi chạy, như đã nói ở trên. Installer sẽ cài Docker, tạo secrets và khởi động toàn bộ stack: web app Next.js, background worker xử lý nhắc lịch, đồng bộ calendar và webhook, PostgreSQL, Redis và Caddy với HTTPS tự động.

DayOtter có phạm vi hỗ trợ calendar rộng nhất trong 4 nền tảng. Nền tảng hỗ trợ Google, Microsoft 365, Apple qua CalDAV và feed ICS, kèm hạn chế của ICS đã nêu ở trên. Mọi integration khác đều là tùy chọn qua biến môi trường, gồm SMTP hoặc Resend cho email, ANTHROPIC_API_KEY cho assistant, Twilio cho SMS và Stripe cho thanh toán. Assistant hoạt động theo cơ chế xác nhận trước: nó đề xuất, bạn phê duyệt, và không có gì được thêm vào calendar nếu bạn chưa xác nhận rõ ràng. Để trống API key thì phần này của sản phẩm sẽ không chạy.

Giấy phép rõ ràng đối với người tự host. Core dùng AGPLv3, còn thư mục ee/ chứa giấy phép thương mại chỉ dành cho cloud và sẽ không hoạt động nếu chưa đặt DAYOTTER_CLOUD=1. Vì vậy, các tính năng dành cho team của gói hosted, có mức phí $9 cho mỗi seat mỗi tháng tính đến August 2026, đều có thể sử dụng trên server của bạn.

Đây cũng là stack nặng nhất ở đây và là dự án mới nhất. Hãy chạy nó song song với booking link hiện có trong 2 tuần, nhận booking thực qua cả hai hệ thống và đọc log của worker trước khi chuyển khách hàng sang hệ thống mới.

Chi phí thực tế của từng stack

Số lượng container là chỉ dấu trung thực về mức tài nguyên mà một stack sẽ yêu cầu trên VPS nhỏ, vì mỗi service đều có mức sử dụng bộ nhớ tối thiểu riêng. Đây là số lượng lấy từ Docker stack được từng dự án công bố, kiểm tra vào tháng 8 năm 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 cần 2 container và chạy được trên 1 GB. Stack đi kèm của Rallly có 4 container, còn tài liệu của dự án yêu cầu 2 GB. Trình cài đặt của DayOtter khởi chạy 5 container, nên nó cần VPS lớn nhất trong 4 lựa chọn ở đây. Cal.com và DayOtter không công bố mức bộ nhớ tối thiểu, nên tôi lấy 2 GB làm mốc bắt đầu cho cả hai, không xem đó là con số được hỗ trợ chính thức.

Hai số liệu này sẽ giảm nếu bạn đã có sẵn hạ tầng. Các container Traefik và Garage của Rallly đều có thể bỏ qua khi bạn trỏ ứng dụng đến proxy và object storage của riêng mình. Prisma Studio của Cal.com là công cụ phát triển, không nên để chạy trên server public.

Nên chọn giải pháp thay thế Calendly tự host nào

Tư vấn viên làm việc một mình nên dùng Cal.com. Đây là dự án duy nhất trong danh sách kết hợp được trang đặt lịch quen thuộc với người dùng, các image dựng sẵn giúp bạn không phải build Node trên VPS, và hỗ trợ CalDAV dành cho những người không dùng lịch trên Google hoặc Microsoft. Một cơ sở dữ liệu PostgreSQL và một application container là khối lượng bảo trì có thể quản lý trong nhiều năm. Hãy dành một buổi chiều để cấu hình OAuth client và mail relay. Lưu ý rằng ứng dụng CalDAV vẫn đang ở giai đoạn beta, vì vậy hãy kiểm thử một lượt đặt lịch thực tế từ đầu đến cuối trước khi công khai liên kết.

Nhóm nhỏ nên xem xét DayOtter. Weighted round robin và đặt lịch tập thể nằm trong core AGPLv3. Vì vậy, tự host cho bạn các tính năng mà dịch vụ hosted thường tính phí theo seat. Worker process cũng được thiết kế cho các reminder và webhook mà nhóm thực sự sử dụng. Đổi lại, dự án còn mới: đây là dự án mới nhất trong danh sách. Vì vậy, trước tiên hãy chạy song song và giữ liên kết cũ hoạt động cho đến khi bạn theo dõi đủ một tháng đặt lịch.

Có 2 trường hợp hẹp hơn. Nếu tất cả những gì bạn cần là một poll để tìm thời gian cả nhóm có thể họp, hãy cài Rallly rồi dừng ở đó. Nếu bạn có VPS 1 GB, dùng Google Calendar và muốn một ứng dụng nhỏ nhất có thể nhận đặt lịch, Easy!Appointments sẽ hoạt động lâu hơn mọi lựa chọn cầu kỳ hơn mà bạn có thể cài trên máy đó. Với câu hỏi rộng hơn về những gì đáng tự host trên cùng máy chủ, xem những gì đáng tự host trong 2026.

FAQ

Tôi có thể chạy trang đặt lịch tự host mà không cần tên miền không?

Không. Tất cả các app này đều ghi URL public vào các liên kết trong email xác nhận. Google và Microsoft cũng đối chiếu OAuth redirect URI với chính giá trị đó. Vì vậy, dùng địa chỉ IP thuần sẽ gây redirect_uri_mismatch tại màn hình cấp quyền. Let’s Encrypt cũng không cấp chứng chỉ cho địa chỉ IP. Trang khi đó sẽ tải qua HTTP thuần và trình duyệt đánh dấu biểu mẫu là không bảo mật. Hãy mua tên miền trước, trỏ bản ghi A đến VPS, rồi mới cài đặt.

Tại sao email xác nhận đặt lịch của tôi không bao giờ đến?

Gần như luôn là vì server đang tự gửi email. Hầu hết nhà cung cấp VPS chặn port outbound 25 trên account mới. Vì vậy, kết nối bị treo. Ngay cả khi port này không bị chặn, một địa chỉ mới cũng chưa có uy tín gửi email và các hệ thống nhận thư lớn sẽ từ chối. Cấu hình app dùng transactional mail relay trên port 587. Xác nhận port có thể truy cập bằng nc -vz -w 5 "$SMTP_HOST" 587. Sau đó, publish các bản ghi SPF và DKIM mà relay cung cấp. Nếu đang chạy Cal.com, hãy kiểm tra bạn đã thay các giá trị mặc định EMAIL_SERVER_HOST=localhostEMAIL_SERVER_PORT=1025 chưa. Các giá trị này trỏ đến mailbox development cục bộ.

Cal.com tự host có đồng bộ với CalDAV hay chỉ với Google?

Cả hai, nhưng mức độ hoàn thiện khác nhau. CalDAV app đang được đánh dấu beta và đã được xác minh với các server gồm Baikal, Radicale, Nextcloud và Kerio Connect. Apple iCloud cũng hoạt động thông qua app này bằng app-specific password. Google Calendar và Microsoft 365 đều đồng bộ hai chiều. Tuy nhiên, với bản cài tự host, bạn phải tự tạo OAuth client và cung cấp thông tin đó qua GOOGLE_API_CREDENTIALS, vì credential của dịch vụ hosted không nằm trong source.

Tại sao đồng bộ Google Calendar ngừng hoạt động sau một tuần?

Vì dự án Google Cloud vẫn có trạng thái publish là Testing. Google cấp refresh token cho các app ở trạng thái này, nhưng token sẽ hết hạn sau 7 ngày. Vì vậy, kết nối hoạt động rồi bị lỗi ở lần refresh token tiếp theo. Application log sẽ hiển thị invalid_grant. Chuyển OAuth consent screen sang In production, rồi kết nối lại calendar một lần. Nếu chỉ kết nối lại mà không đổi trạng thái, bạn chỉ có thêm 7 ngày.

Những app nào trong số này chạy được trên VPS 1 GB?

Easy!Appointments chạy được vì đây là PHP application dùng MySQL. Rallly ghi rõ mức tối thiểu là 2 GB, và stack đi kèm chạy 4 service. Cal.com và DayOtter không công bố mức tối thiểu. Tuy nhiên, một Next.js application chạy cùng PostgreSQL, và trong trường hợp DayOtter còn có Redis cùng worker process, nghĩa là bạn nên dùng 2 GB trở lên. Không bao giờ build Cal.com từ source trên máy nhỏ. Build instructions của project yêu cầu Node heap 16 GB. Thay vào đó, hãy pull prebuilt image.

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