VPS-এ নিজের CalDAV ক্যালেন্ডার সার্ভার সেটআপ করার নিয়ম
Google ছাড়াই ফোন ও ল্যাপটপে ক্যালেন্ডার সিঙ্ক করতে Radicale ব্যবহার করুন। এই গাইডে TLS কনফিগারেশন, সার্ভার ডিসকভারি এবং ক্লায়েন্ট সেটআপের পূর্ণাঙ্গ ধাপগুলো দেখানো হয়েছে।
আপনি যা তৈরি করছেন
একটি self-hosted ক্যালেন্ডার হলো আপনার নিয়ন্ত্রণে থাকা একটি VPS-এ চলমান একটি CalDAV সার্ভার, যা TLS-এর পেছনে সুরক্ষিত এবং প্রতিটি ব্যবহারকারীর জন্য আলাদা লগইন সুবিধা রয়েছে। আপনার পকেটের ফোন এবং ডেস্কের ল্যাপটপে একই ইভেন্ট দেখা যাবে, এমনকি আপনার সঙ্গীর ল্যাপটপেও একই তথ্য থাকবে। এর মাঝে কোনো Google অ্যাকাউন্ট থাকবে না।
এটি একটি self-hosted বুকিং পেজ থেকে ভিন্ন একটি কাজ। বুকিং পেজ অপরিচিতদের জন্য: এটি আপনার ফাঁকা সময়গুলো প্রকাশ করে এবং কাউকে একটি স্লট বুক করার সুযোগ দেয়। ক্যালেন্ডার সার্ভার আপনার নিজের ডিভাইসের জন্য: এটি ইভেন্টগুলো সংরক্ষণ করে এবং প্রতিটি ক্লায়েন্টের মধ্যে তথ্যের সামঞ্জস্য বজায় রাখে। মানুষ সাধারণত দুটিই ব্যবহার করে, এবং বুকিং টুলটি তখন আপনার তৈরি করা এই CalDAV সার্ভার থেকে প্রাপ্যতা যাচাই করে।
ইনস্টলেশনটি বেশ ছোট। Radicale একটি Python প্যাকেজ এবং এতে প্রায় দশ লাইনের কনফিগারেশন থাকে। সেটআপটি প্রথম মাস পার করতে পারবে কি না তা নির্ভর করে TLS, discovery, প্রতি ব্যবহারকারীর কালেকশন এবং ব্যাকআপের ওপর। নিচে এগুলোর ওপরই সবচেয়ে বেশি গুরুত্ব দেওয়া হয়েছে।
CalDAV কী এবং এটি কেন গুরুত্বপূর্ণ?
CalDAV হলো HTTP-এর মাধ্যমে ক্যালেন্ডার সিঙ্ক করার একটি পদ্ধতি। এটি RFC 4791-এ WebDAV (web distributed authoring and versioning, RFC 4918-এ সংজ্ঞায়িত অতিরিক্ত HTTP মেথডের একটি সেট)-এর এক্সটেনশন হিসেবে সংজ্ঞায়িত। একটি ক্যালেন্ডার হলো একটি কালেকশন, যা অনেকটা ডিরেক্টরির মতো কাজ করে। প্রতিটি ইভেন্ট এর ভেতরে থাকা একটি ফাইল, যা iCalendar টেক্সট ফরম্যাটে (RFC 5545) লেখা থাকে; এটি আপনার মেইলে থাকা .ics অ্যাটাচমেন্টের মতোই একটি ফরম্যাট।
ক্লায়েন্টরা সাধারণ HTTP-এর সাথে কিছু অতিরিক্ত মেথড ব্যবহার করে। PROPFIND জানতে চায় সেখানে কী আছে এবং এর কী কী প্রপার্টি রয়েছে। REPORT একটি ফিল্টার করা অংশ চায়, যেমন একটি নির্দিষ্ট তারিখের সীমার মধ্যে থাকা সব ইভেন্ট। PUT একটি ইভেন্ট লেখে এবং DELETE সেটি মুছে ফেলে। প্রতিটি ইভেন্টে একটি UID লাইন থাকে এবং এই আইডেন্টিফায়ারের মাধ্যমেই দুটি ডিভাইস বুঝতে পারে যে তারা একই ইভেন্ট দেখছে, কোনো কপি নয়।
এর মূল সুবিধা হলো পোর্টেবিলিটি, আর এই কারণেই এটি ব্যবহার করা হয়। iOS, macOS, Thunderbird, Evolution এবং DAVx⁵-এর মাধ্যমে Android—সবই CalDAV সাপোর্ট করে। আপনার ডেটা আপনার বর্তমান সার্ভারের সাথে আবদ্ধ নয়। ফাইলগুলো অন্য একটি CalDAV সার্ভারে সরিয়ে নিন, ক্লায়েন্টদের নতুন হোস্টনাম দেখিয়ে দিন, আর কোনো কিছু পরিবর্তন করার প্রয়োজন হবে না।
CardDAV-ও একইভাবে কাজ করে। এটি কন্টাক্টের জন্য একই ধারণা, যা RFC 6352-এ সংজ্ঞায়িত এবং ইভেন্টের পরিবর্তে vCard ফাইল সংরক্ষণ করে। নিচে উল্লিখিত প্রতিটি সার্ভার একই অ্যাকাউন্ট থেকে উভয় প্রোটোকলই সাপোর্ট করে, তাই একবার ক্যালেন্ডার সেটআপ হয়ে গেলে, অ্যাড্রেস বুক চালু করা কেবল একটি চেকবাক্সের বিষয়।
কোন CalDAV সার্ভারটি আপনার চালানো উচিত?
Radicale হলো সবচেয়ে ছোট কার্যকর সমাধান। এটি Python-এ চলে, কোনো ডাটাবেসের প্রয়োজন হয় না এবং ফাইলগুলো সাধারণ ফোল্ডার হিসেবে জমা থাকে। এই গাইডে এটি ব্যবহার করা হয়েছে কারণ একটি পারিবারিক ক্যালেন্ডারের জন্য এর চেয়ে বেশি কিছুর প্রয়োজন নেই এবং ভোর তিনটার সময় এতে কোনো সমস্যা হওয়ার সম্ভাবনা খুবই কম।
Baikal হলো এমন একটি বিকল্প যাতে একটি ওয়েব অ্যাডমিন প্যানেল রয়েছে। এটি PHP এবং sabre/dav লাইব্রেরিতে চলে, ব্যবহারকারী ও ক্যালেন্ডারের তথ্য SQLite বা MySQL-এ রাখে এবং আপনাকে কমান্ড লাইনের পরিবর্তে ব্রাউজার থেকেই নতুন ব্যবহারকারী যোগ করার সুবিধা দেয়। যখন আপনার সার্ভারে ঘনঘন অ্যাকাউন্ট যোগ বা বাদ দেওয়ার প্রয়োজন হয়, তখন এটি বেছে নিন।
Nextcloud তখন উপযুক্ত যখন ক্যালেন্ডার আপনার অনেকগুলো ফিচারের মধ্যে একটি মাত্র ফিচার। আপনি ক্যালেন্ডার, কন্টাক্ট, ফাইল এবং মোবাইল অ্যাপের সুবিধা পাবেন, তবে এর বিনিময়ে আপনাকে PHP-FPM, একটি ডাটাবেস এবং ব্যাকগ্রাউন্ড জব রানার পরিচালনা করতে হবে। যদি আপনার প্রয়োজনের তুলনায় এটি অনেক বেশি ভারী মনে হয়, তবে হালকা Nextcloud বিকল্পগুলো আপনার চাহিদা পূরণ করতে পারে এবং self-hosted file sync সেই কাজের অর্ধেক সমাধান করে যার জন্য মানুষ সাধারণত Nextcloud ইনস্টল করে।
DAViCal হলো দীর্ঘদিনের পরীক্ষিত PostgreSQL ভিত্তিক বিকল্প। এটি কেবল তখনই বিবেচনা করুন যদি আপনি ইতিমধ্যে PostgreSQL ব্যবহার করেন এবং ক্যালেন্ডারের ডাটা সেখানে রাখতে চান।
Ubuntu 24.04-এ Radicale ইনস্টল করুন
8 October 2026-এ tag করা Radicale 3.8.3, 11 October 2026 অনুযায়ী বর্তমান release। এটি নিজস্ব virtual environment-এ ইনস্টল করুন, bcrypt extra-সহ। নিচের password file এটি ব্যবহার করে।
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 system Python-এ ইনস্টল করতে গেলে error: externally-managed-environment দেখা যায়, কারণ Ubuntu তার Python-কে apt-এর মালিকানাধীন হিসেবে চিহ্নিত করে, যাতে pip packaged file overwrite করতে না পারে।
/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-এ bind করে। nginx TLS termination করে ওই port-এ অনুরোধ পাঠায়, তাই Radicale সরাসরি Internet-এর মুখোমুখি হয় না। Radicale-এর documentation-এর পুরোনো উদাহরণের 0.0.0.0:5232 একটি unencrypted service প্রকাশ করে, যা password গ্রহণ করে। এখানে এটিই গুরুত্বপূর্ণ ভুল।
এবার account তৈরি করুন। -B bcrypt নির্বাচন করে। উপরের radicale[bcrypt] install থেকে bcrypt module উপস্থিত থাকলে Radicale htpasswd_encryption = autodetect ব্যবহার করে এটি পড়ে:
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 file তৈরি করে এবং এতে আগে যা ছিল তা মুছে দেয়। এটি শুধু প্রথম user-এর জন্য ব্যবহার করুন। কয়েক মাস পরে আবার htpasswd -B -c চালালে প্রথম user-এর পরে যোগ করা প্রতিটি account মুছে যাবে। তখন একজনের sync ঠিকমতো চলবে, কিন্তু অন্য সবার কাছে এমন password prompt আসবে যা কখনো শেষ হবে না। SHA-512 crypt (-5) এখানে ব্যবহার করা যাবে না। Apache 2.4.59-এ htpasswd SHA-2 support পেয়েছে, কিন্তু Ubuntu 24.04-এ apache2-utils 2.4.58 ship করে। সেই সংস্করণের htpasswd-তে -5 flag নেই।
Radicale documentation-এর unit থেকে মানিয়ে /etc/systemd/system/radicale.service তৈরি করুন:
[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 header দেখা যায়। এর অর্থ service listening করছে এবং authentication সক্রিয়। Connection refused অর্থ ওই address-এ কিছু listening করছে না। সাধারণত service start হতে ব্যর্থ হয়েছে অথবা পরে exit করেছে। journalctl -u radicale -n 50 যে option প্রত্যাখ্যান করেছে তার নাম দেখায়। ProtectSystem=strict এই service-এর জন্য filesystem-কে read only হিসেবে mount করে। তাই ReadWritePaths=/var/lib/radicale/-ই এমন লাইন, যা event save করার অনুমতি দেয়। ওই লাইন বাদ দিলে read কাজ করবে, কিন্তু প্রতিটি write ব্যর্থ হবে।
TLS ঐচ্ছিক নয়, কারণ client-গুলো plaintext প্রত্যাখ্যান করে
এই সেটআপে HTTP Basic ব্যবহার করে authentication করা হয়। Radicale শুধু এই scheme গ্রহণ করে। Basic প্রতিটি request-এ user:password base64-এ encoded করে পাঠায়। Base64 একটি encoding, encryption নয়। Plain HTTP ব্যবহার করলে ফোন থেকে সার্ভারে যাওয়ার পথে থাকা প্রতিটি network-এর কাছে password পৌঁছে যায়—সারাদিন, প্রতিটি sync-এর সময়।
Client-গুলো এটি আপনার হয়ে enforce করে। Radicale documentation-এ বলা আছে, macOS Calendar.app unsecured HTTP-তে credentials পাঠাতে নীরবে অস্বীকৃতি জানাতে পারে। iOS-ও একইভাবে আচরণ করে। Account configured দেখা যায়, কিন্তু কখনো sync হয় না। পড়ার মতো কোনো error-ও থাকে না।
প্রথমে cal.example.com-এর জন্য একটি A record VPS-এর দিকে point করুন, কারণ 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 line Radicale documentation থেকে নেওয়া। এগুলো অপরিবর্তিত রাখুন।
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 করুন। Broken file নিয়ে reload করলে পুরোনো config চালু থাকে এবং পরবর্তী restart পর্যন্ত ভুলটি গোপন থাকে। Certbot site file-টি সরাসরি সম্পাদনা করে। এটি certificate install করে, block-টি port 443-এ পরিবর্তন করে এবং port 80 থেকে redirect যোগ করে। শেষের curl password-এর জন্য prompt দেখাবে এবং 200 ফেরত দেওয়া উচিত। এটি Radicale-এর নিজস্ব web interface। 502 Bad Gateway মানে nginx চলছে, কিন্তু port 5232 থেকে কোনো valid answer পায়নি। সাধারণত এর কারণ Radicale সেখানে listening করছে না।
ফোনে account যোগ করতে ব্যর্থ হয় কেন?
কারণ discovery। RFC 6764 বর্ণনা করে, কীভাবে একটি client hostname-কে calendar URL-এ রূপান্তর করে। এটি একটি _caldavs._tcp SRV record খোঁজে, তারপর https://cal.example.com/.well-known/caldav-এ request পাঠায় এবং DAV root-এ redirect প্রত্যাশা করে। সেখান থেকে এটি current-user-principal-এর জন্য request পাঠায়, তারপর ওই principal-এর calendar-home-set-এর জন্য request পাঠায়, এবং কেবল তখনই আপনার calendar দেখতে পায়। ফোনে server-এর জন্য একটি মাত্র field থাকে, তাই প্রতিটি ধাপ unattended অবস্থায় কাজ করতে হয়।
curl -sI https://cal.example.com/.well-known/caldavসঠিক response হলো HTTP/2 301, যার সঙ্গে একটি location: https://cal.example.com/ header থাকে। সেখানে একটি 404 থাকা একটি সাধারণ কারণ, যার জন্য iOS বলে যে account information যাচাই করা যাচ্ছে না, অথচ একই network-এ Thunderbird কাজ করে। Thunderbird আপনার দেওয়া সম্পূর্ণ URL ব্যবহার করে, তাই তার redirect-এর প্রয়োজন হয় না।
Redirect target server-এর ওপর নির্ভর করে। সাইটের root-এ Radicale serve করা হলে এটি /-এ redirect করে। Request Radicale-এ পৌঁছালে Radicale নিজেই ওই path-এর জন্য 301 response দেয়। তাই nginx rules redirect তৈরি না করে সেটিকে explicit করে। Baikal-এর সঙ্গে এমন sample rules থাকে, যা 308 status ব্যবহার করে /dav.php-এ redirect করে। Nextcloud /remote.php/dav/-এ redirect করে।
ক্যালেন্ডার তৈরি করুন এবং আপনার সঙ্গীর সাথে একটি শেয়ার করুন
অনেক ক্লায়েন্ট ক্যালেন্ডার তৈরি করতে পারে না, শুধুমাত্র সাবস্ক্রাইব করতে পারে। ব্রাউজারে https://cal.example.com/ খুলুন, you হিসেবে লগ ইন করুন এবং সেখানে ক্যালেন্ডারটি তৈরি করুন। ডিস্কে এটি /var/lib/radicale/collections/collection-root/you/-এর অধীনে জমা হয়, যেখানে ফোল্ডারের নামের জন্য একটি জেনারেটেড আইডেন্টিফায়ার থাকে।
Radicale-এর ডিফল্ট রাইটস ব্যাকএন্ড হলো owner_only: একটি অথেন্টিকেটেড অ্যাকাউন্ট শুধুমাত্র /USERNAME/-এর অধীনে তার নিজস্ব কালেকশন পড়তে এবং লিখতে পারে, অন্য কিছু নয়। বেশিরভাগ পরিবারের জন্য এটিই সঠিক সেটিংস এবং ক্যালেন্ডার শেয়ার করার সবচেয়ে সহজ উপায় হলো একটি তৃতীয় অ্যাকাউন্ট ব্যবহার করা। htpasswd দিয়ে household তৈরি করুন, সেই লগইনের অধীনে শেয়ার করা ক্যালেন্ডারটি তৈরি করুন এবং প্রতিটি ডিভাইসে এটিকে দ্বিতীয় CalDAV অ্যাকাউন্ট হিসেবে যোগ করুন। এটি iOS সহ প্রতিটি ক্লায়েন্টে কাজ করে, কারণ ক্যালেন্ডারটি সেই অ্যাকাউন্টের নিজস্ব হোমে থাকে।
যখন আপনি আরও সূক্ষ্ম নিয়ন্ত্রণ চান, তখন রুল-ভিত্তিক রাইটসে স্যুইচ করুন। /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 এমন কালেকশন পড়ে এবং লেখে যা ক্যালেন্ডার বা অ্যাড্রেস বুক নয়, যা মূলত একটি প্রিন্সিপাল ফোল্ডার। r এবং w ক্যালেন্ডারগুলো নিজে পড়ে এবং লেখে। উপরের স্টোরেজ পাথ থেকে আপনার ক্যালেন্ডারের আসল ফোল্ডারের নাম দিয়ে সেই আইডেন্টিফায়ারটি প্রতিস্থাপন করুন।
একটি সীমাবদ্ধতা হলো: যে ক্লায়েন্ট শুধুমাত্র ক্যালেন্ডার হোম সেট পড়ে, সেটি অন্য ব্যবহারকারীর পাথে থাকা ক্যালেন্ডার প্রদর্শন করবে না, কারণ ডিসকভারি সেখানে যায় না। Thunderbird এবং DAVx⁵ সম্পূর্ণ URL ব্যবহার করে এটি যোগ করতে পারে। iOS তা পারে না, আর এই কারণেই শেয়ার করা অ্যাকাউন্টের পদ্ধতিটি সবসময় কাজ করে।
ক্লায়েন্ট সেটআপ করুন, কারণ এখানেই self-hosted ক্যালেন্ডার ব্যবহারের সমাপ্তি ঘটে
iPhone এবং iPad। Settings খুলুন, তারপর Calendar (সাম্প্রতিক iOS ভার্সনে এটি Apps-এর অধীনে থাকে), তারপর Calendar Accounts, Add Account, Other, Add CalDAV Account-এ যান। Server হিসেবে cal.example.com দিন, এরপর ব্যবহারকারীর নাম ও পাসওয়ার্ড লিখুন। Description শুধুমাত্র একটি লেবেল। যদি এটি সেভ না হয়, তবে অ্যাকাউন্টটি আবার খুলুন: advanced ভিউতে Use SSL, port এবং সম্পূর্ণ account URL দেখা যাবে; URL-টি পেস্ট করলে তা সরাসরি discovery প্রক্রিয়া এড়িয়ে যাবে।
Android। এতে কোনো বিল্ট-ইন CalDAV ক্লায়েন্ট নেই। F-Droid বা Google Play থেকে DAVx⁵ ইনস্টল করুন, আপনার ব্যবহারকারীর নামসহ https://cal.example.com/ বেস URL ব্যবহার করে একটি অ্যাকাউন্ট যোগ করুন, তারপর আপনার প্রয়োজনীয় ক্যালেন্ডারগুলো টিক দিন। DAVx⁵ সরাসরি Android ক্যালেন্ডার প্রোভাইডারে তথ্য লেখে, তাই আপনার ব্যবহৃত যেকোনো ক্যালেন্ডার অ্যাপেই ইভেন্টগুলো দেখা যাবে।
Thunderbird। New Calendar, On the Network, তারপর আপনার ব্যবহারকারীর নাম এবং https://cal.example.com/ লোকেশন দিন। এটি কী কী খুঁজে পেয়েছে তার তালিকা দেখাবে এবং কোন ক্যালেন্ডারগুলো যোগ করতে হবে তা জানতে চাইবে।
macOS। System Settings, Internet Accounts, Add Other Account, CalDAV-এ যান, Account Type ম্যানুয়াল হিসেবে সেট করুন, তারপর একই ব্যবহারকারীর নাম, পাসওয়ার্ড এবং সার্ভার অ্যাড্রেস দিন।
CalDAV একটি পোলিং প্রোটোকল। এর স্পেসিফিকেশনে কোনো পুশ (push) সুবিধা নেই, তাই ল্যাপটপে কোনো ইভেন্ট যোগ করলে তা সাথে সাথে ফোনে না এসে পরবর্তী সিঙ্ক-এর সময় আসবে। প্রতিটি ক্লায়েন্টে এমন একটি বিরতি (interval) সেট করুন যা আপনার জন্য সুবিধাজনক, তবে মনে রাখবেন ফোনে বিরতির সময় কম হলে ব্যাটারি বেশি খরচ হয়।
স্টোর ব্যাকআপ নিন, যা কেবল কিছু ফাইল
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এই দুটি ডিরেক্টরিই কেবল web server দ্বারা রাইট করা হয়, তাই অন্য কোনো কিছু রাইট করার অনুমতি দেওয়ার প্রয়োজন নেই। আপনার 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; }nginx রিলোড করুন, ব্রাউজারে সাইটটি ওপেন করুন, এবং সেটআপ উইজার্ডটি অ্যাডমিন অ্যাকাউন্ট ও SQLite ডাটাবেস তৈরি করবে। ক্লায়েন্ট সেটআপ Radicale-এর মতোই, যেখানে সার্ভার অ্যাড্রেস হিসেবে https://cal.example.com/ ব্যবহার করতে হবে, কারণ well-known রুলটি ডিসকভারি রিকোয়েস্টকে /dav.php-এ পাঠিয়ে দেয়।
Nextcloud তখনই কার্যকর হয় যদি আপনি একই লগইনের অধীনে ফাইল এবং ফোন অ্যাপও ব্যবহার করতে চান। এর DAV রুট হলো /remote.php/dav/, এবং একই ডিসকভারি রুল এখানেও প্রযোজ্য। এগুলোর যেকোনোটি ব্যবহারের ক্ষেত্রে, সার্ভিসটিকে কন্টেইনারে চালালে আপনার হোস্ট মেশিনের PHP ভার্সনের সাথে কোনো সংঘর্ষ হবে না: Docker Compose on a VPS-এ compose ফাইল এবং এর সামনের reverse proxy সম্পর্কে আলোচনা করা হয়েছে, এবং what is worth self-hosting in 2026 নিবন্ধটি আপনাকে সিদ্ধান্ত নিতে সাহায্য করবে যে আপনি এই পথে কতদূর এগিয়ে যেতে চান।
ব্যর্থতার ধরন এবং যে বার্তাগুলো আপনি দেখবেন
প্রতিটি সিঙ্ক 401 (401) এরর দেয়। হয় পাসওয়ার্ড ফাইলে থাকা অ্যাকাউন্টগুলো অন্য কোনো htpasswd -c এর কারণে মুছে গেছে, অথবা radicale ব্যবহারকারী ফাইলটি পড়তে পারছে না। sudo -u radicale cat /etc/radicale/users দিয়ে পরীক্ষা করুন; যদি 'permission denied' দেখায় তবে সেটিই আপনার সমস্যার কারণ। সমাধান হলো ফাইলটিকে radicale গ্রুপে রাখা এবং মোড 640 (640) সেট করা। Radicale ডিফল্টভাবে প্রতিটি ব্যর্থ লগইনের পর এক সেকেন্ড অপেক্ষা করে, তাই পুরনো পাসওয়ার্ড ব্যবহারকারী ক্লায়েন্টকে রিজেক্ট হওয়ার বদলে ধীরগতির মনে হতে পারে।
PROPFIND এর জন্য nginx 405 (405) এরর দেয়। URL-টিকে একটি স্ট্যাটিক ফাইল হিসেবে পরিবেশন করা হচ্ছে, তাই WebDAV মেথডটি Radicale পর্যন্ত পৌঁছাতে পারছে না। এন্ডপয়েন্টটি সরাসরি পরীক্ষা করুন:
curl -u you -X PROPFIND -H "Depth: 0" -i https://cal.example.com/you/একটি কার্যকর DAV কালেকশন 207 Multi-Status উত্তর দেয়। অন্য যেকোনো কিছু আসার অর্থ হলো অনুরোধটি ওয়েব সার্ভারেই আটকে গেছে।
ফোন অ্যাকাউন্ট যাচাই করতে পারছে না, কিন্তু ব্রাউজারে ঠিকঠাক কাজ করছে। এর দুটি সাধারণ কারণ থাকে। ওয়েল-নোন রিডাইরেক্ট (well-known redirect) অনুপস্থিত, যা উপরের curl কমান্ড দিয়ে পরীক্ষা করা যায়। অথবা সার্টিফিকেট চেইন অসম্পূর্ণ, যা ব্রাউজারগুলো স্বয়ংক্রিয়ভাবে ইন্টারমিডিয়েট সার্টিফিকেট খুঁজে ঠিক করে নেয়, কিন্তু iOS তা করে না। শেল থেকে এটি পরীক্ষা করুন:
openssl s_client -connect cal.example.com:443 -servername cal.example.com </dev/nullVerify return code: 0 (ok) খুঁজুন। যদি এটি ব্যর্থ হয়, তবে nginx কনফিগারেশন cert.pem কে নির্দেশ করছে যেখানে এটিকে fullchain.pem কে নির্দেশ করা উচিত ছিল।
ইমপোর্টের পর ইভেন্ট ডুপ্লিকেট হয়ে যায়। প্রতিটি ইভেন্টের একটি UID থাকে এবং ক্লায়েন্টরা সেটিকে পরিচয় হিসেবে গণ্য করে। একই ফাইল এমন কোনো টুলের মাধ্যমে দুবার ইমপোর্ট করলে যা আইডেন্টিফায়ার নতুন করে তৈরি করে, তখন দুটি ইভেন্ট তৈরি হয় যা আর কখনো মার্জ হবে না। একটি ডিভাইস থেকে অতিরিক্ত কপিগুলো মুছে ফেলুন এবং ডিলিট সিঙ্ক হতে দিন।
রিবুটের পর সবকিছু কাজ করা বন্ধ করে দেয়। সার্ভিসটি ম্যানুয়ালি চালু করা হয়েছিল। sudo systemctl is-enabled radicale কমান্ডটি disabled প্রিন্ট করে, এবং sudo systemctl enable --now radicale কমান্ডটি স্থায়ীভাবে সমস্যার সমাধান করে।
FAQ
একটি self-hosted CalDAV server-এর জন্য কি সত্যিই TLS দরকার?
হ্যাঁ। Radicale HTTP Basic authentication ব্যবহার করে। তাই প্রতিটি request-এ password base64 encoding-সহ পাঠানো হয়, আর base64 সহজেই আগের রূপে ফেরানো যায়। Client-গুলোও TLS প্রয়োগ করে। macOS Calendar.app অসুরক্ষিত HTTP-তে credentials পাঠাতে নীরবে অস্বীকৃতি জানাতে পারে। iOS-ও একই আচরণ করে। ফলে account save হয়েছে বলে মনে হলেও আর কখনো sync হয় না। sudo certbot --nginx -d cal.example.com-ই পুরো কাজটি করে।
Thunderbird কাজ করলেও আমার phone account যোগ করতে ব্যর্থ হয় কেন?
Thunderbird আপনার লেখা সম্পূর্ণ URL ব্যবহার করে। Phone-এ সাধারণত একটি server field থাকে। তাই এটি RFC 6764 discovery-এর ওপর নির্ভর করে। SRV record না থাকলে এটি https://cal.example.com/.well-known/caldav request করে এবং DAV root-এ redirect প্রত্যাশা করে। ওই request-এর ফল 404 হলে phone সাধারণত জানায় যে account যাচাই করা যাচ্ছে না। nginx-এ location = /.well-known/caldav { return 301 https://$host/; } যোগ করুন। তারপর curl -sI https://cal.example.com/.well-known/caldav দিয়ে নিশ্চিত করুন যে 301 এবং একটি location header পাওয়া যাচ্ছে। iOS-এর advanced settings-এ সম্পূর্ণ account URL paste করলে discovery পুরোপুরি এড়িয়ে যাওয়া যায়।
দুইজন কি একটি calendar share করতে পারেন?
হ্যাঁ। নির্ভরযোগ্য পদ্ধতি হলো shared login ব্যবহার করা। htpasswd দিয়ে তৃতীয় একটি account তৈরি করুন। Shared calendar-টি ওই account-এর অধীনে রাখুন। তারপর প্রতিটি device-এ এটিকে দ্বিতীয় CalDAV account হিসেবে যোগ করুন। Radicale-এর rights file-এর মাধ্যমে অন্য user-এর path-এর অধীন একটি collection-এ নির্দিষ্ট user-কে read ও write access দেওয়া যায়। তবে যে client শুধু নিজের calendar home set পড়ে, সেটি calendar-টি কখনো দেখাবে না। তাই এই পদ্ধতি iOS-এর চেয়ে Thunderbird এবং DAVx⁵-এর জন্য বেশি উপযোগী।
VPS নষ্ট হলে আমার event-গুলোর কী হবে?
Radicale-এ store plain text হিসেবে থাকে। /var/lib/radicale/collections/collection-root/-এর অধীনে প্রতিটি event-এর জন্য একটি .ics file থাকে। tar দিয়ে সেগুলোর backup নেওয়া যায় এবং less দিয়ে পড়া যায়। Restore করার ধাপ হলো extract করা, chown -R radicale:radicale চালানো, তারপর service start করা। প্রতিটি synced client-ও একটি local copy রাখে। তাই failure-এর আগে যে laptop সর্বশেষ অবস্থায় sync করা ছিল, তাতে calendar-এর সম্পূর্ণ দ্বিতীয় একটি copy থাকে।
CalDAV server কি আমার contact-ও sync করে?
Contact-এর জন্য CardDAV ব্যবহার করা হয়। এটি RFC 6352-এ সংজ্ঞায়িত একটি sibling protocol এবং event-এর বদলে vCard file সংরক্ষণ করে। Radicale, Baikal এবং Nextcloud একই account ও একই hostname থেকে এটি serve করে। Android-এ DAVx⁵ একটি account থেকেই calendar ও contact sync করে। iOS-এ একই credentials দিয়ে CardDAV type-এর একটি দ্বিতীয় account যোগ করতে হয়। তাই CalDAV redirect-এর পাশে /.well-known/carddav redirect-টিও nginx config-এ থাকতে হবে।