Installer un serveur CalDAV sur un VPS
Synchronisez vos calendriers entre téléphone et ordinateur sans Google. Installez Radicale sur votre VPS avec TLS, découverte automatique et configuration des clients.
Ce que vous allez mettre en place
Un calendrier auto-hébergé consiste en un serveur CalDAV installé sur un VPS que vous contrôlez, derrière TLS, avec un compte par personne. Le téléphone que vous avez avec vous et l’ordinateur portable posé sur votre bureau affichent les mêmes événements, tout comme l’ordinateur portable de votre partenaire. Aucun compte Google ne sert d’intermédiaire.
Il s’agit d’un usage différent de une page de réservation auto-hébergée. Une page de réservation s’adresse à des personnes externes : elle publie vos créneaux disponibles et leur permet d’en réserver un. Un serveur de calendrier s’adresse à vos propres appareils : il stocke les événements et maintient tous les clients synchronisés. Il est courant d’utiliser les deux. L’outil de réservation lit alors ses disponibilités sur le serveur CalDAV que vous allez mettre en place ici.
L’installation est légère. Radicale est un package Python et nécessite environ dix lignes de configuration. La pérennité de l’installation dépend surtout de TLS, de la découverte automatique, des collections propres à chaque utilisateur et des sauvegardes. Ces points occupent l’essentiel de la suite.
Qu’est-ce que CalDAV et pourquoi est-ce important ?
CalDAV est un protocole de synchronisation de calendriers sur HTTP. Il est défini par la RFC 4791 comme un ensemble d’extensions de WebDAV (web distributed authoring and versioning, un ensemble de méthodes HTTP supplémentaires défini par la RFC 4918). Un calendrier est une collection qui se comporte comme un répertoire. Un événement est un fichier à l’intérieur de cette collection, écrit au format texte iCalendar (RFC 5545), le même format que les pièces jointes .ics de vos e-mails.
Les clients utilisent HTTP standard avec quelques méthodes supplémentaires. PROPFIND demande quels éléments sont présents et quelles sont leurs propriétés. REPORT demande une sélection filtrée, par exemple tous les événements d’une plage de dates. PUT écrit un événement et DELETE le supprime. Chaque événement contient une ligne UID. Cet identifiant permet à deux appareils de vérifier qu’ils consultent le même événement, et non une copie.
La portabilité est le principal avantage, et c’est la raison essentielle d’utiliser CalDAV. iOS, macOS, Thunderbird, Evolution et Android avec DAVx⁵ prennent tous en charge CalDAV. Vos données ne sont pas liées au serveur que vous avez choisi aujourd’hui. Déplacez les fichiers vers un autre serveur CalDAV, indiquez le nouveau hostname aux clients, et rien d’autre ne change.
CardDAV fonctionne de la même manière. Il s’agit du même principe pour les contacts, défini dans la RFC 6352, avec des fichiers vCard à la place des événements. Chaque serveur ci-dessous fournit les deux protocoles avec le même compte. Une fois le calendrier opérationnel, le carnet d’adresses se résume à cocher une case.
Quel serveur CalDAV devriez-vous utiliser ?
Radicale est la solution la plus légère qui fonctionne. Il utilise Python, ne nécessite aucune base de données et stocke les données dans un dossier contenant des fichiers texte. Ce guide l’utilise parce qu’un calendrier familial n’a pas besoin de davantage, et parce qu’il offre très peu de points de panne au milieu de la nuit.
Baikal est l’option dotée d’un panneau d’administration web. Il fonctionne avec PHP et la bibliothèque sabre/dav, stocke les utilisateurs et les calendriers dans SQLite ou MySQL, et permet d’ajouter une personne dans un navigateur plutôt qu’en ligne de commande. Choisissez-le lorsque les comptes sont fréquemment créés et supprimés.
Nextcloud convient lorsque le calendrier n’est qu’une fonctionnalité parmi d’autres. Vous disposez d’un calendrier, de contacts, de fichiers et d’une application mobile, au prix de PHP-FPM, d’une base de données et d’un exécuteur de tâches en arrière-plan. Si cela vous semble trop lourd par rapport à votre besoin réel, les alternatives plus légères à Nextcloud présentent les compromis, et la synchronisation de fichiers auto-hébergée couvre l’autre moitié des usages qui motivent l’installation de Nextcloud.
DAViCal est l’option PostgreSQL historique. Elle mérite votre attention uniquement si vous utilisez déjà PostgreSQL et souhaitez y stocker les données de calendrier.
Installer Radicale sur Ubuntu 24.04
Radicale 3.5.10 était la version actuelle en août 2026. Installez-le dans son propre environnement virtuel.
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 radicaleL’environnement virtuel n’est pas une question de style. sudo pip install radicale dans le Python système échoue avec error: externally-managed-environment, car Ubuntu considère son Python comme géré par apt afin que pip ne puisse pas écraser les fichiers des paquets.
Écrivez /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 se lie volontairement à la loopback. nginx termine TLS et transfère les requêtes vers ce port. Radicale n’est donc jamais directement exposé à Internet. L’exemple amont de 0.0.0.0:5232 publie un service non chiffré qui accepte les mots de passe. C’est l’erreur importante à éviter ici.
Passons aux comptes. -5 sélectionne crypt en SHA-512, que Radicale lit avec htpasswd_encryption = autodetect sans module supplémentaire :
sudo htpasswd -5 -c /etc/radicale/users you
sudo htpasswd -5 /etc/radicale/users partner
sudo chown root:radicale /etc/radicale/users
sudo chmod 640 /etc/radicale/users-c crée le fichier et tronque son contenu existant. Utilisez-le uniquement pour le premier utilisateur. Exécuter htpasswd -5 -c de nouveau plusieurs mois plus tard supprime tous les comptes ajoutés après le premier. Le symptôme est le suivant : une personne synchronise correctement, tandis que toutes les autres voient une demande de mot de passe qui ne se termine jamais. Bcrypt fonctionne également et nécessite l’installation supplémentaire radicale[bcrypt].
Créez /etc/systemd/system/radicale.service en l’adaptant à partir de l’unité présentée dans la documentation de 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/Un résultat correct est 401 Unauthorized avec un en-tête WWW-Authenticate : le service écoute et l’authentification est activée. Connection refused signifie qu’il n’a jamais démarré, et journalctl -u radicale -n 50 indique l’option qu’il a rejetée. ProtectSystem=strict monte le système de fichiers en lecture seule pour ce service. ReadWritePaths=/var/lib/radicale/ est donc la ligne qui lui permet d’enregistrer un événement. Si vous supprimez cette ligne, les lectures continuent de fonctionner, mais toutes les écritures échouent.
TLS n’est pas facultatif, car les clients refusent le texte en clair
CalDAV s’authentifie avec HTTP Basic, qui envoie user:password encodé en base64 à chaque requête. Base64 est un encodage, pas un chiffrement. Avec HTTP en clair, vous transmettez le mot de passe à tous les réseaux situés entre le téléphone et le serveur, toute la journée, à chaque synchronisation.
Les clients appliquent cette règle pour vous. La documentation de Radicale indique que l’application Calendrier de macOS peut refuser silencieusement d’envoyer les identifiants sur HTTP non sécurisé, et iOS se comporte de la même manière. Le compte semble configuré, mais ne se synchronise jamais, sans message d’erreur exploitable.
Pointez d’abord un enregistrement A pour cal.example.com vers le VPS, car l’autorité de certification le vérifie. Créez ensuite /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/; }
}Les quatre lignes d’en-têtes du proxy proviennent de la documentation de Radicale. Laissez-les telles quelles.
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 affiche syntax is ok et test is successful. Effectuez le reload uniquement après cette vérification, car un reload avec un fichier incorrect laisse l’ancienne configuration active et masque l’erreur jusqu’au prochain redémarrage. Certbot modifie le fichier du site directement : il installe le certificat, bascule le bloc sur le port 443 et ajoute une redirection du port 80. La commande curl finale demande le mot de passe et doit renvoyer 200, qui est l’interface web de Radicale. Un résultat 502 Bad Gateway signifie que nginx fonctionne et que Radicale n’écoute pas sur le port 5232.
Pourquoi l’ajout du compte échoue-t-il sur un téléphone ?
La cause est la découverte automatique. La RFC 6764 décrit comment un client transforme un nom d’hôte en URL de calendrier. Il recherche un enregistrement SRV _caldavs._tcp, puis demande https://cal.example.com/.well-known/caldav et attend une redirection vers la racine DAV. Il demande ensuite current-user-principal, puis le calendar-home-set de ce principal, et ce n’est qu’à ce moment qu’il voit vos calendriers. Un téléphone ne fournit qu’un seul champ pour le serveur. Chaque étape doit donc fonctionner sans intervention.
curl -sI https://cal.example.com/.well-known/caldavLa réponse correcte est HTTP/2 301 avec un en-tête location: https://cal.example.com/. La présence de 404 à cet emplacement explique pourquoi iOS indique qu’il ne peut pas vérifier les informations du compte, alors que Thunderbird fonctionne sur le même réseau : Thunderbird utilise l’URL complète que vous avez saisie et n’a donc jamais besoin de la redirection.
La cible de la redirection dépend du serveur. Radicale, lorsqu’il est servi à la racine du site, redirige vers /. Baikal fournit des règles d’exemple qui redirigent vers /dav.php avec le code d’état 308. Nextcloud redirige vers /remote.php/dav/.
Créez les calendriers et partagez-en un avec votre partenaire
De nombreux clients ne peuvent pas créer de calendrier et peuvent seulement s’y abonner. Ouvrez https://cal.example.com/ dans un navigateur, connectez-vous avec you, puis créez le calendrier à cet endroit. Sur le disque, il est placé sous /var/lib/radicale/collections/collection-root/you/, avec un identifiant généré comme nom de dossier.
Le backend de droits par défaut de Radicale est owner_only : un compte authentifié lit et modifie ses propres collections sous /USERNAME/, et rien d’autre. Pour la plupart des foyers, ce réglage convient. La méthode la plus simple pour partager un calendrier consiste à utiliser un troisième compte. Créez household avec htpasswd, créez le calendrier partagé avec ce compte, puis ajoutez-le sur chaque appareil comme second compte CalDAV. Cette méthode fonctionne avec tous les clients, y compris iOS, car le calendrier se trouve dans le home de ce compte.
Pour un contrôle plus précis, utilisez des règles de droits. Ajoutez ceci à /etc/radicale/config :
[rights]
type = from_file
file = /etc/radicale/rightsPuis /etc/radicale/rights, en vous basant sur l’exemple de la documentation 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: rwLes majuscules et les minuscules ont des significations différentes. R et W lisent et modifient les collections qui ne sont ni des calendriers ni des carnets d’adresses, ce qui correspond à un dossier principal. r et w lisent et modifient les calendriers eux-mêmes. Remplacez cet identifiant par le nom réel du dossier de votre calendrier, indiqué dans le chemin de stockage ci-dessus.
Une limite doit être clairement précisée : un client qui lit uniquement l’ensemble des homes de calendriers n’affichera pas un calendrier situé sous le chemin d’un autre utilisateur, car la découverte ne parcourt jamais ce chemin. Thunderbird et DAVx⁵ peuvent l’ajouter avec son URL complète. iOS ne le peut pas. C’est pourquoi la méthode avec un compte partagé fonctionne toujours.
Configurer les clients, car c’est à cette étape que les calendriers auto-hébergés cessent souvent de fonctionner
iPhone et iPad. Ouvrez Réglages, puis Calendrier (dans Apps sur les versions récentes d’iOS), puis Comptes Calendrier, Ajouter un compte, Autre, Ajouter un compte CalDAV. Le serveur est cal.example.com, suivi du nom d’utilisateur et du mot de passe. La description sert uniquement de libellé. Si l’enregistrement échoue, ouvrez de nouveau le compte : la vue avancée affiche Use SSL, le port et l’URL complète du compte. Coller l’URL permet de désactiver complètement la découverte automatique.
Android. Android ne fournit aucun client CalDAV intégré. Installez DAVx⁵ depuis F-Droid ou Google Play, ajoutez un compte à l’aide de l’URL de base https://cal.example.com/ et de votre nom d’utilisateur, puis cochez les calendriers souhaités. DAVx⁵ écrit dans le fournisseur de calendriers Android. Les événements apparaissent donc dans l’application de calendrier que vous utilisez déjà.
Thunderbird. Nouveau calendrier, Sur le réseau, puis votre nom d’utilisateur et l’emplacement https://cal.example.com/. Thunderbird affiche les éléments trouvés et vous demande quels calendriers ajouter.
macOS. Réglages Système, Comptes Internet, Ajouter un autre compte, CalDAV. Définissez Type de compte sur Manuel, puis saisissez le même nom d’utilisateur, le même mot de passe et la même adresse de serveur.
CalDAV est un protocole d’interrogation périodique. La spécification ne prévoit aucun push. Un événement ajouté sur l’ordinateur portable arrive donc sur le téléphone lors de la prochaine synchronisation, et non à la seconde près. Définissez dans chaque client un intervalle acceptable pour vous. Gardez à l’esprit qu’un intervalle plus court sur un téléphone réduit l’autonomie de la batterie.
Sauvegarder le contenu, qui se limite à des fichiers
Avec Radicale, votre calendrier est un répertoire de fichiers .ics, avec un fichier par événement, ainsi qu’un petit fichier de propriétés par collection. Toute commande qui copie un répertoire crée une sauvegarde. Vous pouvez ouvrir une sauvegarde avec less pour vérifier qu’elle contient bien des événements. C’est un avantage réel par rapport à un dump de base de données que vous ne pouvez pas lire.
sudo systemctl stop radicale
sudo tar czf /root/radicale-$(date +%F).tar.gz -C /var/lib/radicale collections
sudo systemctl start radicaleArrêtez le service pendant les quelques secondes nécessaires à la création de l’archive. Ainsi, aucun client ne sera au milieu d’une écriture lorsque les fichiers seront lus. Copiez ensuite l’archive hors du serveur, car une sauvegarde stockée sur le même VPS ne résiste pas à la panne contre laquelle vous vous préparez. La restauration suit l’opération inverse : extrayez l’archive, sudo chown -R radicale:radicale /var/lib/radicale/collections, puis démarrez le service. Chaque client conserve également une copie locale de ses calendriers. Un ordinateur portable qui n’a pas synchronisé ses données depuis la panne constitue donc une seconde copie de vos données.
Quand Baikal ou Nextcloud est le meilleur choix
Baikal 0.12.1 est sorti le 5 août 2026 et nécessite PHP 8.2 ou une version ultérieure. Décompressez-le en dehors de la racine web et n’exposez que son répertoire 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/configCes deux répertoires sont les seuls dans lesquels le serveur web écrit. Aucun autre répertoire ne doit donc être accessible en écriture. Dans votre bloc server nginx, les éléments spécifiques à Baikal sont les suivants :
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; }Rechargez nginx, ouvrez le site dans un navigateur, puis l’assistant de configuration crée le compte administrateur et la base de données SQLite. La configuration des clients est identique à celle de Radicale, avec https://cal.example.com/ comme adresse du serveur, car la règle well-known redirige la découverte vers /dav.php.
Nextcloud n’est pertinent que si vous voulez également gérer des fichiers et utiliser une application mobile avec le même compte. Sa racine DAV est /remote.php/dav/, et les mêmes règles de découverte s’appliquent. Pour tous ces services, l’exécution dans un conteneur évite d’installer différentes versions de PHP sur l’hôte : Docker Compose sur un VPS présente le fichier Compose et le reverse proxy placé devant, tandis que ce qui mérite d’être auto-hébergé en 2026 vous aide à décider jusqu’où vous souhaitez aller dans cette direction.
Modes de panne et chaînes que vous verrez
Chaque synchronisation renvoie 401. Soit le fichier de mots de passe a perdu ses comptes lors d’un second htpasswd -c, soit l’utilisateur radicale ne peut pas le lire. Vérifiez avec sudo -u radicale cat /etc/radicale/users ; un message « permission denied » vous donne la cause, et la correction consiste à utiliser le groupe radicale avec le mode 640. Par défaut, Radicale attend également une seconde après chaque échec d’authentification. Un client utilisant un ancien mot de passe semble donc lent plutôt que rejeté.
nginx renvoie 405 pour PROPFIND. L’URL est servie comme un fichier statique. La méthode WebDAV n’atteint donc jamais Radicale. Testez directement le endpoint :
curl -u you -X PROPFIND -H "Depth: 0" -i https://cal.example.com/you/Une collection DAV fonctionnelle renvoie 207 Multi-Status. Toute autre réponse signifie que la requête s’est arrêtée au niveau du serveur web.
Le téléphone ne peut pas vérifier le compte, alors que le navigateur fonctionne. Deux causes sont courantes. La redirection well-known est absente ; testez-la avec la commande curl précédente. Ou bien la chaîne de certificats est incomplète. Les navigateurs corrigent souvent ce problème en récupérant l’intermédiaire manquant, contrairement à iOS. Vérifiez depuis le shell :
openssl s_client -connect cal.example.com:443 -servername cal.example.com </dev/nullRecherchez Verify return code: 0 (ok). Si la vérification échoue, la configuration nginx pointe vers cert.pem au lieu de fullchain.pem.
Événements en double après un import. Chaque événement contient un UID, que les clients utilisent comme identifiant. Si vous importez deux fois le même fichier avec un outil qui régénère les identifiants, vous obtenez deux événements que rien ne pourra fusionner. Supprimez les copies supplémentaires sur un appareil, puis laissez la suppression se synchroniser.
Tout cesse de fonctionner après un redémarrage. Le service a été démarré manuellement. sudo systemctl is-enabled radicale affiche disabled, et sudo systemctl enable --now radicale corrige le problème définitivement.
FAQ
Ai-je réellement besoin de TLS pour un serveur CalDAV auto-hébergé ?
Oui. CalDAV s’authentifie avec HTTP Basic. Le mot de passe est donc transmis encodé en base64 dans chaque requête, et base64 est trivial à décoder. Les clients l’imposent également : macOS Calendar.app peut refuser silencieusement d’envoyer des identifiants sur HTTP non sécurisé, et iOS se comporte de la même manière. Le compte semble alors être enregistré, mais ne se synchronise jamais. sudo certbot --nginx -d cal.example.com constitue l’ensemble de la configuration nécessaire.
Pourquoi mon téléphone n’arrive-t-il pas à ajouter le compte alors que Thunderbird fonctionne ?
Thunderbird utilise l’URL complète que vous avez saisie. Un téléphone ne fournit qu’un champ de serveur. Il suit donc le mécanisme de découverte de la RFC 6764 : il demande https://cal.example.com/.well-known/caldav et attend une redirection vers la racine DAV. Sans cette redirection, le téléphone reçoit une erreur 404 et indique qu’il ne peut pas vérifier le compte. Ajoutez location = /.well-known/caldav { return 301 https://$host/; } à nginx, puis vérifiez avec curl -sI https://cal.example.com/.well-known/caldav que vous obtenez une réponse 301 et un en-tête location.
Deux personnes peuvent-elles partager un calendrier ?
Oui. La méthode la plus fiable consiste à utiliser un compte partagé. Créez un troisième compte avec htpasswd, placez le calendrier partagé sous ce compte, puis ajoutez-le comme deuxième compte CalDAV sur chaque appareil. Le fichier de droits de Radicale peut aussi accorder à un utilisateur nommé les droits de lecture et d’écriture sur une collection située sous le chemin d’un autre utilisateur. Toutefois, un client qui ne lit que son propre ensemble de calendriers ne l’affichera jamais. Cette méthode convient donc à Thunderbird et à DAVx⁵ plutôt qu’à iOS.
Que deviennent mes événements si le VPS tombe en panne ?
Avec Radicale, le stockage utilise du texte brut : un fichier .ics par événement sous /var/lib/radicale/collections/collection-root/. Vous pouvez le sauvegarder avec tar et le lire avec less. Pour restaurer les données, extrayez-les, exécutez chown -R radicale:radicale, puis démarrez le service. Chaque client synchronisé conserve également une copie locale. Un ordinateur portable à jour avant la panne contient donc une deuxième copie complète de votre calendrier.
Un serveur CalDAV synchronise-t-il aussi mes contacts ?
Les contacts utilisent CardDAV, un protocole associé défini dans la RFC 6352. Il stocke des fichiers vCard à la place des événements. Radicale, Baikal et Nextcloud le fournissent tous avec le même compte et le même nom d’hôte. Sous Android, DAVx⁵ synchronise les calendriers et les contacts depuis un seul compte. Sous iOS, vous ajoutez un deuxième compte de type CardDAV avec les mêmes identifiants. C’est pourquoi la redirection /.well-known/carddav doit figurer dans votre configuration nginx à côté de celle de CalDAV.