SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor

Cài CalDAV trên VPS với Radicale, TLS và discovery

Đồng bộ lịch giữa điện thoại và laptop không cần Google. Cài Radicale trên VPS với TLS, discovery, tài khoản riêng và cấu hình client hoạt động ổn định.

Bạn đang xây dựng cái gì

Một lịch tự host là một CalDAV server chạy trên VPS do bạn kiểm soát, nằm sau TLS, với mỗi người dùng một tài khoản đăng nhập. Điện thoại trong túi và laptop trên bàn của bạn sẽ hiển thị cùng một sự kiện, và laptop của người thân cũng vậy. Không có tài khoản Google nào nằm ở giữa.

Đây là công việc khác với một trang đặt lịch tự host. Trang đặt lịch dành cho người lạ: nó công khai các khoảng thời gian trống của bạn và cho phép người khác chọn một lịch hẹn. Server lịch dành cho thiết bị của chính bạn: nó lưu trữ các sự kiện và đồng bộ hóa mọi client. Người dùng thường chạy cả hai, khi đó công cụ đặt lịch sẽ đọc trạng thái rảnh/bận từ CalDAV server mà bạn xây dựng ở đây.

Việc cài đặt rất nhỏ gọn. Radicale chỉ là một gói Python với khoảng mười dòng cấu hình. Điều quyết định liệu hệ thống có hoạt động ổn định sau tháng đầu tiên hay không chính là TLS, cơ chế discovery, bộ sưu tập theo người dùng và sao lưu. Những phần này chiếm phần lớn nội dung bên dưới.

CalDAV là gì và tại sao nó quan trọng?

CalDAV là giao thức đồng bộ lịch qua HTTP. Nó được định nghĩa trong RFC 4791 như các phần mở rộng của WebDAV (web distributed authoring and versioning, một tập hợp các phương thức HTTP bổ sung được định nghĩa trong RFC 4918). Một lịch là một tập hợp, hoạt động giống như một thư mục. Một sự kiện là một file bên trong đó, được viết bằng định dạng văn bản iCalendar (RFC 5545), cùng định dạng với các .ics tệp đính kèm trong email của bạn.

Các client sử dụng HTTP thông thường với một vài phương thức bổ sung. PROPFIND hỏi những gì có ở đây và các thuộc tính của nó là gì. REPORT yêu cầu một lát cắt đã lọc, ví dụ như mọi sự kiện trong một phạm vi ngày. PUT ghi một sự kiện và DELETE xóa nó. Mỗi sự kiện mang một dòng UID, và định danh đó là cách hai thiết bị xác nhận chúng đang xem cùng một sự kiện thay vì một bản sao.

Tính di động là lợi ích thu được, và đó là lý do duy nhất để bận tâm. iOS, macOS, Thunderbird, Evolution và Android thông qua DAVx⁵ đều hỗ trợ CalDAV. Dữ liệu của bạn không bị ràng buộc với máy chủ mà bạn đã chọn hôm nay. Hãy di chuyển các file sang một máy chủ CalDAV khác, trỏ các client vào hostname mới, và không có gì khác thay đổi.

CardDAV đi kèm theo đó. Nó có cùng ý tưởng cho danh bạ, được định nghĩa trong RFC 6352 và lưu trữ các file vCard thay vì các sự kiện. Mọi máy chủ bên dưới đều phục vụ cả hai giao thức từ cùng một tài khoản, vì vậy khi lịch đã hoạt động, sổ địa chỉ chỉ là một thao tác đánh dấu chọn.

Bạn nên chạy CalDAV server nào?

Radicale là lựa chọn gọn nhẹ nhất mà vẫn hoạt động tốt. Nó chạy bằng Python, không cần database, và lưu trữ dữ liệu dưới dạng các file văn bản thuần túy trong một thư mục. Hướng dẫn này sử dụng Radicale vì lịch cá nhân trong gia đình không cần gì hơn thế, và vì nó rất ít khi gặp sự cố vào lúc 3 giờ sáng.

