วิธีติดตั้ง CalDAV Server บน VPS ด้วย Radicale
สร้างเซิร์ฟเวอร์ปฏิทินส่วนตัวบน VPS เพื่อซิงค์ข้อมูลข้ามอุปกรณ์โดยไม่ต้องพึ่ง Google เรียนรู้วิธีตั้งค่า Radicale พร้อมระบบ TLS และการทำ Service Discovery อย่างละเอียด
สิ่งที่คุณกำลังสร้าง
ปฏิทินแบบ self-hosted คือเซิร์ฟเวอร์ CalDAV หนึ่งตัวบน VPS ที่คุณควบคุม โดยอยู่หลัง TLS และมีการเข้าสู่ระบบแยกรายบุคคล โทรศัพท์ในกระเป๋าและแล็ปท็อปบนโต๊ะของคุณจะแสดงกิจกรรมเดียวกัน รวมถึงแล็ปท็อปของคู่ของคุณด้วย โดยไม่มีบัญชี Google เข้ามาเกี่ยวข้อง
งานนี้แตกต่างจาก หน้าจองนัดหมายแบบ self-hosted หน้าจองนัดหมายมีไว้สำหรับบุคคลภายนอก โดยจะเผยแพร่ช่วงเวลาว่างของคุณและให้ผู้อื่นเข้ามาจองได้ แต่เซิร์ฟเวอร์ปฏิทินมีไว้สำหรับอุปกรณ์ของคุณเอง โดยทำหน้าที่จัดเก็บกิจกรรมและทำให้ไคลเอนต์ทุกเครื่องซิงค์ข้อมูลตรงกัน ผู้คนมักใช้งานทั้งสองอย่างควบคู่กัน โดยเครื่องมือจองนัดหมายจะอ่านสถานะความว่างจากเซิร์ฟเวอร์ CalDAV ที่คุณสร้างขึ้นที่นี่
การติดตั้งมีขนาดเล็ก Radicale เป็นแพ็กเกจ Python หนึ่งตัวและมีค่าคอนฟิกประมาณสิบบรรทัด สิ่งที่จะตัดสินว่าระบบจะใช้งานได้ยาวนานเกินเดือนแรกหรือไม่ คือ TLS, การค้นหาบริการ (discovery), การจัดการคอลเลกชันรายผู้ใช้ และการสำรองข้อมูล ซึ่งเนื้อหาส่วนใหญ่ด้านล่างจะเน้นไปที่หัวข้อเหล่านี้
CalDAV คืออะไร และทำไมจึงสำคัญ?
CalDAV คือการซิงค์ปฏิทินผ่านโปรโตคอล HTTP โดยถูกกำหนดไว้ใน RFC 4791 ในฐานะส่วนขยายของ WebDAV (Web Distributed Authoring and Versioning ซึ่งเป็นชุดคำสั่ง HTTP เพิ่มเติมที่กำหนดไว้ใน RFC 4918) ปฏิทินเปรียบเสมือนคอลเลกชันที่มีพฤติกรรมคล้ายกับไดเรกทอรี โดยเหตุการณ์หนึ่งรายการคือไฟล์หนึ่งไฟล์ที่อยู่ภายใน ซึ่งเขียนด้วยรูปแบบข้อความ iCalendar (RFC 5545) ซึ่งเป็นรูปแบบเดียวกับ .ics ที่แนบมาในอีเมลของคุณ
ไคลเอนต์จะใช้ HTTP ปกติร่วมกับคำสั่งที่เพิ่มเข้ามาบางส่วน PROPFIND ใช้เพื่อสอบถามว่ามีข้อมูลอะไรอยู่ที่นี่และมีคุณสมบัติอย่างไร REPORT ใช้เพื่อขอข้อมูลเฉพาะส่วนที่กรองแล้ว เช่น เหตุการณ์ทั้งหมดในช่วงวันที่ที่กำหนด PUT ใช้สำหรับเขียนเหตุการณ์ลงไปหนึ่งรายการ และ DELETE ใช้สำหรับลบเหตุการณ์นั้นออก เหตุการณ์ทุกรายการจะมีบรรทัด UID กำกับไว้ ซึ่งตัวระบุนี้เองที่ทำให้สองอุปกรณ์ตกลงกันได้ว่ากำลังดูเหตุการณ์เดียวกันอยู่ ไม่ใช่แค่สำเนาของข้อมูล
ความสามารถในการย้ายข้อมูล (Portability) คือผลตอบแทนที่ได้รับ และเป็นเหตุผลทั้งหมดที่ควรให้ความสำคัญกับเรื่องนี้ ทั้ง iOS, macOS, Thunderbird, Evolution และ Android ผ่านทาง DAVx⁵ ต่างก็รองรับ CalDAV ข้อมูลของคุณจึงไม่ได้ถูกผูกติดอยู่กับเซิร์ฟเวอร์ที่คุณเลือกใช้ในวันนี้ คุณสามารถย้ายไฟล์ไปยังเซิร์ฟเวอร์ CalDAV อื่น แล้วชี้ไคลเอนต์ไปยังชื่อโฮสต์ใหม่ได้ทันทีโดยไม่ต้องเปลี่ยนแปลงสิ่งอื่นใด
CardDAV ก็ทำงานในลักษณะเดียวกัน โดยเป็นแนวคิดเดียวกันสำหรับรายชื่อผู้ติดต่อ ซึ่งกำหนดไว้ใน RFC 6352 และจัดเก็บไฟล์ vCard แทนที่จะเป็นเหตุการณ์ เซิร์ฟเวอร์ทุกตัวที่ระบุไว้ด้านล่างให้บริการทั้งสองโปรโตคอลจากบัญชีเดียวกัน ดังนั้นเมื่อคุณตั้งค่าปฏิทินสำเร็จแล้ว การใช้งานสมุดรายชื่อก็เป็นเพียงการทำเครื่องหมายในช่องเลือกเท่านั้น
คุณควรเลือกใช้ CalDAV server ตัวใด?
Radicale เป็นตัวเลือกที่เล็กที่สุดที่ใช้งานได้จริง เขียนด้วย Python ไม่ต้องใช้ฐานข้อมูล และจัดเก็บข้อมูลเป็นไฟล์ธรรมดาในโฟลเดอร์ คู่มือนี้เลือกใช้ Radicale เพราะปฏิทินสำหรับใช้งานในครัวเรือนไม่จำเป็นต้องมีฟีเจอร์ซับซ้อน และมีโอกาสเกิดข้อผิดพลาดน้อยมากในช่วงเวลาตีสาม
Baikal เป็นตัวเลือกที่มีหน้าเว็บสำหรับจัดการ (admin panel) ทำงานบน PHP และไลบรารี sabre/dav โดยเก็บข้อมูลผู้ใช้และปฏิทินไว้ใน SQLite หรือ MySQL ช่วยให้คุณสามารถเพิ่มผู้ใช้งานผ่านเบราว์เซอร์ได้โดยไม่ต้องใช้ command line เลือกใช้ตัวนี้หากมีการเพิ่มหรือลบบัญชีผู้ใช้อยู่บ่อยครั้ง
Nextcloud เหมาะสมเมื่อปฏิทินเป็นเพียงฟีเจอร์หนึ่งในหลายๆ อย่างที่คุณต้องการ คุณจะได้รับทั้งปฏิทิน รายชื่อผู้ติดต่อ ไฟล์ และแอปบนมือถือ แต่ต้องแลกมาด้วยการติดตั้ง PHP-FPM, ฐานข้อมูล และตัวรัน background job หากคุณรู้สึกว่านั่นเป็นภาระหนักเกินความจำเป็น ทางเลือกอื่นที่เบากว่า Nextcloud จะช่วยให้คุณเห็นภาพรวมของตัวเลือกอื่น และ การซิงค์ไฟล์ด้วยตนเอง จะครอบคลุมอีกครึ่งหนึ่งของเหตุผลที่ผู้คนเลือกติดตั้ง Nextcloud
DAViCal เป็นตัวเลือกที่ใช้ PostgreSQL มาอย่างยาวนาน ควรพิจารณาตัวเลือกนี้ก็ต่อเมื่อคุณใช้งาน PostgreSQL อยู่แล้วและต้องการเก็บข้อมูลปฏิทินไว้ในฐานข้อมูลดังกล่าวเท่านั้น
การติดตั้ง Radicale บน Ubuntu 24.04
Radicale 3.8.3 ซึ่งถูกแท็กไว้เมื่อวันที่ 8 ตุลาคม 2026 เป็นเวอร์ชันล่าสุด ณ วันที่ 11 ตุลาคม 2026 ให้ติดตั้งลงใน virtual environment ของตนเอง พร้อมกับ bcrypt ซึ่งเป็นส่วนเสริมที่ไฟล์รหัสผ่านด้านล่างจำเป็นต้องใช้
sudo apt update
sudo apt install -y python3-venv apache2-utils nginx
sudo useradd --system --user-group --home-dir / --shell /usr/sbin/nologin radicale
sudo install -d -o radicale -g radicale -m 750 /var/lib/radicale/collections
sudo install -d -m 750 -o root -g radicale /etc/radicale
sudo python3 -m venv /opt/radicale/venv
sudo /opt/radicale/venv/bin/pip install --upgrade 'radicale[bcrypt]'การใช้ virtual environment ไม่ใช่แค่เรื่องของรูปแบบ แต่การใช้ sudo pip install radicale ลงใน Python ของระบบจะหยุดทำงานด้วย error: externally-managed-environment เนื่องจาก Ubuntu กำหนดให้ Python ของระบบอยู่ภายใต้การจัดการของ apt เพื่อป้องกันไม่ให้ pip เขียนทับไฟล์ที่ติดตั้งผ่านแพ็กเกจ
เขียนไฟล์ /etc/radicale/config:
[server]
hosts = 127.0.0.1:5232
[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = autodetect
[storage]
filesystem_folder = /var/lib/radicale/collectionshosts ผูกไว้กับ loopback โดยเจตนา เพื่อให้ nginx ทำหน้าที่จัดการ TLS และส่งต่อคำขอไปยังพอร์ตดังกล่าว Radicale จึงไม่ต้องเผชิญกับอินเทอร์เน็ตโดยตรง ตัวอย่าง upstream ของ 0.0.0.0:5232 นั้นเผยแพร่บริการที่ไม่มีการเข้ารหัสซึ่งรับรหัสผ่าน ซึ่งเป็นข้อผิดพลาดเพียงจุดเดียวที่สำคัญในที่นี้
ถัดไปคือเรื่องบัญชีผู้ใช้ -B เลือกใช้ bcrypt ซึ่ง Radicale จะอ่านด้วย htpasswd_encryption = autodetect เมื่อมีโมดูล bcrypt จากการติดตั้ง radicale[bcrypt] ด้านบน:
sudo htpasswd -B -c /etc/radicale/users you
sudo htpasswd -B /etc/radicale/users partner
sudo chown root:radicale /etc/radicale/users
sudo chmod 640 /etc/radicale/users-c จะสร้างไฟล์ใหม่และล้างข้อมูลเดิมทิ้งทั้งหมด ให้ใช้คำสั่งนี้สำหรับผู้ใช้คนแรกเท่านั้น การรัน htpasswd -B -c อีกครั้งในภายหลังจะลบบัญชีทั้งหมดที่เพิ่มเข้ามาหลังจากคนแรกทิ้งไป และอาการที่พบคือผู้ใช้คนหนึ่งจะซิงค์ข้อมูลได้ปกติ ในขณะที่คนอื่นจะติดหน้าต่างถามรหัสผ่านซ้ำไม่สิ้นสุด SHA-512 crypt (-5) ไม่ใช่ตัวเลือกในที่นี้ เนื่องจาก htpasswd เพิ่งรองรับ SHA-2 ใน Apache 2.4.59 แต่ Ubuntu 24.04 มาพร้อมกับ apache2-utils 2.4.58 ซึ่งคำสั่ง htpasswd ไม่มีแฟล็ก -5
สร้าง /etc/systemd/system/radicale.service โดยปรับปรุงจาก unit ในเอกสารประกอบของ Radicale:
[Unit]
Description=CalDAV and CardDAV server
After=network.target
Requires=network.target
[Service]
ExecStart=/opt/radicale/venv/bin/python -m radicale
Restart=on-failure
User=radicale
UMask=0027
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
ReadWritePaths=/var/lib/radicale/
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now radicale
curl -i http://127.0.0.1:5232/ผลลัพธ์ที่ถูกต้องคือ 401 Unauthorized พร้อมส่วนหัว WWW-Authenticate: บริการกำลังทำงานและเปิดใช้งานการยืนยันตัวตนแล้ว Connection refused หมายความว่าไม่มีบริการใดฟังอยู่ที่แอดเดรสนี้ ซึ่งมักเกิดจากบริการไม่สามารถเริ่มทำงานได้หรือหยุดทำงานไปแล้ว และ journalctl -u radicale -n 50 จะระบุตัวเลือกที่ถูกปฏิเสธ ProtectSystem=strict จะทำการ mount ระบบไฟล์แบบอ่านได้อย่างเดียวสำหรับบริการนี้ ดังนั้น ReadWritePaths=/var/lib/radicale/ จึงเป็นบรรทัดที่อนุญาตให้บันทึกเหตุการณ์ได้ หากลบบรรทัดนี้ออก การอ่านข้อมูลจะยังคงทำงานได้ปกติ แต่การเขียนข้อมูลทั้งหมดจะล้มเหลว
TLS เป็นสิ่งจำเป็น เพราะไคลเอนต์ปฏิเสธการเชื่อมต่อแบบ plaintext
การตั้งค่านี้ใช้การยืนยันตัวตนด้วย HTTP Basic ซึ่งเป็นรูปแบบเดียวที่ Radicale รองรับ โดย Basic จะส่ง user:password ในรูปแบบ base64 ไปกับทุกคำขอ Base64 เป็นเพียงการเข้ารหัส ไม่ใช่การเข้ารหัสลับ (encryption) หากส่งผ่าน HTTP ธรรมดา รหัสผ่านของคุณจะถูกส่งผ่านทุกเครือข่ายระหว่างโทรศัพท์กับเซิร์ฟเวอร์ตลอดเวลาที่มีการซิงค์
ไคลเอนต์จะบังคับใช้ข้อกำหนดนี้ให้คุณ เอกสารของ Radicale ระบุว่า macOS Calendar.app อาจปฏิเสธการส่งข้อมูลยืนยันตัวตนผ่าน HTTP ที่ไม่ปลอดภัยโดยไม่แจ้งเตือน และ iOS ก็มีพฤติกรรมเช่นเดียวกัน บัญชีจะดูเหมือนถูกตั้งค่าไว้แล้วแต่ไม่เคยซิงค์ข้อมูล โดยไม่มีข้อความแสดงข้อผิดพลาดให้ตรวจสอบ
ให้ชี้ A record ของ cal.example.com ไปที่ VPS ก่อน เนื่องจากหน่วยงานออกใบรับรอง (certificate authority) จะตรวจสอบจุดนี้ จากนั้นสร้าง /etc/nginx/sites-available/cal.example.com:
server {
listen 80;
server_name cal.example.com;
location / {
proxy_pass http://localhost:5232/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_pass_header Authorization;
}
location = /.well-known/caldav { return 301 https://$host/; }
location = /.well-known/carddav { return 301 https://$host/; }
}บรรทัด proxy header ทั้งสี่บรรทัดมาจากเอกสารของ Radicale ให้คงค่าไว้ตามนั้น
sudo ln -s /etc/nginx/sites-available/cal.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d cal.example.com
curl -i -u you https://cal.example.com/nginx -t จะแสดงผล syntax is ok และ test is successful ให้ทำการ reload หลังจากที่คำสั่งทำงานสำเร็จเท่านั้น เนื่องจากการ reload ขณะที่ไฟล์ config มีข้อผิดพลาดจะทำให้ระบบยังคงใช้ config เดิมและปิดบังข้อผิดพลาดไว้จนกว่าจะมีการ restart ครั้งถัดไป Certbot จะแก้ไขไฟล์ site โดยตรง โดยจะติดตั้งใบรับรอง สลับบล็อกไปที่พอร์ต 443 และเพิ่มการ redirect จากพอร์ต 80 คำสั่ง curl สุดท้ายจะถามรหัสผ่านและควรแสดงผลเป็น 200 ซึ่งเป็นหน้าเว็บอินเทอร์เฟซของ Radicale เอง หากพบ 502 Bad Gateway หมายความว่า nginx ทำงานอยู่แต่ไม่ได้รับคำตอบที่ถูกต้องจากพอร์ต 5232 ซึ่งสาเหตุที่พบบ่อยคือ Radicale ไม่ได้เปิดฟัง (listening) ที่พอร์ตดังกล่าว
เหตุใดการเพิ่มบัญชีบนโทรศัพท์จึงล้มเหลว
เนื่องจากกระบวนการค้นหา (discovery) RFC 6764 อธิบายวิธีที่ไคลเอนต์แปลงชื่อโฮสต์ให้เป็น URL ของปฏิทิน โดยไคลเอนต์จะค้นหา _caldavs._tcp SRV record จากนั้นจะร้องขอ https://cal.example.com/.well-known/caldav และคาดหวังการเปลี่ยนเส้นทาง (redirect) ไปยัง DAV root จากจุดนั้นไคลเอนต์จะร้องขอ current-user-principal ตามด้วย calendar-home-set ของ principal นั้น แล้วจึงจะมองเห็นปฏิทินของคุณ โทรศัพท์ให้ช่องกรอกข้อมูลเซิร์ฟเวอร์เพียงช่องเดียว ดังนั้นทุกขั้นตอนจึงต้องทำงานได้โดยอัตโนมัติ
curl -sI https://cal.example.com/.well-known/caldavคำตอบที่ถูกต้องคือ HTTP/2 301 พร้อมกับส่วนหัว location: https://cal.example.com/ การที่ 404 ปรากฏขึ้นที่นั่นเป็นสาเหตุทั่วไปที่ทำให้ iOS แจ้งว่าไม่สามารถตรวจสอบข้อมูลบัญชีได้ ในขณะที่ Thunderbird บนเครือข่ายเดียวกันกลับใช้งานได้ เนื่องจาก Thunderbird ใช้ URL เต็มที่คุณพิมพ์ลงไป จึงไม่จำเป็นต้องอาศัยการเปลี่ยนเส้นทาง
เป้าหมายของการเปลี่ยนเส้นทางขึ้นอยู่กับเซิร์ฟเวอร์ Radicale ที่ให้บริการที่ root ของไซต์จะเปลี่ยนเส้นทางไปยัง / และตัว Radicale เองจะตอบกลับ path นั้นด้วยสถานะ 301 เมื่อคำขอไปถึง ดังนั้นกฎของ nginx จึงทำให้การเปลี่ยนเส้นทางมีความชัดเจนแทนที่จะสร้างขึ้นใหม่ Baikal มีตัวอย่างกฎที่เปลี่ยนเส้นทางไปยัง /dav.php ด้วยสถานะ 308 ส่วน Nextcloud จะเปลี่ยนเส้นทางไปยัง /remote.php/dav/
สร้างปฏิทินและแชร์กับคู่ของคุณ
ไคลเอนต์จำนวนมากไม่สามารถสร้างปฏิทินได้ ทำได้เพียงแค่สมัครสมาชิก (subscribe) เท่านั้น ให้เปิด https://cal.example.com/ ในเบราว์เซอร์ ล็อกอินด้วย you แล้วสร้างปฏิทินที่นั่น บนดิสก์ปฏิทินจะถูกจัดเก็บไว้ภายใต้ /var/lib/radicale/collections/collection-root/you/ โดยมีตัวระบุที่ถูกสร้างขึ้นอัตโนมัติเป็นชื่อโฟลเดอร์
ระบบจัดการสิทธิ์เริ่มต้นของ Radicale คือ owner_only ซึ่งหมายความว่าบัญชีที่ผ่านการตรวจสอบสิทธิ์แล้วจะสามารถอ่านและเขียนคอลเลกชันของตนเองได้ภายใต้ /USERNAME/ เท่านั้น สำหรับครัวเรือนส่วนใหญ่ นี่คือการตั้งค่าที่เหมาะสมที่สุด และวิธีที่ง่ายที่สุดในการแชร์ปฏิทินคือการใช้บัญชีที่สาม ให้สร้าง household ด้วย htpasswd สร้างปฏิทินที่แชร์ไว้ภายใต้การล็อกอินนั้น แล้วเพิ่มปฏิทินดังกล่าวในแต่ละอุปกรณ์ในฐานะบัญชี CalDAV บัญชีที่สอง วิธีนี้ใช้งานได้กับทุกไคลเอนต์รวมถึง iOS เพราะปฏิทินนั้นตั้งอยู่ในโฮมไดเรกทอรีของบัญชีนั้นโดยตรง
หากคุณต้องการการควบคุมที่ละเอียดขึ้น ให้เปลี่ยนไปใช้การกำหนดสิทธิ์แบบอิงตามกฎ (rule-based rights) โดยเพิ่มข้อความนี้ลงใน /etc/radicale/config:
[rights]
type = from_file
file = /etc/radicale/rightsจากนั้นให้ตั้งค่า /etc/radicale/rights โดยอ้างอิงจากตัวอย่างในเอกสารประกอบของ Radicale:
[root]
user: .+
collection:
permissions: R
[principal]
user: .+
collection: {user}
permissions: RW
[own-calendars]
user: .+
collection: {user}/[^/]+
permissions: rw
[shared-household]
user: you|partner
collection: you/2f0a9c1e-1f4c-4c2b-9a1b-0d2f7a5c9e11
permissions: rwตัวอักษรพิมพ์ใหญ่และพิมพ์เล็กมีความหมายต่างกัน R และ W ใช้สำหรับอ่านและเขียนคอลเลกชันที่ไม่ใช่ปฏิทินหรือสมุดรายชื่อ ซึ่งเป็นสิ่งที่โฟลเดอร์หลัก (principal folder) เป็นอยู่ ส่วน r และ w ใช้สำหรับอ่านและเขียนตัวปฏิทินโดยตรง ให้แทนที่ตัวระบุนั้นด้วยชื่อโฟลเดอร์จริงของปฏิทินของคุณจากพาธการจัดเก็บข้อมูลที่ระบุไว้ข้างต้น
ข้อจำกัดที่ต้องทราบคือ ไคลเอนต์ที่อ่านเฉพาะโฮมไดเรกทอรีของปฏิทินจะไม่แสดงปฏิทินที่อยู่ในพาธของผู้ใช้อื่น เนื่องจากกระบวนการค้นหา (discovery) จะไม่เข้าไปถึงที่นั่น Thunderbird และ DAVx⁵ สามารถเพิ่มปฏิทินดังกล่าวได้โดยใช้ URL เต็ม แต่ iOS ไม่สามารถทำได้ นี่คือเหตุผลที่รูปแบบการใช้บัญชีแชร์เป็นวิธีที่ใช้งานได้เสมอในทุกกรณี
ตั้งค่าไคลเอนต์ เพราะนี่คือจุดที่การทำ self-hosted calendar มักจะล้มเหลว
iPhone และ iPad เปิด Settings จากนั้นไปที่ Calendar (ใน iOS เวอร์ชันใหม่จะอยู่ในหัวข้อ Apps) เลือก Calendar Accounts, Add Account, Other และ Add CalDAV Account ในช่อง Server ให้ใส่ cal.example.com ตามด้วยชื่อผู้ใช้และรหัสผ่าน ส่วน Description เป็นเพียงป้ายกำกับเท่านั้น หากระบบไม่ยอมบันทึก ให้เปิดบัญชีนั้นขึ้นมาอีกครั้ง มุมมองขั้นสูง (advanced view) จะแสดงตัวเลือก Use SSL, พอร์ต และ URL บัญชีแบบเต็ม การวาง URL โดยตรงจะช่วยข้ามขั้นตอนการค้นหาอัตโนมัติไปได้เลย
Android ระบบไม่มีไคลเอนต์ CalDAV ในตัว ให้ติดตั้ง DAVx⁵ จาก F-Droid หรือ Google Play จากนั้นเพิ่มบัญชีโดยใช้ URL หลักคือ https://cal.example.com/ พร้อมชื่อผู้ใช้ของคุณ แล้วเลือกปฏิทินที่ต้องการ DAVx⁵ จะเขียนข้อมูลลงในตัวจัดการปฏิทินของ Android ทำให้กิจกรรมต่างๆ ปรากฏในแอปปฏิทินที่คุณใช้งานอยู่แล้ว
Thunderbird เลือก New Calendar, On the Network จากนั้นใส่ชื่อผู้ใช้และตำแหน่ง https://cal.example.com/ ระบบจะแสดงรายการที่พบและถามว่าต้องการเพิ่มปฏิทินใดบ้าง
macOS ไปที่ System Settings, Internet Accounts, Add Other Account, CalDAV ตั้งค่า Account Type เป็น Manual จากนั้นใส่ชื่อผู้ใช้ รหัสผ่าน และที่อยู่เซิร์ฟเวอร์ตามที่กำหนด
CalDAV เป็นโปรโตคอลแบบ polling ในข้อกำหนดไม่มีระบบ push ดังนั้นกิจกรรมที่คุณเพิ่มบนแล็ปท็อปจะไปปรากฏบนโทรศัพท์ในการซิงค์รอบถัดไป ไม่ใช่ในวินาทีนั้นทันที ให้ตั้งค่าช่วงเวลาการซิงค์ในแต่ละไคลเอนต์ตามที่คุณยอมรับได้ และควรจำไว้ว่าการตั้งค่าช่วงเวลาที่สั้นเกินไปบนโทรศัพท์จะทำให้เปลืองแบตเตอรี่
สำรองข้อมูลที่จัดเก็บซึ่งเป็นเพียงไฟล์ธรรมดา
ภายใต้ Radicale ปฏิทินของคุณคือไดเรกทอรีของไฟล์ .ics โดยหนึ่งไฟล์ต่อหนึ่งเหตุการณ์ พร้อมด้วยไฟล์คุณสมบัติขนาดเล็กต่อหนึ่งคอลเลกชัน เครื่องมือใดก็ตามที่คัดลอกไดเรกทอรีสามารถใช้สำรองข้อมูลได้ และคุณสามารถเปิดไฟล์สำรองด้วย less เพื่อตรวจสอบว่ามีข้อมูลเหตุการณ์อยู่จริง นี่เป็นข้อได้เปรียบที่แท้จริงเมื่อเทียบกับการดัมพ์ฐานข้อมูลที่คุณไม่สามารถอ่านได้โดยตรง
sudo systemctl stop radicale
sudo tar czf /root/radicale-$(date +%F).tar.gz -C /var/lib/radicale collections
sudo systemctl start radicaleให้หยุดการทำงานของเซอร์วิสในช่วงเวลาไม่กี่วินาทีที่ใช้ในการทำอาร์ไคฟ์ เพื่อป้องกันไม่ให้ไคลเอนต์เขียนข้อมูลค้างไว้ในขณะที่กำลังอ่านไฟล์ จากนั้นให้คัดลอกไฟล์อาร์ไคฟ์ออกจากเครื่อง เพราะการสำรองข้อมูลไว้บน VPS เครื่องเดียวกันจะไม่สามารถกู้คืนได้หากเกิดความล้มเหลวที่คุณกำลังเตรียมรับมือ การกู้คืนทำได้โดยย้อนขั้นตอนเดิม คือแตกไฟล์ sudo chown -R radicale:radicale /var/lib/radicale/collections แล้วเริ่มการทำงานของเซอร์วิส นอกจากนี้ ไคลเอนต์ทุกตัวยังเก็บสำเนาปฏิทินไว้ในเครื่อง ดังนั้นแล็ปท็อปที่ไม่ได้ซิงค์ข้อมูลตั้งแต่เกิดความล้มเหลวจึงถือเป็นสำเนาข้อมูลชุดที่สองของคุณ
เมื่อใดที่ Baikal หรือ Nextcloud เป็นตัวเลือกที่เหมาะสมกว่า
Baikal 0.12.1 ถูกปล่อยออกมาเมื่อวันที่ 5 สิงหาคม 2026 และต้องการ PHP 8.2 หรือใหม่กว่า ให้แตกไฟล์ไว้นอก web root และเปิดเผยเฉพาะไดเรกทอรี html เท่านั้น:
sudo apt install -y php-fpm php-sqlite3 php-xml php-mbstring php-curl unzip
cd /tmp
curl -LO https://github.com/sabre-io/Baikal/releases/download/0.12.1/baikal-0.12.1.zip
sudo unzip -q baikal-0.12.1.zip -d /srv
sudo chown -R www-data:www-data /srv/baikal/Specific /srv/baikal/configไดเรกทอรีทั้งสองนี้เป็นเพียงส่วนเดียวที่เว็บเซิร์ฟเวอร์ต้องเขียนข้อมูลลงไป ดังนั้นส่วนอื่นจึงไม่จำเป็นต้องมีสิทธิ์เขียน ภายใน nginx server block ของคุณ ส่วนที่เฉพาะเจาะจงสำหรับ Baikal มีดังนี้:
root /srv/baikal/html;
index index.php;
location ~ /(\.ht|Core|Specific|config) { deny all; }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location = /.well-known/caldav { return 308 /dav.php; }
location = /.well-known/carddav { return 308 /dav.php; }ให้ reload nginx แล้วเปิดเว็บไซต์ผ่านเบราว์เซอร์ ตัวช่วยติดตั้งจะสร้างบัญชีผู้ดูแลระบบและฐานข้อมูล SQLite ให้ การตั้งค่าฝั่งไคลเอนต์จะเหมือนกับของ Radicale โดยใช้ https://cal.example.com/ เป็นที่อยู่เซิร์ฟเวอร์ เนื่องจากกฎ well-known จะส่งการค้นหาไปยัง /dav.php โดยอัตโนมัติ
Nextcloud จะคุ้มค่ากับทรัพยากรที่ใช้ก็ต่อเมื่อคุณต้องการจัดการไฟล์และใช้งานแอปบนโทรศัพท์ด้วยบัญชีเดียวกันเท่านั้น โดยมี DAV root อยู่ที่ /remote.php/dav/ และใช้กฎการค้นหาเดียวกัน สำหรับบริการเหล่านี้ การรันในคอนเทนเนอร์จะช่วยแยกเวอร์ชันของ PHP ออกจากโฮสต์ของคุณได้: Docker Compose บน VPS ครอบคลุมถึงไฟล์ compose และการตั้งค่า reverse proxy ที่อยู่ด้านหน้า และ สิ่งที่ควรทำ self-hosting ในปี 2026 เป็นแหล่งข้อมูลที่เหมาะสมสำหรับการตัดสินใจว่าคุณต้องการก้าวไปไกลแค่ไหนในเส้นทางนี้
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
การซิงค์ทุกครั้งส่งค่า 401 กลับมา อาจเป็นไปได้ว่าไฟล์รหัสผ่านสูญเสียบัญชีผู้ใช้ไปให้กับ htpasswd -c อีกตัว หรือผู้ใช้ radicale ไม่สามารถอ่านไฟล์นั้นได้ ให้ตรวจสอบด้วย sudo -u radicale cat /etc/radicale/users หากพบข้อผิดพลาด permission denied นั่นคือสาเหตุ วิธีแก้ไขคือให้ใช้กลุ่ม radicale และตั้งค่า mode เป็น 640 นอกจากนี้ Radicale จะหน่วงเวลาหนึ่งวินาทีหลังจากการล็อกอินล้มเหลวแต่ละครั้งโดยค่าเริ่มต้น ดังนั้นไคลเอนต์ที่ใช้รหัสผ่านเก่าจะดูเหมือนทำงานช้าแทนที่จะถูกปฏิเสธการเชื่อมต่อ
nginx ตอบกลับ 405 เมื่อเรียกใช้ PROPFIND URL ดังกล่าวถูกให้บริการในฐานะไฟล์แบบ static ทำให้เมธอด WebDAV ไปไม่ถึง Radicale ให้ทดสอบ endpoint โดยตรงดังนี้:
curl -u you -X PROPFIND -H "Depth: 0" -i https://cal.example.com/you/DAV collection ที่ทำงานปกติจะตอบกลับด้วย 207 Multi-Status หากเป็นค่าอื่นแสดงว่าคำขอถูกหยุดไว้ที่เว็บเซิร์ฟเวอร์
โทรศัพท์ไม่สามารถยืนยันบัญชีได้ แต่เบราว์เซอร์ใช้งานได้ปกติ มีสาเหตุหลักสองประการ ประการแรกคือการทำ well-known redirect หายไป ซึ่งทดสอบได้ด้วยคำสั่ง curl ด้านบน ประการที่สองคือ certificate chain ไม่สมบูรณ์ ซึ่งเบราว์เซอร์มักจะแก้ไขให้โดยอัตโนมัติด้วยการดึง intermediate certificate ที่ขาดหายไปมาให้ แต่ iOS ไม่ทำเช่นนั้น ให้ตรวจสอบจากเชลล์:
openssl s_client -connect cal.example.com:443 -servername cal.example.com </dev/nullให้มองหา Verify return code: 0 (ok) หากตรวจสอบไม่ผ่าน แสดงว่าการตั้งค่า nginx ชี้ไปที่ cert.pem ในขณะที่ควรชี้ไปที่ fullchain.pem
เกิดเหตุการณ์ซ้ำซ้อนหลังจากการนำเข้าข้อมูล แต่ละเหตุการณ์จะมี UID ซึ่งไคลเอนต์จะใช้สิ่งนี้ในการระบุตัวตน หากคุณนำเข้าไฟล์เดิมซ้ำสองครั้งผ่านเครื่องมือที่สร้างตัวระบุใหม่ คุณจะได้เหตุการณ์สองรายการที่ไม่มีวันรวมกันได้ ให้ลบสำเนาส่วนเกินออกบนอุปกรณ์เครื่องหนึ่งแล้วปล่อยให้การลบนั้นซิงค์ออกไป
ทุกอย่างหยุดทำงานหลังจากรีบูต บริการถูกเริ่มด้วยมือ sudo systemctl is-enabled radicale จะแสดงผลเป็น disabled และ sudo systemctl enable --now radicale จะแก้ไขปัญหานี้อย่างถาวร
FAQ
ฉันจำเป็นต้องใช้ TLS สำหรับเซิร์ฟเวอร์ CalDAV ที่โฮสต์เองจริงหรือ?
จำเป็นครับ Radicale ใช้วิธีการตรวจสอบสิทธิ์แบบ HTTP Basic ซึ่งรหัสผ่านจะถูกส่งในรูปแบบ base64 ในทุกคำขอ และ base64 นั้นสามารถถอดรหัสกลับได้โดยง่าย นอกจากนี้ไคลเอนต์ยังบังคับใช้เรื่องนี้ด้วย เช่น macOS Calendar.app อาจปฏิเสธการส่งข้อมูลรับรองผ่าน HTTP ที่ไม่ปลอดภัยโดยไม่แจ้งเตือน และ iOS ก็มีพฤติกรรมเช่นเดียวกัน ส่งผลให้ดูเหมือนว่าบันทึกบัญชีสำเร็จแต่ไม่เคยซิงค์ข้อมูลเลย การทำ sudo certbot --nginx -d cal.example.com จึงเป็นสิ่งที่ต้องทำทั้งหมด
ทำไมโทรศัพท์ถึงเพิ่มบัญชีไม่ได้ในขณะที่ Thunderbird ใช้งานได้ปกติ?
Thunderbird จะใช้ URL เต็มที่คุณระบุ แต่โทรศัพท์ให้คุณกรอกเพียงช่องเซิร์ฟเวอร์เดียว จึงต้องพึ่งพาการค้นหาตามมาตรฐาน RFC 6764 ซึ่งหากไม่มี SRV record โทรศัพท์จะร้องขอ https://cal.example.com/.well-known/caldav และคาดหวังว่าจะได้รับการเปลี่ยนเส้นทาง (redirect) ไปยัง root ของ DAV หากคำขอดังกล่าวได้รับสถานะ 404 โทรศัพท์มักจะแจ้งว่าไม่สามารถตรวจสอบบัญชีได้ ให้เพิ่ม location = /.well-known/caldav { return 301 https://$host/; } ลงใน nginx จากนั้นตรวจสอบด้วย curl -sI https://cal.example.com/.well-known/caldav ว่าคุณได้รับสถานะ 301 และ header location หรือไม่ การวาง URL บัญชีแบบเต็มในหน้าการตั้งค่าขั้นสูงของ iOS จะช่วยข้ามขั้นตอนการค้นหาไปได้เลย
สองคนสามารถใช้ปฏิทินร่วมกันได้หรือไม่?
ได้ครับ วิธีที่น่าเชื่อถือที่สุดคือการใช้บัญชีล็อกอินร่วมกัน ให้สร้างบัญชีที่สามด้วย htpasswd เก็บปฏิทินที่แชร์ไว้ภายใต้บัญชีนั้น แล้วเพิ่มบัญชีนี้เป็น CalDAV บัญชีที่สองในแต่ละอุปกรณ์ ไฟล์สิทธิ์ของ Radicale สามารถกำหนดให้ผู้ใช้ที่ระบุมีสิทธิ์อ่านและเขียนในคอลเลกชันภายใต้ path ของผู้ใช้อื่นได้ แต่ไคลเอนต์ที่อ่านเฉพาะปฏิทินใน home set ของตัวเองจะไม่แสดงปฏิทินดังกล่าว ดังนั้นวิธีนี้จึงเหมาะกับ Thunderbird และ DAVx⁵ มากกว่า iOS
จะเกิดอะไรขึ้นกับกิจกรรมของฉันหาก VPS ล่ม?
สำหรับ Radicale ข้อมูลจะถูกเก็บเป็นข้อความธรรมดา: ไฟล์ .ics หนึ่งไฟล์ต่อหนึ่งกิจกรรมภายใต้ /var/lib/radicale/collections/collection-root/ ซึ่งคุณสามารถสำรองข้อมูลด้วย tar และอ่านด้วย less การกู้คืนทำได้โดยการแตกไฟล์ chown -R radicale:radicale แล้วเริ่ม service ใหม่ ไคลเอนต์ทุกเครื่องที่ซิงค์ไว้จะเก็บสำเนาในเครื่องด้วย ดังนั้นแล็ปท็อปที่อัปเดตข้อมูลล่าสุดก่อนระบบล่มจะมีสำเนาปฏิทินของคุณเก็บไว้อีกชุดหนึ่งอย่างครบถ้วน
เซิร์ฟเวอร์ CalDAV ซิงค์รายชื่อผู้ติดต่อให้ด้วยหรือไม่?
รายชื่อผู้ติดต่อใช้โปรโตคอล CardDAV ซึ่งเป็นโปรโตคอลพี่น้องที่กำหนดไว้ใน RFC 6352 โดยจะเก็บไฟล์ vCard แทนกิจกรรม Radicale, Baikal และ Nextcloud ต่างก็ให้บริการผ่านบัญชีเดียวกันและ hostname เดียวกัน บน Android แอป DAVx⁵ จะซิงค์ทั้งปฏิทินและรายชื่อผู้ติดต่อจากบัญชีเดียว ส่วนบน iOS คุณต้องเพิ่มบัญชีที่สองประเภท CardDAV โดยใช้ข้อมูลรับรองเดียวกัน ซึ่งเป็นเหตุผลว่าทำไมการเปลี่ยนเส้นทาง /.well-known/carddav จึงต้องอยู่ใน config ของ nginx ควบคู่ไปกับ CalDAV