เปรียบเทียบเครื่องมือจองนัดหมาย Self-hosted แทน Calendly
เปรียบเทียบ Cal.com, Easy!Appointments, Rallly และ DayOtter บน VPS ของคุณเอง โดยเน้นที่การซิงค์ปฏิทินสองทางและการส่งอีเมลขาออก ซึ่งเป็นจุดที่ระบบส่วนใหญ่มักตั้งค่าไม่สำเร็จ
คำตอบโดยย่อ
ทางเลือกแทน Calendly สำหรับการ self-host ต้องทำสิ่งหนึ่งที่เครื่องมือภายใน VPS ของคุณไม่เคยทำ นั่นคือการตอบสนองต่อสาธารณะ หน้าจองนัดหมาย คือ ตัวผลิตภัณฑ์ ดังนั้นจึงจำเป็นต้องมีชื่อโดเมนจริงและ TLS (transport layer security) ตั้งแต่วันแรก และต้องสามารถส่งอีเมลไปยังผู้ที่ไม่เคยรู้จักเซิร์ฟเวอร์ของคุณมาก่อนได้
มี 4 โครงการที่ครอบคลุมการใช้งานจริง Cal.com เป็นตัวเลือกที่ใกล้เคียงกับ Calendly มากที่สุดและเป็นตัวเลือกเริ่มต้นสำหรับที่ปรึกษาอิสระ Easy!Appointments เป็นตัวเลือกที่เบาบางกว่า ใช้ PHP และ MySQL ซึ่งทำงานได้ดีบน VPS ขนาด 1 GB ส่วน Rallly เป็นเครื่องมือสำหรับทำโพลกลุ่มและไม่มีหน้าจองนัดหมาย และ DayOtter เป็นผู้เล่นรายใหม่ล่าสุด ซึ่งเป็นแพลตฟอร์มการจัดตารางเวลาแบบ AGPLv3 ที่มีระบบผู้ช่วยยืนยันนัดหมายอยู่เบื้องหน้า
คำถาม 2 ข้อจะเป็นตัวตัดสินว่าคุณสามารถรันตัวไหนได้จริง ข้อแรกคือ มันสามารถซิงค์ข้อมูลแบบสองทางกับปฏิทินที่คุณใช้งานอยู่เป็นประจำได้หรือไม่? และข้อสองคือ มันสามารถส่งอีเมลได้หรือไม่? คำถามข้อที่สองนี้คือจุดที่การตั้งค่าระบบจองนัดหมายแบบ self-host ส่วนใหญ่ล้มเหลวโดยไม่รู้ตัว ดังนั้นจึงต้องพิจารณาเป็นอันดับแรก
การส่งอีเมลขาออกเป็นส่วนที่มักจะล้มเหลว
การยืนยันการจองที่ส่งไปยังกล่องจดหมายของบุคคลอื่นถือเป็นอีเมลเชิงธุรกรรม (transactional mail) ซึ่งจะถูกส่งไปยัง Gmail หรือ Microsoft 365 โดยผู้รับเหล่านี้จะประเมินความน่าเชื่อถือของคุณจาก IP address ที่ใช้ส่งและระเบียน DNS ของคุณ
การส่งอีเมลโดยตรงจาก VPS มักจะใช้งานไม่ได้ ผู้ให้บริการส่วนใหญ่บล็อกพอร์ต TCP 25 สำหรับบัญชีใหม่ ทำให้การเชื่อมต่อค้างและหมดเวลา (timeout) แม้ในกรณีที่พอร์ต 25 เปิดอยู่ IP address ของ VPS ที่เพิ่งเริ่มต้นใช้งานจะไม่มีประวัติการส่งอีเมล และผู้ให้บริการรายใหญ่จะถือว่า IP จากช่วงของโฮสติ้งเป็นที่น่าสงสัย ข้อมูลการจองจะถูกบันทึกลงฐานข้อมูล หน้าเว็บจะแสดงว่ายืนยันแล้ว แต่ไม่มีใครได้รับอีเมล ซึ่งจากฝั่งเซิร์ฟเวอร์จะดูเหมือนไม่มีอะไรผิดปกติ ทำให้ปัญหานี้มักถูกพบในอีกหลายสัปดาห์ต่อมาโดยลูกค้าที่ไม่ได้ปรากฏตัวตามนัด
ให้ใช้ relay แทน ผู้ให้บริการอีเมลเชิงธุรกรรมรายใดก็ได้สามารถใช้งานได้ โดยแอปพลิเคชันต้องการเพียง hostname, port, user และ password เท่านั้น ให้ตรวจสอบว่าพอร์ตสามารถเข้าถึงได้ก่อนที่จะแก้ไขการตั้งค่าของแอปพลิเคชัน:
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) เข้าไป ทดสอบส่งการจองไปยังที่อยู่อีเมลจริงของผู้ให้บริการรายใหญ่ เปิดดูส่วนหัวของข้อความ (message headers) และยืนยันว่าบรรทัดการตรวจสอบสิทธิ์แสดงค่าเป็น pass หน้าเว็บจองที่ไม่สามารถส่งอีเมลได้นั้นแย่ยิ่งกว่าการไม่มีหน้าเว็บจอง เพราะมันจะล้มเหลวโดยไม่มีการแจ้งเตือนใดๆ
Backend ปฏิทินใดบ้างที่รองรับการซิงค์แบบสองทางอย่างแท้จริง
การซิงค์มีสองทิศทางและอาจล้มเหลวแยกกันได้ ทิศทางการอ่านคือความพร้อมใช้งาน (availability): แอปพลิเคชันต้องเห็นช่วงเวลาที่คุณไม่ว่างอยู่แล้ว มิฉะนั้นแอปจะเสนอช่วงเวลาที่คุณติดธุระให้ผู้อื่น ทิศทางการเขียนคือการจอง (booking): กิจกรรมที่ยืนยันแล้วต้องปรากฏบนปฏิทินที่คุณใช้งานจริง ไม่ใช่แค่ภายในเครื่องมือจองเท่านั้น
Google Calendar และ Microsoft 365 รองรับทั้งสองทิศทาง โดยมีเงื่อนไขหนึ่งข้อสำหรับการติดตั้งแบบ self-hosted คือคุณต้องสร้าง OAuth (open authorization) client ด้วยตนเอง เนื่องจาก client ID ของผลิตภัณฑ์ที่โฮสต์ไว้ไม่ได้รวมอยู่ในซอร์สโค้ด สำหรับ Cal.com สิ่งนี้คือ GOOGLE_API_CREDENTIALS ใน .env ซึ่งเก็บไฟล์ JSON ที่คุณดาวน์โหลดมาจาก Google Cloud console ส่วน DayOtter รับข้อมูลประจำตัว OAuth ของ Google และ Microsoft ในลักษณะเดียวกัน
มีสองสิ่งที่มักจะเกิดปัญหา ซึ่งควรทราบก่อนเริ่มดำเนินการ ประการแรก redirect URI ที่คุณลงทะเบียนต้องตรงกับ URL สาธารณะของคุณทุกประการ รวมถึง scheme และ path ต่อท้าย มิฉะนั้น Google จะหยุดการเชื่อมต่อด้วย redirect_uri_mismatch ที่หน้าจอขอความยินยอม ประการที่สอง โปรเจกต์ของ Google ที่ยังคงสถานะการเผยแพร่เป็น Testing จะออก refresh token ที่มีอายุการใช้งานเพียง 7 วัน การซิงค์จะทำงานได้ตลอดทั้งสัปดาห์แล้วหยุดลง และ log ของแอปจะแสดง invalid_grant ในการรีเฟรชครั้งถัดไป คุณต้องเปลี่ยนสถานะหน้าจอขอความยินยอมเป็น 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 ไม่ใช่การซิงค์ URL ของ .ics ที่สมัครสมาชิกไว้ถูกออกแบบมาให้อ่านได้อย่างเดียว (read-only) ดังนั้นจึงสามารถบล็อกเวลาบนหน้าจองของคุณได้ แต่ไม่สามารถรับการจองได้ หากเครื่องมือใดเสนอเพียง ICS สำหรับปฏิทินของคุณ แสดงว่าคุณมีระบบรองรับเพียงครึ่งเดียว และคุณยังคงต้องคัดลอกกิจกรรมด้วยตนเองอยู่ดี
Easy!Appointments ซิงค์กับ Google Calendar ได้เพียงอย่างเดียว ส่วน Rallly ไม่ได้อ่านความพร้อมใช้งานเลย แต่จะรวบรวมคะแนนโหวตจากชุดวันที่ที่เสนอไว้ ซึ่งเป็นเครื่องมือที่เหมาะสมสำหรับคำถามที่ว่า "พวกเรา 6 คนจะนัดเจอกันเมื่อไหร่ดี" แต่ไม่ใช่เครื่องมือที่เหมาะสมสำหรับ "จองเวลา 30 นาทีกับฉัน"
หน้าจองานจองเป็นสาธารณะ ดังนั้น TLS จึงต้องมาก่อน
บริการส่วนใหญ่ที่ผู้คนทำ self-host มักเป็นบริการส่วนตัว ไม่ว่าจะเป็น wiki, กระดานสนทนา หรือแดชบอร์ด ทั้งหมดนี้สามารถวางไว้หลัง VPN หรือการล็อกอินผ่าน SSO โดยไม่ต้องเปิดเผยต่ออินเทอร์เน็ตสาธารณะ แต่ลิงก์สำหรับจองคิวไม่สามารถทำเช่นนั้นได้ ทุกคนที่คุณส่งลิงก์ให้จำเป็นต้องเข้าถึงหน้านั้นได้ ซึ่งทำให้การตั้งค่าเปลี่ยนไปใน 3 ประเด็นหลัก
คุณต้องมีชื่อโดเมนที่ชี้ A record ไปยัง VPS ก่อนที่จะติดตั้งสิ่งใดๆ คุณต้องมีใบรับรองตั้งแต่วันแรก เพราะเบราว์เซอร์จะระบุว่าฟอร์ม HTTP ธรรมดาไม่ปลอดภัย ในขณะที่ลูกค้าของคุณกำลังกรอกชื่อและอีเมลลงไป และคุณต้องตั้งค่า URL สาธารณะของแอปในไฟล์ config ให้ถูกต้อง เพราะค่านี้จะถูกฝังอยู่ในลิงก์ภายในอีเมลที่ส่งออกไปและใน OAuth redirect URI ให้ตั้งค่า 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 ให้ผูก container ของแอปไว้กับ 127.0.0.1 เพื่อให้ทางเข้าเดียวคือผ่าน proxy ที่คุณควบคุม หากเซิร์ฟเวอร์เดียวกันนี้รัน ทางเลือกของ Trello สำหรับทำ self-host ไว้ใช้ภายใน อยู่ก่อนแล้ว ให้เก็บบริการนั้นไว้หลังระบบยืนยันตัวตนเดิมของคุณ และเปิด server block สาธารณะเฉพาะสำหรับโฮสต์ที่ใช้จองคิวเท่านั้น
การติดตั้ง Cal.com บน VPS ของคุณ
การตั้งค่า Docker จะอยู่ใน repository แยกต่างหาก และ image ได้ถูก build ไว้ล่วงหน้าบน Docker Hub แล้ว ดังนั้นให้ใช้วิธี pull แทนการ 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ค่าสุ่มค่าแรกให้ใส่ใน NEXTAUTH_SECRET และค่าที่สองใน CALENDSO_ENCRYPTION_KEY ทั้งสองค่าจำเป็นต้องระบุ กำหนดค่า DATABASE_URL และชี้ NEXT_PUBLIC_WEBAPP_URL ไปยัง public address ของคุณ ชุดซอฟต์แวร์ที่รวมมาด้วยประกอบด้วยเว็บแอป, PostgreSQL และ Prisma Studio เอกสารประกอบระบุ docker compose up -d calcom สำหรับการรันแอปเพียงอย่างเดียวโดยเชื่อมต่อกับฐานข้อมูลที่คุณโฮสต์ไว้ที่อื่น ซึ่งเป็นสิ่งที่คุณควรทำเมื่อการติดตั้งเสร็จสมบูรณ์แล้ว
ให้ใช้วิธี pull image มาใช้งาน อย่า build บน VPS คำแนะนำของโปรเจกต์ระบุให้ export NODE_OPTIONS="--max-old-space-size=16384" เมื่อ build จาก source ซึ่งต้องใช้หน่วยความจำ 16 GB สำหรับ Node เพียงอย่างเดียว สำหรับฮาร์ดแวร์ ARM ให้เพิ่ม suffix -arm ต่อท้าย tag ของ image โปรเจกต์ไม่ได้ระบุความต้องการขั้นต่ำสำหรับการรัน image ที่ build ไว้ล่วงหน้า ดังนั้นให้ถือว่า 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 ในขณะที่ container แสดงสถานะว่ากำลังทำงานอยู่ มักหมายความว่าการบูตครั้งแรกกำลังดำเนินการทำ database migration อยู่ ให้รอสักครู่และตรวจสอบ log ก่อนที่จะสรุปว่าระบบมีปัญหา webhooks ของ Cal.com จะทำงานทุกครั้งที่มีการยืนยันการจอง ดังนั้นการจองสามารถกระตุ้นระบบอัตโนมัติที่คุณใช้งานอยู่ได้ เช่น n8n instance ที่เข้าถึงได้ผ่าน HTTPS บน VPS ของคุณ
ซอฟต์แวร์หลักใช้สัญญาอนุญาต AGPLv3 โดยมีฟีเจอร์บางอย่างอยู่ใน enterprise directory ภายใต้สัญญาอนุญาตเชิงพาณิชย์แยกต่างหาก โปรดอ่านสัญญาอนุญาตนั้นก่อนที่จะสร้างกระบวนการทางธุรกิจที่มีค่าใช้จ่ายบนฟีเจอร์สำหรับทีม
Easy!Appointments บนเซิร์ฟเวอร์ขนาด 1 GB
ข้อกำหนดคือ Apache หรือ Nginx, PHP 8.2 หรือใหม่กว่า และ MySQL โดยมีอิมเมจอย่างเป็นทางการอยู่ที่ alextselegidis/easyappointments
คำเตือนประการแรก docker-compose.yml ใน repository นั้นเป็นสภาพแวดล้อมสำหรับการพัฒนา ซึ่งคาดหวังให้คุณเปิด shell ในคอนเทนเนอร์แล้วรัน npm install && composer install && npm start ไม่ใช่สำหรับการใช้งานจริง (deployment) ให้ใช้อิมเมจที่เผยแพร่ไว้แทน:
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/mysqlBASE_URL ต้องเป็นที่อยู่ HTTPS สาธารณะ หากตั้งค่าผิด ลิงก์การจองภายในอีเมลยืนยันจะชี้ไปยังโฮสต์ที่ไคลเอนต์ของคุณเข้าถึงไม่ได้ อิมเมจนี้ให้บริการ HTTP ปกติบนพอร์ต 80 โดยไม่มีใบรับรองในตัว ซึ่งเป็นเหตุผลว่าทำไมพอร์ตจึงถูกผูกไว้กับ 127.0.0.1 และให้ Nginx ทำหน้าที่ TLS termination อยู่ด้านหน้า หากไวยากรณ์ของ compose เป็นเรื่องใหม่สำหรับคุณ ให้เริ่มต้นที่ พื้นฐาน Docker Compose บน VPS แล้วค่อยกลับมาที่นี่
นี่เป็นตัวเลือกที่เบาที่สุดในบรรดาทั้งหมด สองคอนเทนเนอร์ซึ่งประกอบด้วยแอปพลิเคชัน PHP และ MySQL สามารถทำงานได้อย่างราบรื่นบน VPS ขนาด 1 GB แต่มีข้อจำกัดคือ Google Calendar เป็น backend ปฏิทินเพียงอย่างเดียว และอินเทอร์เฟซเป็นแผงควบคุมผู้ดูแลระบบแบบดั้งเดิม ไม่ใช่ขั้นตอนการจองที่ทันสมัย หากปฏิทินของคุณเป็น Microsoft 365, Fastmail หรือ Nextcloud ตัวเลือกนี้จะไม่สามารถใช้งานได้ตั้งแต่เริ่มต้น
Rallly สำหรับการทำโพลกลุ่ม
Rallly ตอบโจทย์ที่ต่างออกไป โดยไม่ได้เผยแพร่สถานะความพร้อมของคุณ แต่จะนำเสนอชุดเวลาที่เป็นตัวเลือกให้กลุ่มคนเข้ามาลงคะแนน ซึ่งเป็นสิ่งที่เหมาะสำหรับการประชุมคณะกรรมการ แต่ไม่เหมาะกับการใช้เป็นลิงก์สำหรับให้ลูกค้าจองเวลา
curl -fsSL https://get.rallly.co | bashโปรดอ่านสคริปต์ทุกครั้งก่อนที่จะส่งผ่าน (pipe) เข้าสู่ shell ให้เปลี่ยน bash เป็น less เพื่ออ่านการทำงานของสคริปต์ก่อนดำเนินการ การทำตามขั้นตอนด้วยตนเองจะให้ผลลัพธ์เดียวกันโดยที่คุณสามารถตรวจสอบแต่ละขั้นตอนได้:
git clone https://github.com/lukevella/rallly-selfhosted.git
cd rallly-selfhosted
./rallly.sh setup
./rallly.sh startข้อกำหนดตามเอกสารระบุว่าต้องมี RAM อย่างน้อย 2 GB, Docker 19.03 หรือใหม่กว่าที่มาพร้อมกับ Compose v2, พอร์ต 80 และ 443 ต้องว่าง และต้องมีโดเมนที่ชี้มายังเซิร์ฟเวอร์ สแต็กที่รวมมาให้ประกอบด้วย Traefik สำหรับ HTTPS, ตัวเว็บแอปพลิเคชัน, PostgreSQL และ Garage สำหรับการจัดเก็บข้อมูลแบบ object storage ที่รองรับ S3 ให้ตั้งค่า DOMAIN, SECRET_PASSWORD ที่มีความยาวอย่างน้อย 32 ตัวอักษร, SUPPORT_EMAIL และ INITIAL_ADMIN_EMAIL หากคุณใช้งาน reverse proxy อยู่แล้ว ให้ตั้งค่า PROXY_MODE=external และ WEB_PORT เพื่อให้ Traefik ไม่เข้ามาแทรกแซงการทำงาน หากคุณใช้งาน ระบบจัดเก็บข้อมูลแบบ object storage ที่รองรับ S3 ด้วยตนเองอย่าง MinIO อยู่แล้ว ให้ชี้ตัวแปร S3_* ไปยังระบบดังกล่าวและตัด container ของ Garage ออกได้เลย
SMTP เป็นสิ่งที่จำเป็นสำหรับระบบนี้ เนื่องจากกระบวนการเข้าสู่ระบบใช้ magic link หากไม่มี relay ที่ใช้งานได้ จะไม่มีใครสามารถเข้าสู่ระบบได้ รวมถึงบัญชีผู้ดูแลระบบที่คุณเพิ่งสร้างขึ้นมาด้วย นี่คือข้อดีของความล้มเหลวทางอีเมลในรูปแบบนี้ คือมันจะหยุดคุณไว้ตั้งแต่หน้าประตู แทนที่จะทำให้การจองของลูกค้าสูญหายไปในอีกสามสัปดาห์ให้หลัง
DayOtter ผู้เล่นรายใหม่ล่าสุด
DayOtter เป็นแพลตฟอร์มการจัดตารางเวลาภายใต้สัญญาอนุญาต AGPLv3 ที่มาพร้อมกับผู้ช่วยส่วนตัว การติดตั้งสำหรับใช้งานจริงทำได้ด้วยคำสั่งเดียว:
curl -fsSL https://raw.githubusercontent.com/Dayotter/dayotter/main/deploy/install.sh \
| sudo DAYOTTER_DOMAIN=cal.example.com bashโปรดอ่านคำสั่งดังกล่าวก่อนดำเนินการตามคำแนะนำข้างต้น ตัวติดตั้งจะทำการตั้งค่า Docker สร้างรหัสลับ (secrets) และเริ่มการทำงานของ stack ทั้งหมด ได้แก่ เว็บแอป Next.js, background worker สำหรับจัดการการแจ้งเตือน การซิงค์ปฏิทิน และ webhooks, PostgreSQL, Redis และ Caddy สำหรับการทำ HTTPS อัตโนมัติ
การรองรับปฏิทินของเครื่องมือนี้ครอบคลุมกว้างขวางที่สุดในบรรดาสี่ตัวเลือก ได้แก่ Google, Microsoft 365, Apple ผ่าน CalDAV และฟีด ICS (โดยมีข้อควรระวังเรื่อง ICS ตามที่ระบุไว้ข้างต้น) การเชื่อมต่อส่วนอื่นทั้งหมดต้องเลือกเปิดใช้งานผ่าน environment variables รวมถึง SMTP หรือ Resend สำหรับอีเมล, ANTHROPIC_API_KEY สำหรับผู้ช่วย, Twilio สำหรับ SMS และ Stripe สำหรับการชำระเงิน ระบบผู้ช่วยจะใช้หลักการยืนยันก่อนเสมอ: ระบบจะเสนอทางเลือกให้คุณอนุมัติ และจะไม่มีข้อมูลใดถูกเพิ่มลงในปฏิทินของคุณหากไม่ได้รับการตอบรับอย่างชัดเจน หากเว้นว่าง API key ไว้ ฟีเจอร์ส่วนนั้นของผลิตภัณฑ์จะไม่ทำงาน
สัญญาอนุญาตมีความชัดเจนสำหรับผู้ที่โฮสต์ด้วยตนเอง โดยแกนหลักเป็น AGPLv3 และมีไดเรกทอรี ee/ ที่บรรจุสัญญาอนุญาตเชิงพาณิชย์สำหรับระบบคลาวด์เท่านั้น ซึ่งจะไม่มีผลใดๆ เว้นแต่จะมีการตั้งค่า DAYOTTER_CLOUD=1 นั่นหมายความว่าฟีเจอร์สำหรับทีมที่เรียกเก็บเงินในแผน hosted ที่ราคา 9 ดอลลาร์ต่อที่นั่งต่อเดือน (ข้อมูล ณ เดือนสิงหาคม 2026) สามารถใช้งานบนเซิร์ฟเวอร์ของคุณเองได้
อย่างไรก็ตาม นี่เป็น stack ที่หนักที่สุดและเป็นโปรเจกต์ที่ใหม่ที่สุดในกลุ่มนี้ แนะนำให้รันควบคู่ไปกับลิงก์การจองเดิมของคุณเป็นเวลาสองสัปดาห์ รับการจองจริงผ่านทั้งสองระบบ และตรวจสอบ log ของ worker ก่อนที่จะย้ายลูกค้าของคุณมาใช้งานอย่างเต็มตัว
ต้นทุนที่แท้จริงของแต่ละ stack
จำนวน container เป็นตัวชี้วัดที่ตรงไปตรงมาที่สุดว่า stack หนึ่งจะใช้ทรัพยากรจาก VPS ขนาดเล็กไปเท่าใด เนื่องจากแต่ละบริการมีฐานการใช้หน่วยความจำของตัวเอง นี่คือจำนวน container จาก Docker stack ที่แต่ละโปรเจกต์เผยแพร่อย่างเป็นทางการ ข้อมูล ณ เดือนสิงหาคม 2026
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 ใช้ container จำนวน 2 และสามารถทำงานได้บน 1 GB ส่วน stack ที่มาพร้อมกับ Rallly มีจำนวน 4 ซึ่งเอกสารระบุว่าต้องการหน่วยความจำ 2 GB ตัวติดตั้งของ DayOtter เรียกใช้งาน container จำนวน 5 จึงเป็นเหตุผลว่าทำไมมันถึงต้องการ VPS ขนาดใหญ่ที่สุดในบรรดา 4 รายการที่นำมาเปรียบเทียบนี้ ทั้ง Cal.com และ DayOtter ไม่ได้ระบุตัวเลขหน่วยความจำขั้นต่ำไว้ ดังนั้นผมจึงกำหนดที่ 2 GB เป็นจุดเริ่มต้นสำหรับทั้งคู่แทนที่จะเป็นตัวเลขที่ได้รับการรับรอง
จำนวน container สองรายการนี้จะลดลงหากคุณมีโครงสร้างพื้นฐานใช้งานอยู่แล้ว Container ประเภท Traefik และ Garage ของ Rallly จะถูกตัดออกเมื่อคุณเชื่อมต่อกับ proxy และ object storage ของคุณเอง ส่วน Prisma Studio ของ Cal.com เป็นเครื่องมือสำหรับพัฒนาที่คุณไม่ควรเปิดทิ้งไว้บนเซิร์ฟเวอร์สาธารณะ
ทางเลือกสำหรับ Calendly แบบ self-hosted ที่คุณควรเลือก
ที่ปรึกษาอิสระควรเลือกใช้ Cal.com นี่เป็นโปรเจกต์เดียวในรายการนี้ที่รวมหน้าจองนัดหมายที่ผู้คนคุ้นเคยเข้ากับอิมเมจที่สร้างไว้ล่วงหน้า ซึ่งช่วยให้ VPS ของคุณไม่ต้องเสียเวลา build ด้วย Node และยังมีเส้นทาง CalDAV สำหรับผู้ที่ไม่ได้ใช้ปฏิทินของ Google หรือ Microsoft การดูแลรักษาฐานข้อมูล PostgreSQL หนึ่งตัวและคอนเทนเนอร์แอปพลิเคชันหนึ่งตัวเป็นภาระที่คุณสามารถรับมือได้นานหลายปี ให้เผื่อเวลาไว้หนึ่งช่วงบ่ายสำหรับการตั้งค่า OAuth client และ mail relay และโปรดทราบว่าแอป CalDAV ยังอยู่ในสถานะเบต้า ดังนั้นควรทดสอบการจองจริงให้ครบถ้วนทุกขั้นตอนก่อนที่จะเผยแพร่ลิงก์
ทีมขนาดเล็กควรพิจารณา DayOtter ฟีเจอร์การกระจายงานแบบ weighted round robin และการจองแบบกลุ่มถูกรวมอยู่ในแกนหลักที่เป็น AGPLv3 ดังนั้นการทำ self-hosting จะทำให้คุณได้รับฟีเจอร์ที่บริการแบบเช่าใช้ต้องเสียค่าธรรมเนียมรายที่นั่ง และ worker process ยังถูกสร้างมาเพื่อรองรับการแจ้งเตือนและ webhooks ที่ทีมงานจำเป็นต้องใช้งานจริง ข้อแลกเปลี่ยนคือเรื่องความเสถียร เนื่องจากเป็นโปรเจกต์ที่ใหม่ที่สุดในรายการนี้ คุณควรเปิดใช้งานควบคู่ไปกับระบบเดิมก่อน และเก็บลิงก์เก่าไว้จนกว่าคุณจะตรวจสอบการจองผ่านระบบใหม่ได้ครบหนึ่งเดือนเต็ม
สำหรับกรณีเฉพาะทางอีกสองกรณี หากคุณต้องการเพียงแค่แบบสำรวจเพื่อหานัดหมายที่ทุกคนในกลุ่มว่างตรงกัน ให้ติดตั้ง Rallly แล้วจบแค่นั้น หากคุณมี VPS ขนาด 1 GB, ใช้งาน Google Calendar เป็นหลัก และต้องการสิ่งที่เล็กที่สุดที่สามารถรับการจองได้ Easy!Appointments จะเป็นตัวเลือกที่ใช้งานได้ยาวนานกว่าตัวเลือกอื่นที่ซับซ้อนกว่าที่คุณอาจนำมาติดตั้งบนเซิร์ฟเวอร์นั้น สำหรับคำถามในวงกว้างว่ามีอะไรอื่นที่คุ้มค่าแก่การติดตั้งบนเซิร์ฟเวอร์เดียวกันอีกบ้าง โปรดดู สิ่งที่คุ้มค่าแก่การทำ self-hosting ในปี 2026
FAQ
ฉันสามารถรันหน้าจองคิวแบบ self-hosted โดยไม่มีชื่อโดเมนได้หรือไม่
ไม่ได้ แอปพลิเคชันเหล่านี้จะเขียน URL สาธารณะลงในลิงก์ภายในอีเมลยืนยัน และทั้ง Google กับ Microsoft จะตรวจสอบค่า OAuth redirect URI เทียบกับค่าเดียวกันนี้ ดังนั้นการใช้เพียง IP address จะทำให้คุณพบกับ redirect_uri_mismatch ที่หน้าจอขอสิทธิ์ นอกจากนี้ Let’s Encrypt จะไม่ออกใบรับรองให้สำหรับ IP address ทำให้หน้าเว็บต้องโหลดผ่าน HTTP ธรรมดาและเบราว์เซอร์จะระบุว่าฟอร์มดังกล่าวไม่ปลอดภัย ให้ซื้อโดเมนก่อน ชี้ A record ไปยัง VPS แล้วจึงค่อยติดตั้ง
ทำไมอีเมลยืนยันการจองของฉันจึงไม่เคยส่งถึง
สาเหตุเกือบทั้งหมดเกิดจากการที่เซิร์ฟเวอร์พยายามส่งอีเมลด้วยตัวเอง ผู้ให้บริการ VPS ส่วนใหญ่จะบล็อกพอร์ต 25 สำหรับขาออกในบัญชีใหม่ ทำให้การเชื่อมต่อค้าง และแม้ในกรณีที่พอร์ตเปิดอยู่ ที่อยู่ IP ใหม่ก็ไม่มีชื่อเสียงในการส่งอีเมล ทำให้ผู้รับรายใหญ่ปฏิเสธการรับ ให้ตั้งค่าแอปพลิเคชันให้ส่งผ่าน transactional mail relay ที่พอร์ต 587 ตรวจสอบว่าพอร์ตสามารถเข้าถึงได้ด้วย nc -vz -w 5 "$SMTP_HOST" 587 จากนั้นจึงประกาศ SPF และ DKIM record ที่ทาง relay ให้มา หากคุณใช้งาน Cal.com ให้ตรวจสอบว่าคุณได้เปลี่ยนค่าเริ่มต้น EMAIL_SERVER_HOST=localhost และ EMAIL_SERVER_PORT=1025 ที่ติดมากับซอฟต์แวร์แล้ว ซึ่งค่าเหล่านั้นชี้ไปยังกล่องจดหมายสำหรับการพัฒนาในเครื่อง
Cal.com แบบ self-hosted ซิงค์กับ CalDAV หรือซิงค์ได้แค่ Google
ซิงค์ได้ทั้งสองอย่างแต่มีความเสถียรต่างกัน แอป CalDAV ถูกระบุว่าเป็นรุ่นเบต้าและผ่านการตรวจสอบกับเซิร์ฟเวอร์ต่างๆ เช่น Baikal, Radicale, Nextcloud และ Kerio Connect ส่วน Apple iCloud สามารถใช้งานผ่านแอปนี้ได้โดยใช้ app-specific password สำหรับ Google Calendar และ Microsoft 365 นั้นซิงค์ได้ทั้งสองทิศทาง แต่ในการติดตั้งแบบ self-hosted คุณต้องสร้าง OAuth client ของคุณเองและระบุค่าผ่าน GOOGLE_API_CREDENTIALS เนื่องจาก credentials ของบริการที่โฮสต์ไว้ไม่ได้รวมอยู่ในซอร์สโค้ด
ทำไมการซิงค์ Google Calendar ของฉันจึงหยุดทำงานหลังจากผ่านไปหนึ่งสัปดาห์
เพราะโปรเจกต์ใน Google Cloud ยังคงอยู่ในสถานะการเผยแพร่แบบ Testing โดย Google จะออก refresh token ให้กับแอปในสถานะนี้ซึ่งมีอายุการใช้งานเพียงเจ็ดวัน ดังนั้นการเชื่อมต่อจะทำงานได้ปกติในช่วงแรกแล้วจึงหยุดทำงานเมื่อถึงเวลาต้องรีเฟรช token ครั้งถัดไป และ log ของแอปพลิเคชันจะแสดง invalid_grant ให้เปลี่ยนสถานะหน้าจอ OAuth consent เป็น In production แล้วเชื่อมต่อปฏิทินใหม่อีกครั้ง การเชื่อมต่อใหม่โดยไม่เปลี่ยนสถานะจะช่วยยืดเวลาให้คุณได้อีกเพียงเจ็ดวันเท่านั้น
แอปใดบ้างที่สามารถรันบน VPS ขนาด 1 GB ได้
Easy!Appointments สามารถรันได้เนื่องจากเป็นแอปพลิเคชัน PHP ร่วมกับ MySQL ส่วน Rallly ระบุความต้องการขั้นต่ำไว้ที่ 2 GB และ stack ที่มาพร้อมกันนั้นรันถึงสี่บริการ สำหรับ Cal.com และ DayOtter ไม่ได้ระบุความต้องการขั้นต่ำไว้ แต่เนื่องจากเป็นแอปพลิเคชัน Next.js ที่ต้องใช้ PostgreSQL และในกรณีของ DayOtter ยังต้องใช้ Redis กับ worker process อีกด้วย คุณจึงควรวางแผนใช้ RAM ขนาด 2 GB ขึ้นไป ห้าม build Cal.com จากซอร์สโค้ดบนเครื่องขนาดเล็ก เพราะคำแนะนำในการ build ของโปรเจกต์เองระบุว่าต้องใช้ Node heap ขนาด 16 GB ดังนั้นให้ดึง prebuilt image มาใช้งานแทน