Baikal là tùy chọn có bảng điều khiển quản trị qua web. Nó chạy trên PHP và thư viện sabre/dav, lưu trữ người dùng và lịch trong SQLite hoặc MySQL, cho phép bạn thêm người dùng qua trình duyệt thay vì dùng CLI. Hãy chọn nó khi các tài khoản thường xuyên thay đổi.

Nextcloud là lựa chọn phù hợp khi lịch chỉ là một trong nhiều tính năng bạn cần. Bạn sẽ có lịch, danh bạ, lưu trữ file và ứng dụng di động, nhưng phải đánh đổi bằng việc duy trì PHP-FPM, một database và một trình chạy background job. Nếu thấy cấu hình này quá nặng nề so với nhu cầu thực tế, các giải pháp thay thế Nextcloud nhẹ hơn sẽ phù hợp hơn, và tự host đồng bộ file sẽ giải quyết phần còn lại của những lý do khiến mọi người cài đặt Nextcloud.

DAViCal là tùy chọn sử dụng PostgreSQL lâu đời. Nó chỉ đáng xem xét nếu bạn đã chạy sẵn PostgreSQL và muốn lưu trữ dữ liệu lịch ngay trong đó.

Cài đặt Radicale trên Ubuntu 24.04

Radicale 3.8.3, được gắn tag vào ngày 8 October 2026, là bản phát hành hiện tại tính đến ngày 11 October 2026. Hãy cài đặt Radicale trong một virtual environment riêng, cùng extra bcrypt mà file mật khẩu bên dưới sử dụng.

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 không phải lựa chọn về phong cách. sudo pip install radicale trực tiếp vào system Python sẽ dừng với error: externally-managed-environment, vì Ubuntu đánh dấu Python hệ thống là do apt quản lý để pip không thể ghi đè các file do package cung cấp.

Tạo /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/collections

hosts cố ý bind vào loopback. nginx terminate TLS và forward đến cổng đó, nên Radicale không trực tiếp nhận lưu lượng từ Internet. Ví dụ upstream về 0.0.0.0:5232 public một service không mã hóa nhưng vẫn nhận password. Đây là sai sót cần tránh trong trường hợp này.

Tiếp theo là các account. -B chọn bcrypt. Radicale đọc bcrypt bằng htpasswd_encryption = autodetect khi module bcrypt từ lần cài radicale[bcrypt] ở trên đã có mặt:

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 tạo file và truncate toàn bộ nội dung hiện có. Chỉ dùng lệnh này cho user đầu tiên. Nếu chạy lại htpasswd -B -c sau vài tháng, mọi account được thêm sau account đầu tiên sẽ bị xóa. Dấu hiệu là một người vẫn sync bình thường, còn tất cả người khác liên tục nhận prompt password nhưng không đăng nhập được. SHA-512 crypt (-5) không dùng được ở đây: htpasswd chỉ có hỗ trợ SHA-2 từ Apache 2.4.59, trong khi Ubuntu 24.04 cung cấp apache2-utils 2.4.58 và htpasswd trong đó không có flag -5.

Tạo /etc/systemd/system/radicale.service, dựa trên unit trong tài liệu 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.target
sudo systemctl daemon-reload
sudo systemctl enable --now radicale
curl -i http://127.0.0.1:5232/

Kết quả bình thường là 401 Unauthorized với header WWW-Authenticate: service đang listen và authentication đã bật. Connection refused có nghĩa là không có tiến trình nào listen trên địa chỉ đó, thường do service khởi động thất bại hoặc đã thoát sau đó. journalctl -u radicale -n 50 cho biết option mà service đã từ chối. ProtectSystem=strict mount filesystem ở chế độ chỉ đọc cho service này, nên ReadWritePaths=/var/lib/radicale/ là dòng cho phép service lưu một event. Nếu bỏ dòng đó, thao tác đọc vẫn hoạt động nhưng mọi thao tác ghi đều thất bại.

TLS không phải tùy chọn, vì client từ chối kết nối plaintext

