راه اندازی سرور CalDAV روی VPS با Radicale
با نصب Radicale روی VPS خود، تقویمها را بدون نیاز به Google بین گوشی و لپتاپ همگام کنید. این راهنما شامل تنظیمات TLS، قابلیت discovery و پیکربندی کلاینت برای عملکرد پایدار است.
آنچه میسازید
یک تقویم خود-میزبانیشده (self-hosted)، یک سرور CalDAV روی یک VPS تحت کنترل شماست که پشت TLS قرار دارد و برای هر نفر یک حساب کاربری مجزا تعریف میشود. گوشی در جیب شما و لپتاپ روی میزتان، رویدادهای یکسانی را نمایش میدهند و لپتاپ همسرتان نیز همینطور. هیچ حساب کاربری Google در این میان واسطه نیست.
این کار با یک صفحه رزرو خود-میزبانیشده متفاوت است. صفحه رزرو برای افراد غریبه است: زمانهای آزاد شما را منتشر میکند و به دیگران اجازه میدهد یکی را انتخاب کنند. سرور تقویم برای دستگاههای شخصی خودتان است: رویدادها را ذخیره میکند و همه کلاینتها را با هم همگام نگه میدارد. افراد اغلب هر دو را اجرا میکنند و ابزار رزرو، وضعیت در دسترس بودن را از سرور CalDAV که در اینجا میسازید، میخواند.
نصب آن کوچک است. Radicale یک بسته Python و حدود ده خط پیکربندی است. آنچه تعیین میکند آیا این تنظیمات در ماه اول دوام میآورد یا خیر، TLS، قابلیت کشف (discovery)، مجموعههای کاربری (collections) و پشتیبانگیری است. بخش عمدهای از مطالب زیر به این موارد اختصاص دارد.
CalDAV چیست و چرا اهمیت دارد؟
CalDAV پروتکلی برای همگامسازی تقویم بر بستر HTTP است. این پروتکل در RFC 4791 به عنوان افزونهای برای WebDAV (مخفف Web Distributed Authoring and Versioning، مجموعهای از متدهای اضافی HTTP که در RFC 4918 تعریف شده) معرفی شده است. یک تقویم در اینجا به عنوان یک collection شناخته میشود که رفتاری مشابه یک دایرکتوری دارد. هر رویداد، یک فایل درون آن است که با فرمت متنی iCalendar (طبق RFC 5545) نوشته شده است؛ همان فرمتی که در .ics پیوستهای ایمیل شما استفاده میشود.
کلاینتها از HTTP معمولی به همراه چند متد اضافه استفاده میکنند. PROPFIND میپرسد چه چیزی در اینجا وجود دارد و چه ویژگیهایی دارد. REPORT یک بخش فیلترشده را درخواست میکند، مثلاً تمام رویدادها در یک بازه زمانی خاص. PUT یک رویداد را مینویسد و DELETE آن را حذف میکند. هر رویداد دارای یک خط UID است و این شناسه همان چیزی است که باعث میشود دو دستگاه توافق کنند که در حال مشاهده یک رویداد واحد هستند، نه یک کپی از آن.
مزیت اصلی این کار، قابلیت حمل (Portability) است و همین دلیل کافی برای استفاده از آن محسوب میشود. سیستمعاملهای iOS، macOS، Thunderbird، Evolution و همچنین Android از طریق DAVx⁵ همگی از CalDAV پشتیبانی میکنند. دادههای شما به سروری که امروز انتخاب کردهاید محدود نمیشود. کافی است فایلها را به یک سرور CalDAV دیگر منتقل کنید و کلاینتها را به نام میزبان (hostname) جدید هدایت کنید؛ هیچ چیز دیگری تغییر نخواهد کرد.
CardDAV نیز در کنار آن قرار دارد. این پروتکل دقیقاً همان ایده را برای مخاطبین پیاده میکند، در RFC 6352 تعریف شده و به جای رویدادها، فایلهای vCard را ذخیره میکند. تمام سرورهای زیر، هر دو پروتکل را از یک حساب کاربری واحد ارائه میدهند، بنابراین به محض اینکه تقویم را راهاندازی کنید، فعالسازی دفترچه تلفن تنها به یک تیک زدن ساده نیاز دارد.
کدام سرور CalDAV را باید اجرا کنید؟
Radicale کوچکترین گزینهای است که کارایی لازم را دارد. این برنامه با Python نوشته شده، به دیتابیس نیاز ندارد و دادهها را در پوشهای از فایلهای متنی ساده ذخیره میکند. این راهنما از Radicale استفاده میکند، زیرا تقویم خانگی به چیزی بیش از این نیاز ندارد و احتمال بروز خطا در ساعت 3 صبح با آن بسیار کم است.
Baikal گزینهای است که پنل مدیریت تحت وب دارد. این برنامه با PHP و کتابخانه sabre/dav اجرا میشود، کاربران و تقویمها را در SQLite یا MySQL نگه میدارد و به شما اجازه میدهد به جای خط فرمان، از طریق مرورگر کاربر اضافه کنید. اگر حسابهای کاربری زیاد تغییر میکنند، این گزینه را انتخاب کنید.
Nextcloud زمانی مناسب است که تقویم تنها یکی از چندین قابلیت مورد نیاز شما باشد. شما تقویم، مخاطبین، فایلها و اپلیکیشن موبایل را دریافت میکنید، اما در عوض باید هزینه اجرای PHP-FPM، یک دیتابیس و یک اجراکننده وظایف پسزمینه (background job runner) را بپردازید. اگر این موارد برای نیاز واقعی شما سنگین به نظر میرسد، جایگزینهای سبکتر Nextcloud این نیاز را پوشش میدهند و همگامسازی فایل به صورت self-hosted بخش دیگر دلایل نصب 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]'استفاده از محیط مجازی یک انتخاب سلیقهای نیست. اجرای sudo pip install radicale روی پایتونِ سیستم با خطای error: externally-managed-environment متوقف میشود، زیرا Ubuntu پایتون خود را تحت مدیریت 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/collectionsسرویس hosts عمداً روی loopback گوش میدهد. nginx عملیات TLS termination را انجام داده و درخواستها را به این پورت هدایت میکند، بنابراین Radicale هرگز مستقیماً در معرض اینترنت قرار نمیگیرد. نمونه بالادستی 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 پس از گذشت چند ماه، تمام حسابهایی که پس از کاربر اول اضافه شدهاند را حذف میکند؛ نشانه این مشکل این است که یک نفر بهخوبی همگامسازی (sync) میکند، در حالی که بقیه با درخواست رمز عبور مواجه میشوند که هرگز تمام نمیشود. الگوریتم SHA-512 crypt (-5) در اینجا گزینهای مناسب نیست: ابزار htpasswd در Apache 2.4.59 پشتیبانی از SHA-2 را دریافت کرد، اما 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 فایلسیستم را برای این سرویس فقطخواندنی (read-only) میکند، بنابراین ReadWritePaths=/var/lib/radicale/ خطی است که به آن اجازه میدهد رویدادها را ذخیره کند. اگر آن خط را حذف کنید، عملیات خواندن همچنان کار میکند اما تمام عملیات نوشتن با شکست مواجه میشود.
استفاده از TLS الزامی است، زیرا کلاینتها از اتصال متنی ساده (plaintext) خودداری میکنند
این پیکربندی با استفاده از HTTP Basic احراز هویت میکند که تنها روش مورد پذیرش Radicale است. در این روش، user:password در هر درخواست به صورت base64 کدگذاری و ارسال میشود. Base64 یک روش کدگذاری است، نه رمزنگاری. در صورت استفاده از HTTP ساده، شما رمز عبور خود را در تمام طول روز و در هر عملیات همگامسازی، در اختیار تمام شبکههای موجود در مسیر بین گوشی و سرور قرار میدهید.
کلاینتها این موضوع را برای شما اجباری کردهاند. مستندات Radicale اشاره میکند که Calendar.app در macOS ممکن است بدون نمایش پیام خطا، از ارسال اعتبارنامهها روی HTTP ناامن خودداری کند؛ رفتار iOS نیز به همین صورت است. در این حالت، اکانت پیکربندیشده به نظر میرسد اما هرگز همگامسازی نمیشود و هیچ خطایی نیز برای بررسی وجود ندارد.
ابتدا یک رکورد A برای cal.example.com به سمت VPS تنظیم کنید، زیرا مرجع صدور گواهی (CA) آن را بررسی میکند. سپس فایل /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/; }
}چهار خط مربوط به هدرهای پروکسی از مستندات 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 کردن با یک فایل دارای خطا، پیکربندی قبلی را فعال نگه میدارد و اشتباه را تا ریاستارت بعدی مخفی میکند. Certbot فایل سایت را بهصورت درجا (in-place) ویرایش میکند: گواهی را نصب کرده، بلوک را به پورت 443 منتقل میکند و یک redirect از پورت 80 اضافه مینماید. دستور curl نهایی، رمز عبور را درخواست میکند و باید پاسخ 200 را برگرداند که رابط وب خود Radicale است. دریافت 502 Bad Gateway به این معناست که nginx در حال اجراست اما پاسخ معتبری از پورت 5232 دریافت نکرده است؛ دلیل معمول این اتفاق، گوش ندادن (listening) سرویس Radicale روی آن پورت است.
چرا افزودن حساب کاربری روی گوشی با خطا مواجه میشود؟
دلیل این امر نحوه کشف (discovery) سرویس است. استاندارد RFC 6764 توضیح میدهد که چگونه یک کلاینت، نام میزبان (hostname) را به یک URL تقویم تبدیل میکند. کلاینت ابتدا به دنبال یک رکورد _caldavs._tcp SRV میگردد، سپس درخواستی برای https://cal.example.com/.well-known/caldav ارسال میکند و انتظار دارد به ریشه DAV هدایت (redirect) شود. از آنجا، کلاینت برای 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 کاملی که شما وارد کردهاید استفاده میکند و بنابراین نیازی به هدایت (redirect) ندارد.
مقصد هدایت به سرور بستگی دارد. Radicale که در ریشه سایت سرویس میدهد، درخواستها را به / هدایت میکند و خود Radicale نیز زمانی که درخواست به آن میرسد، با یک پاسخ 301 به آن مسیر پاسخ میدهد؛ بنابراین قوانین nginx این هدایت را بهجای ایجاد مجدد، بهصورت صریح تعریف میکنند. Baikal نمونه قوانینی را ارائه میدهد که درخواستها را با وضعیت 308 به /dav.php هدایت میکنند. Nextcloud نیز درخواستها را به /remote.php/dav/ هدایت میکند.
ایجاد تقویمها و اشتراکگذاری یکی از آنها با شریک خود
بسیاری از کلاینتها قابلیت ایجاد تقویم را ندارند و تنها میتوانند در تقویمهای موجود عضو شوند (subscribe). برای این کار، https://cal.example.com/ را در مرورگر باز کنید، با حساب you وارد شوید و تقویم را در آنجا ایجاد کنید. این تقویم روی دیسک در مسیر /var/lib/radicale/collections/collection-root/you/ و با یک شناسه تولیدشده به عنوان نام پوشه ذخیره میشود.
بکاند پیشفرض برای مدیریت دسترسیها در Radicale، گزینه owner_only است: در این حالت، هر حساب کاربری احراز هویتشده میتواند مجموعههای (collections) خود را در مسیر /USERNAME/ بخواند و ویرایش کند و به هیچ داده دیگری دسترسی ندارد. برای اکثر خانوادهها، این تنظیم مناسبترین گزینه است و سادهترین راه برای اشتراکگذاری یک تقویم، ایجاد یک حساب کاربری سوم است. حساب household را با استفاده از htpasswd بسازید، تقویم اشتراکی را تحت آن حساب ایجاد کنید و آن را به عنوان یک حساب CalDAV دوم روی هر دستگاه اضافه کنید. این روش روی تمامی کلاینتها از جمله iOS کار میکند، زیرا تقویم در فضای اصلی (home) همان حساب قرار دارد.
اگر به کنترل دقیقتری نیاز دارید، از سیستم دسترسی مبتنی بر قانون (rule-based) استفاده کنید. کد زیر را به /etc/radicale/config اضافه کنید:
[rights]
type = from_file
file = /etc/radicale/rightsسپس بر اساس نمونه موجود در مستندات Radicale، فایل /etc/radicale/rights را تنظیم کنید:
[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). r و w امکان خواندن و نوشتن در خود تقویمها را فراهم میکنند. در این بخش، شناسه مربوطه را با نام واقعی پوشه تقویم خود که از مسیر ذخیرهسازی در بالا به دست آوردید، جایگزین کنید.
یک محدودیت صادقانه: کلاینتی که فقط مسیر اصلی تقویم را میخواند، تقویمی را که در مسیر کاربر دیگری قرار دارد نمایش نمیدهد، زیرا فرآیند کشف (discovery) به آن مسیرها سرک نمیکشد. Thunderbird و DAVx⁵ میتوانند این تقویم را از طریق URL کامل اضافه کنند. سیستم iOS چنین قابلیتی ندارد و به همین دلیل است که الگوی استفاده از حساب کاربری اشتراکی، تنها روشی است که همیشه جواب میدهد.
راهاندازی کلاینتها؛ جایی که تقویمهای self-hosted معمولاً با شکست مواجه میشوند
iPhone و iPad. به Settings و سپس Calendar (در نسخههای جدید iOS در بخش Apps قرار دارد) بروید. مسیر Calendar Accounts، Add Account، Other و در نهایت Add CalDAV Account را دنبال کنید. آدرس سرور cal.example.com است و پس از آن نام کاربری و رمز عبور را وارد کنید. فیلد Description فقط یک برچسب است. اگر ذخیره نشد، دوباره حساب را باز کنید: در نمای پیشرفته (advanced view)، گزینههای Use SSL، پورت و URL کامل حساب نمایش داده میشوند؛ جایگذاری (paste) مستقیم URL باعث میشود مرحله discovery کاملاً نادیده گرفته شود.
Android. هیچ کلاینت CalDAV داخلی در اندروید وجود ندارد. برنامه DAVx⁵ را از F-Droid یا Google Play نصب کنید، یک حساب با استفاده از URL پایه https://cal.example.com/ و نام کاربری خود اضافه کنید و سپس تقویمهای مورد نظر را انتخاب نمایید. برنامه DAVx⁵ دادهها را در ارائهدهنده تقویم اندروید مینویسد، بنابراین رویدادها در هر برنامه تقویمی که از قبل استفاده میکنید، نمایش داده خواهند شد.
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 وجود ندارد، بنابراین رویدادی که در لپتاپ اضافه میکنید، در لحظه روی گوشی ظاهر نمیشود و در همگامسازی (sync) بعدی دریافت خواهد شد. در هر کلاینت، بازه زمانی همگامسازی را روی مقداری تنظیم کنید که برایتان قابلقبول باشد؛ به یاد داشته باشید که بازههای زمانی کوتاهتر در گوشی، مصرف باتری را افزایش میدهد.
پشتیبانگیری از مخزن، که تنها شامل فایلها است
در Radicale، تقویم شما یک دایرکتوری از فایلهای .ics است که هر فایل مربوط به یک رویداد بوده و یک فایل کوچک properties نیز برای هر مجموعه وجود دارد. هر ابزاری که یک دایرکتوری را کپی کند، از آن پشتیبان میگیرد و شما میتوانید یک نسخه پشتیبان را با less باز کنید تا مطمئن شوید که حاوی رویدادهای واقعی است. این یک مزیت واقعی نسبت به dump دیتابیس است که نمیتوانید آن را بخوانید.
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 انتخاب بهتری هستند
نسخه 0.12.1 از Baikal در تاریخ 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این دو دایرکتوری تنها مواردی هستند که وبسرور در آنها عملیات نوشتن انجام میدهد، بنابراین هیچ بخش دیگری نیازی به دسترسی نوشتن ندارد. در داخل server block مربوط به nginx، بخشهای اختصاصی 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; }سرویس nginx را reload کنید، سایت را در مرورگر باز کنید تا setup wizard حساب کاربری مدیر و دیتابیس SQLite را ایجاد کند. تنظیمات کلاینت مشابه Radicale است و از https://cal.example.com/ به عنوان آدرس سرور استفاده میشود، زیرا قانون well-known، فرآیند discovery را به /dav.php هدایت میکند.
Nextcloud تنها در صورتی ارزش استفاده دارد که بخواهید فایلها و اپلیکیشن موبایل را نیز با همان حساب کاربری داشته باشید. ریشه DAV آن /remote.php/dav/ است و همان قوانین discovery برای آن اعمال میشود. برای هر یک از این سرویسها، اجرای آنها در یک container باعث میشود نسخههای PHP از سیستمعامل میزبان شما جدا بمانند: Docker Compose روی VPS فایل compose و reverse proxy جلوی آن را پوشش میدهد، و چه سرویسهایی در سال 2026 ارزش self-hosting دارند مکان مناسبی برای تصمیمگیری در مورد میزان پیشروی در این مسیر است.
حالتهای شکست و پیامهایی که مشاهده خواهید کرد
هر همگامسازی با خطای 401 مواجه میشود. یا فایل رمز عبور حسابهای خود را به دلیل یک htpasswd -c دیگر از دست داده است، یا کاربر radicale امکان خواندن آن را ندارد. با استفاده از sudo -u radicale cat /etc/radicale/users بررسی کنید؛ اگر با خطای permission denied مواجه شدید، پاسخ همینجاست و راهحل آن قرار دادن فایل در گروه radicale با مجوز 640 است. Radicale بهصورت پیشفرض پس از هر ورود ناموفق، یک ثانیه تأخیر ایجاد میکند، بنابراین کلاینتی که رمز عبور قدیمی دارد، بهجای اینکه بلافاصله رد شود، کند به نظر میرسد.
پاسخ nginx به متد PROPFIND خطای 405 است. این URL بهعنوان یک فایل ایستا (static file) سرو میشود، بنابراین متد WebDAV هرگز به Radicale نمیرسد. نقطه پایانی (endpoint) را مستقیماً تست کنید:
curl -u you -X PROPFIND -H "Depth: 0" -i https://cal.example.com/you/یک مجموعه DAV فعال، پاسخ 207 Multi-Status را برمیگرداند. هر پاسخ دیگری به این معناست که درخواست در وبسرور متوقف شده است.
گوشی نمیتواند حساب را تأیید کند، اما مرورگر بهدرستی کار میکند. دو دلیل رایج وجود دارد. یا redirect معروف (well-known) وجود ندارد که با دستور curl بالا قابل تست است، یا زنجیره گواهی (certificate chain) ناقص است؛ مرورگرها با دریافت گواهی میانی (intermediate) گمشده این مشکل را نادیده میگیرند، اما iOS این کار را انجام نمیدهد. آن را از طریق shell بررسی کنید:
openssl s_client -connect cal.example.com:443 -servername cal.example.com </dev/nullبه دنبال Verify return code: 0 (ok) بگردید. اگر این بررسی شکست خورد، پیکربندی nginx به جای fullchain.pem به cert.pem اشاره میکند.
رویدادهای تکراری پس از import. هر رویداد دارای یک UID است و کلاینتها از آن بهعنوان شناسه استفاده میکنند. اگر فایلی را دو بار از طریق ابزاری که شناسهها را بازتولید میکند import کنید، دو رویداد خواهید داشت که هیچگاه با هم ادغام نمیشوند. نسخههای اضافی را روی یک دستگاه حذف کنید و اجازه دهید حذف از طریق همگامسازی اعمال شود.
همه چیز پس از reboot از کار میافتد. سرویس بهصورت دستی اجرا شده است. دستور sudo systemctl is-enabled radicale وضعیت disabled را نمایش میدهد و sudo systemctl enable --now radicale مشکل را بهصورت دائمی برطرف میکند.
FAQ
آیا واقعاً برای یک سرور CalDAV شخصی به TLS نیاز دارم؟
بله. Radicale با استفاده از HTTP Basic احراز هویت میکند، بنابراین رمز عبور در هر درخواست بهصورت base64 کدگذاری شده و ارسال میشود؛ کدگذاری base64 بهسادگی قابل بازگشت است. کلاینتها نیز این موضوع را اجباری کردهاند: برنامه Calendar.app در macOS ممکن است بدون اطلاع قبلی از ارسال اعتبارنامه روی HTTP ناامن خودداری کند و iOS نیز رفتار مشابهی دارد؛ در نتیجه حساب کاربری ذخیره میشود اما هرگز همگامسازی نمیشود. sudo certbot --nginx -d cal.example.com تمام کاری است که باید انجام دهید.
چرا گوشی من نمیتواند حساب را اضافه کند در حالی که Thunderbird کار میکند؟
نرمافزار Thunderbird از آدرس URL کاملی که وارد کردهاید استفاده میکند. گوشیها تنها یک فیلد برای سرور در اختیار شما میگذارند، بنابراین به قابلیت کشف خودکار طبق RFC 6764 متکی هستند که بدون رکورد SRV، به معنای درخواست https://cal.example.com/.well-known/caldav و انتظار برای هدایت (redirect) به ریشه DAV است. اگر این درخواست با خطای 404 مواجه شود، گوشی معمولاً گزارش میدهد که قادر به تأیید حساب نیست. تنظیم location = /.well-known/caldav { return 301 https://$host/; } را به nginx اضافه کنید و سپس با curl -sI https://cal.example.com/.well-known/caldav تأیید کنید که کد وضعیت 301 و هدر location را دریافت میکنید. وارد کردن آدرس URL کامل حساب در تنظیمات پیشرفته iOS، مرحله کشف خودکار را بهطور کامل دور میزند.
آیا دو نفر میتوانند یک تقویم مشترک داشته باشند؟
بله، و مطمئنترین روش استفاده از یک حساب کاربری مشترک است. یک حساب سوم با htpasswd ایجاد کنید، تقویم مشترک را زیرمجموعه آن قرار دهید و آن را بهعنوان دومین حساب CalDAV روی هر دستگاه اضافه کنید. فایل حقوق دسترسی Radicale میتواند به یک کاربر مشخص اجازه خواندن و نوشتن روی یک مجموعه در مسیر کاربر دیگر را بدهد، اما کلاینتی که فقط تقویمهای اصلی خود را میخواند آن را نمایش نخواهد داد؛ بنابراین این روش برای Thunderbird و DAVx⁵ مناسبتر از iOS است.
اگر VPS از کار بیفتد چه بلایی سر رویدادهای من میآید؟
در Radicale، دادهها بهصورت متن ساده ذخیره میشوند: هر رویداد یک فایل .ics در مسیر /var/lib/radicale/collections/collection-root/ است که میتوانید با tar از آن نسخه پشتیبان تهیه کرده و با less آن را بخوانید. بازیابی شامل استخراج فایلها، chown -R radicale:radicale و راهاندازی مجدد سرویس است. هر کلاینت همگامسازیشده نیز یک نسخه محلی نگه میدارد، بنابراین لپتاپی که پیش از خرابی سرور بهروز بوده، یک نسخه کامل و دوم از تقویم شما را در اختیار دارد.
آیا سرور CalDAV مخاطبین من را هم همگامسازی میکند؟
مخاطبین از پروتکل CardDAV استفاده میکنند که پروتکلی مشابه و تعریفشده در RFC 6352 است و بهجای رویدادها، فایلهای vCard را ذخیره میکند. Radicale، Baikal و Nextcloud همگی این سرویس را از همان حساب و همان نام میزبان (hostname) ارائه میدهند. در اندروید، DAVx⁵ تقویمها و مخاطبین را از یک حساب همگامسازی میکند. در iOS، شما باید یک حساب دوم از نوع CardDAV با همان اعتبارنامهها اضافه کنید؛ به همین دلیل است که تنظیم هدایت /.well-known/carddav در پیکربندی nginx شما، در کنار تنظیمات CalDAV ضروری است.