Thiết lập này xác thực bằng HTTP Basic, là scheme duy nhất Radicale chấp nhận, và Basic gửi user:password được mã hóa bằng base64 trong từng request. Base64 là encoding, không phải encryption. Qua HTTP không mã hóa, bạn gửi password cho mọi mạng nằm giữa điện thoại và server, liên tục trong suốt mỗi lần đồng bộ.

Client tự áp đặt yêu cầu này. Tài liệu Radicale ghi rõ macOS Calendar.app có thể âm thầm từ chối gửi credential qua HTTP không bảo mật, và iOS cũng hoạt động như vậy. Account hiển thị như đã được cấu hình nhưng hoàn toàn không sync, không có lỗi để xem.

Trước tiên, trỏ bản ghi A của cal.example.com đến VPS, vì certificate authority sẽ kiểm tra bản ghi này. Sau đó tạo /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/; }
}

Bốn dòng proxy header này lấy từ tài liệu Radicale. Giữ nguyên chúng.

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 in ra syntax is ok và test is successful. Chỉ reload sau khi lệnh này in ra các kết quả đó, vì reload với file bị lỗi sẽ giữ config cũ đang chạy và che giấu lỗi cho đến lần restart tiếp theo. Certbot chỉnh sửa site file trực tiếp: cài certificate, chuyển block sang port 443 và thêm redirect từ port 80. Lệnh curl cuối cùng sẽ hỏi password và phải trả về 200, là web interface của chính Radicale. 502 Bad Gateway nghĩa là nginx đang chạy nhưng không nhận được câu trả lời hợp lệ từ port 5232. Nguyên nhân thường gặp là Radicale không listen trên port đó.

Tại sao thêm tài khoản trên điện thoại bị lỗi?

Nguyên nhân là cơ chế discovery. RFC 6764 mô tả cách client chuyển hostname thành URL lịch. Client tìm bản ghi _caldavs._tcp SRV, sau đó gọi https://cal.example.com/.well-known/caldav và chờ redirect đến DAV root. Từ đó, client yêu cầu current-user-principal, rồi yêu cầu calendar-home-set của principal đó, và chỉ sau đó mới thấy các lịch của bạn. Điện thoại chỉ có một trường để nhập server, nên mọi bước phải hoạt động tự động.

curl -sI https://cal.example.com/.well-known/caldav

Phản hồi đúng là HTTP/2 301 kèm header location: https://cal.example.com/. Phản hồi 404 tại đó là một nguyên nhân phổ biến khiến iOS báo không thể xác minh thông tin tài khoản, trong khi Thunderbird trên cùng network vẫn hoạt động: Thunderbird dùng full URL bạn đã nhập nên không cần redirect.

Đích redirect phụ thuộc vào server. Radicale được phục vụ tại root của site sẽ redirect đến /. Khi request đến Radicale, Radicale tự trả về 301 cho path đó, nên các rule nginx chỉ làm redirect này rõ ràng thay vì tạo redirect mới. Baikal có sẵn các rule mẫu redirect đến /dav.php với status 308. Nextcloud redirect đến /remote.php/dav/.

Tạo lịch và chia sẻ với đối tác

Nhiều client không hỗ trợ tạo lịch mà chỉ có thể subscribe. Hãy mở https://cal.example.com/ trên trình duyệt, đăng nhập bằng you và tạo lịch tại đó. Trên ổ đĩa, lịch sẽ nằm tại /var/lib/radicale/collections/collection-root/you/ với tên thư mục là một định danh được tạo tự động.

Backend quyền mặc định của Radicale là owner_only: một tài khoản đã xác thực có thể đọc và ghi các collection của chính mình trong /USERNAME/ và không thể truy cập nơi khác. Đối với hầu hết các hộ gia đình, đây là thiết lập phù hợp. Cách đơn giản nhất để chia sẻ lịch là dùng một tài khoản thứ ba. Hãy tạo household bằng htpasswd, tạo lịch chia sẻ dưới tài khoản đó, rồi thêm lịch này vào mỗi thiết bị như một tài khoản CalDAV thứ hai. Cách này hoạt động trên mọi client, bao gồm cả iOS, vì lịch nằm trong thư mục home của chính tài khoản đó.

Khi bạn cần kiểm soát chi tiết hơn, hãy chuyển sang sử dụng quyền dựa trên quy tắc (rule-based rights). Thêm nội dung sau vào /etc/radicale/config:

[rights]
type = from_file
file = /etc/radicale/rights

Sau đó cấu hình /etc/radicale/rights dựa trên ví dụ trong tài liệu của 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

Các chữ cái viết hoa và viết thường có ý nghĩa khác nhau. R và W dùng để đọc và ghi các collection không phải là lịch hoặc sổ địa chỉ, đây chính là thư mục principal. r và w dùng để đọc và ghi chính các lịch đó. Hãy thay thế định danh đó bằng tên thư mục thực tế của lịch từ đường dẫn lưu trữ nêu trên.

Một hạn chế thực tế: client chỉ đọc tập hợp calendar home sẽ không hiển thị lịch nằm dưới đường dẫn của người dùng khác, vì quá trình discovery không quét đến đó. Thunderbird và DAVx⁵ có thể thêm lịch bằng URL đầy đủ. iOS thì không làm được, đó là lý do tại sao mô hình tài khoản chia sẻ là cách luôn hoạt động ổn định.

Thiết lập các client, vì đây là nơi các lịch tự host thường bị lỗi

iPhone và iPad. Mở Settings, sau đó chọn Calendar (nằm trong mục Apps trên các phiên bản iOS gần đây), chọn Calendar Accounts, Add Account, Other, Add CalDAV Account. Server là cal.example.com, sau đó nhập user name và password. Description chỉ là nhãn hiển thị. Nếu máy không cho lưu, hãy mở lại tài khoản đó: chế độ xem nâng cao (advanced view) sẽ hiển thị Use SSL, cổng và URL tài khoản đầy đủ, việc dán trực tiếp URL sẽ bỏ qua bước tự động dò tìm.

Android. Không có client CalDAV tích hợp sẵn. Hãy cài đặt DAVx⁵ từ F-Droid hoặc Google Play, thêm tài khoản bằng URL cơ sở https://cal.example.com/ với user name của bạn, sau đó đánh dấu chọn các lịch muốn đồng bộ. DAVx⁵ ghi dữ liệu vào nhà cung cấp lịch của Android, vì vậy các sự kiện sẽ xuất hiện trong bất kỳ ứng dụng lịch nào bạn đang sử dụng.

Thunderbird. Chọn New Calendar, On the Network, sau đó nhập user name và vị trí https://cal.example.com/. Ứng dụng sẽ liệt kê những gì tìm thấy và hỏi bạn muốn thêm lịch nào.

macOS. Mở System Settings, Internet Accounts, Add Other Account, CalDAV, đặt Account Type thành Manual, sau đó nhập user name, password và địa chỉ server tương tự.

CalDAV là giao thức dạng polling (truy vấn). Đặc tả kỹ thuật không có cơ chế push, vì vậy một sự kiện bạn thêm trên laptop sẽ xuất hiện trên điện thoại vào lần đồng bộ tiếp theo thay vì ngay lập tức. Hãy đặt khoảng thời gian đồng bộ (interval) trong mỗi client sao cho phù hợp với nhu cầu, và lưu ý rằng khoảng thời gian càng ngắn trên điện thoại thì càng tốn pin.

Sao lưu kho dữ liệu, vốn chỉ là các file

Với Radicale, lịch của bạn là một thư mục chứa các .ics file, mỗi file tương ứng với một sự kiện, cộng thêm một file thuộc tính nhỏ cho mỗi bộ sưu tập. Bất kỳ công cụ nào sao chép thư mục đều có thể dùng để sao lưu, và bạn có thể mở bản sao lưu bằng less để xác nhận nó chứa các sự kiện thực tế. Đây là một lợi thế thực sự so với việc dump database mà bạn không thể đọc được.

sudo systemctl stop radicale
sudo tar czf /root/radicale-$(date +%F).tar.gz -C /var/lib/radicale collections
sudo systemctl start radicale

Hãy dừng service trong vài giây để thực hiện lưu trữ, nhằm tránh việc client đang ghi dữ liệu dở dang khi các file được đọc. Sau đó, hãy sao chép bản lưu trữ ra khỏi máy chủ, vì bản sao lưu nằm trên cùng một VPS sẽ không còn nếu xảy ra sự cố mà bạn đang phòng ngừa. Việc khôi phục thực hiện ngược lại: giải nén, sudo chown -R radicale:radicale /var/lib/radicale/collections, và khởi động lại service. Mỗi client cũng lưu một bản sao cục bộ của lịch, vì vậy một chiếc laptop chưa đồng bộ kể từ khi xảy ra sự cố chính là bản sao thứ hai cho dữ liệu của bạn.

Khi nào nên chọn Baikal hoặc Nextcloud

Baikal 0.12.1 được phát hành vào ngày 5 tháng 8 năm 2026 và yêu cầu PHP 8.2 trở lên. Hãy giải nén nó bên ngoài web root và chỉ expose thư mục html của nó:

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

Hai thư mục đó là những nơi duy nhất mà web server cần quyền ghi, vì vậy không có gì khác cần phải ở trạng thái writable. Bên trong block server nginx của bạn, các phần dành riêng cho Baikal như sau:

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, mở trang web trên trình duyệt, và trình hướng dẫn thiết lập sẽ tạo tài khoản admin cùng database SQLite. Việc thiết lập client tương tự như với Radicale, sử dụng https://cal.example.com/ làm địa chỉ máy chủ, vì quy tắc well-known sẽ gửi yêu cầu discovery đến /dav.php.

Nextcloud chỉ thực sự đáng dùng nếu bạn muốn đồng bộ file và sử dụng app điện thoại với cùng một tài khoản đăng nhập. DAV root của nó là /remote.php/dav/, và các quy tắc discovery tương tự cũng được áp dụng. Đối với bất kỳ dịch vụ nào trong số này, việc chạy trong container sẽ giúp tách biệt các phiên bản PHP khỏi host của bạn: Docker Compose trên VPS hướng dẫn về file compose và reverse proxy phía trước nó, còn những gì đáng để tự host trong năm 2026 là nơi phù hợp để bạn quyết định xem mình muốn đi xa đến đâu trên con đường này.

Các kiểu lỗi và thông báo bạn sẽ gặp

Mọi thao tác sync đều trả về 401. Hoặc file mật khẩu đã bị mất các tài khoản do một htpasswd -c khác, hoặc người dùng radicale không có quyền đọc file đó. Hãy kiểm tra bằng sudo -u radicale cat /etc/radicale/users; nếu nhận được thông báo permission denied thì đó chính là nguyên nhân, cách khắc phục là đặt group thành radicale với mode 640. Radicale cũng mặc định chờ một giây sau mỗi lần đăng nhập thất bại, vì vậy client dùng mật khẩu cũ sẽ có cảm giác phản hồi chậm thay vì bị từ chối ngay lập tức.

nginx trả về 405 với phương thức PROPFIND. URL đang được phục vụ như một file tĩnh, nên phương thức WebDAV không bao giờ đến được Radicale. Hãy kiểm tra endpoint trực tiếp:

curl -u you -X PROPFIND -H "Depth: 0" -i https://cal.example.com/you/

Một collection DAV hoạt động bình thường sẽ trả về 207 Multi-Status. Bất kỳ kết quả nào khác nghĩa là yêu cầu đã bị chặn lại ở web server.

Điện thoại không xác thực được tài khoản, trong khi trình duyệt vẫn hoạt động bình thường. Có hai nguyên nhân phổ biến. Redirect well-known bị thiếu, có thể kiểm tra bằng lệnh curl ở trên. Hoặc chuỗi chứng chỉ không đầy đủ, trình duyệt thường tự xử lý bằng cách tải về các chứng chỉ trung gian còn thiếu trong khi iOS thì không. Hãy kiểm tra từ shell:

openssl s_client -connect cal.example.com:443 -servername cal.example.com </dev/null

Tìm kiếm Verify return code: 0 (ok). Nếu thất bại, cấu hình nginx đang trỏ đến cert.pem thay vì trỏ đến fullchain.pem.

Sự kiện bị trùng lặp sau khi import. Mỗi sự kiện mang một UID, và các client coi đó là định danh duy nhất. Nếu import cùng một file hai lần thông qua một công cụ tự tạo lại định danh, bạn sẽ có hai sự kiện mà không hệ thống nào có thể tự gộp lại được. Hãy xóa các bản sao thừa trên một thiết bị và để lệnh xóa đó đồng bộ ra các thiết bị khác.

Mọi thứ ngừng hoạt động sau khi reboot. Service đã được khởi động thủ công. sudo systemctl is-enabled radicale sẽ in ra disabled, và sudo systemctl enable --now radicale sẽ khắc phục vấn đề này vĩnh viễn.

FAQ

Tôi có thực sự cần TLS cho CalDAV server tự host không?

Có. Radicale xác thực bằng HTTP Basic, nên password được gửi dưới dạng mã hóa base64 trong mỗi request, và base64 có thể giải mã rất dễ dàng. Các client cũng yêu cầu điều này: macOS Calendar.app có thể âm thầm từ chối gửi credential qua HTTP không bảo mật, còn iOS cũng hoạt động tương tự. Vì vậy, account có vẻ được lưu nhưng không bao giờ sync. sudo certbot --nginx -d cal.example.com là toàn bộ việc cần làm.

Tại sao điện thoại không thêm được account trong khi Thunderbird vẫn hoạt động?

Thunderbird dùng URL đầy đủ mà bạn nhập. Điện thoại chỉ có một trường server, nên dựa vào cơ chế discovery của RFC 6764. Nếu không có bản ghi SRV, điện thoại sẽ request https://cal.example.com/.well-known/caldav và chờ redirect đến DAV root. Nếu request đó trả về 404, điện thoại thường báo không thể xác minh account. Thêm location = /.well-known/caldav { return 301 https://$host/; } vào nginx, rồi xác nhận bằng curl -sI https://cal.example.com/.well-known/caldav rằng kết quả trả về là 301 và có header location. Dán URL đầy đủ của account vào phần cài đặt nâng cao của iOS sẽ bỏ qua hoàn toàn bước discovery.

Hai người có thể dùng chung một calendar không?

Có. Cách đáng tin cậy là dùng một login chung. Tạo account thứ ba bằng htpasswd, đặt calendar dùng chung dưới account đó, rồi thêm account này dưới dạng CalDAV account thứ hai trên từng thiết bị. File rights của Radicale cũng có thể cấp quyền đọc và ghi cho một user được chỉ định trên một collection nằm dưới path của user khác. Tuy nhiên, client chỉ đọc calendar home set của chính nó sẽ không bao giờ hiển thị collection đó. Vì vậy, cách này phù hợp với Thunderbird và DAVx⁵ hơn iOS.

Điều gì xảy ra với các event nếu VPS bị hỏng?

Với Radicale, dữ liệu được lưu dưới dạng plain text: mỗi event có một file .ics riêng trong /var/lib/radicale/collections/collection-root/. Bạn có thể backup bằng tar và đọc bằng less. Khôi phục gồm các bước extract, chown -R radicale:radicale, rồi khởi động service. Mỗi client đã sync cũng giữ một bản sao cục bộ. Vì vậy, laptop đã sync đầy đủ trước khi xảy ra sự cố sẽ giữ một bản sao thứ hai đầy đủ của calendar.

CalDAV server có sync cả contact không?

Contact dùng CardDAV, một protocol liên quan được định nghĩa trong RFC 6352. Protocol này lưu các file vCard thay vì event. Radicale, Baikal và Nextcloud đều cung cấp protocol này bằng cùng account và cùng hostname. Trên Android, DAVx⁵ sync calendar và contact từ một account. Trên iOS, bạn thêm một account loại CardDAV với cùng credential. Vì vậy, redirect /.well-known/carddav cần được thêm vào cấu hình nginx bên cạnh redirect CalDAV.

#caldav#calendar#radicale#tự lưu trữ#sync