← Blogue

Le HTTPS pour plusieurs applications sur un seul serveur

Railyard

11 juin 2026 · 9 min de lecture

Suivez ça sur votre propre serveur. Commencer gratuitement, sans carte

Un seul serveur suffit largement pour plusieurs petites applications : un blogue, une API interne, une page de statut, aucune n’ayant besoin d’une machine rien qu’à elle. Ce qu’elles ont toutes besoin, c’est de répondre en HTTPS chacune sur son propre sous-domaine, avec un certificat réellement valide et qui se renouvelle réellement, sans que vous lanciez certbot renew à la main tous les quelques mois ou écriviez une configuration nginx et une tâche cron séparées par site. Une seule instance Caddy devant tout ça fait le travail avec moins de configuration qu’on pourrait croire, en émettant et en renouvelant un certificat par domaine depuis un seul fichier.

Ce guide configure trois applications derrière une instance Caddy sur un seul serveur, chacune accessible sur son propre sous-domaine avec le HTTPS géré automatiquement, puis couvre ce que Railyard automatise sur le même problème, de sorte qu’il n’y a plus aucun Caddyfile à entretenir.

Ce qu’il vous faut

  • Un serveur avec au moins 2 Go de RAM. Un droplet DigitalOcean Basic de 2 Go (12 $US/mois) ou un Hetzner CX23 (2 vCPU, 4 Go de RAM, 5,99 €/mois hors TVA) a de la place pour plusieurs petits conteneurs en plus de Caddy lui-même.
  • Un domaine que vous contrôlez, avec accès pour ajouter des enregistrements DNS.
  • Docker et le plugin Compose installés sur le serveur.
  • Les ports 80 et 443 ouverts et pas déjà utilisés par un autre serveur web.

Étape 1 : pointer le DNS vers le serveur avant tout le reste

Caddy demande un certificat la première fois que chaque domaine est sollicité en HTTPS, et Let’s Encrypt valide cette demande en vérifiant que le domaine pointe bien vers ce serveur. Créez un enregistrement A par sous-domaine dès maintenant, pour qu’il ait le temps de se propager avant que vous démarriez Caddy :

blog.example.com.    A  203.0.113.10
api.example.com.     A  203.0.113.10
status.example.com.  A  203.0.113.10

Les trois peuvent pointer vers la même IP ; Caddy les distingue grâce à l’en-tête Host de la requête, exactement comme n’importe quel reverse proxy.

Étape 2 : organiser les applications en conteneurs séparés

Cet exemple fait tourner trois choses : une application de blogue et une API, chacune son propre petit conteneur, et une page de statut servie comme de simples fichiers statiques sans aucun backend. Chaque application vit dans son propre répertoire avec son propre Dockerfile :

.
├── blog/
│   └── Dockerfile
├── api/
│   └── Dockerfile
├── status/
│   └── index.html
├── docker-compose.yml
└── Caddyfile

Rien ici ne dépend de ce qui se trouve réellement dans blog ou api ; une application Django d’un guide précédent, une API Node, ou n’importe quoi d’autre qui écoute sur un port dans son conteneur fonctionne de la même façon. La seule exigence de Caddy, c’est que chaque conteneur expose un port sur le réseau Docker.

Étape 3 : écrire le Caddyfile

{
	email ops@example.com
}

blog.example.com {
	reverse_proxy blog:8000
}

api.example.com {
	reverse_proxy api:8001
}

status.example.com {
	root * /srv/status
	file_server
}

Chaque domaine a son propre bloc. blog.example.com et api.example.com redirigent vers leurs conteneurs respectifs par le nom du service Docker Compose ; Caddy résout ce nom sur le réseau interne exactement comme il résoudrait un nom d’hôte n’importe où ailleurs. status.example.com n’a aucun backend, Caddy sert simplement des fichiers directement depuis le disque avec file_server. Le bloc email tout en haut sert à Let’s Encrypt pour vous avertir si un certificat risque de ne pas se renouveler ; c’est facultatif mais vaut la peine de le définir.

Validez un Caddyfile avant de lui faire confiance en production :

docker run --rm -v "$(pwd)/Caddyfile:/etc/caddy/Caddyfile" caddy:2 caddy validate --config /etc/caddy/Caddyfile

Vous devriez voir « Valid configuration » s’afficher, pas une erreur de syntaxe.

Étape 4 : écrire docker-compose.yml

services:
  blog:
    build: ./blog
    restart: unless-stopped
    expose:
      - "8000"

  api:
    build: ./api
    restart: unless-stopped
    expose:
      - "8001"

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./status:/srv/status:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - blog
      - api

volumes:
  caddy_data:
  caddy_config:

Seul caddy publie les ports 80 et 443 vers l’hôte ; blog et api utilisent expose, qui rend leurs ports accessibles aux autres conteneurs du même réseau Compose mais pas du tout depuis l’extérieur du serveur. C’est tout l’intérêt de tout mettre derrière un seul proxy : les conteneurs d’applications ne sont jamais directement accessibles, seulement à travers Caddy, et seulement sur le domaine auquel ils sont censés répondre. Le volume caddy_data est ce qui permet aux certificats de survivre à un redémarrage ; le perdre signifie que Caddy en redemande de nouveaux à Let’s Encrypt au prochain démarrage, ce qui est sans risque mais compte dans les limites de taux de Let’s Encrypt si ça arrive souvent.

Étape 5 : démarrer et vérifier les certificats

docker compose up -d --build
docker compose logs -f caddy

Vous devriez voir une ligne par domaine indiquant qu’un certificat a été obtenu, quelque chose comme « certificate obtained successfully » avec le nom du domaine à côté. Confirmez depuis l’extérieur du serveur :

curl -I https://blog.example.com
curl -I https://api.example.com
curl -I https://status.example.com

Chacune devrait renvoyer une réponse sans avertissement de certificat, et curl -v montrerait un certificat émis par Let’s Encrypt pour ce nom d’hôte exact.

Étape 6 : ajouter une quatrième application plus tard, sans interruption

Ajoutez un nouveau bloc au Caddyfile, un nouveau service à docker-compose.yml, créez son enregistrement DNS, puis dites au conteneur Caddy déjà en cours d’exécution de prendre en compte le changement sans redémarrer quoi que ce soit d’autre :

docker compose up -d newapp
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

caddy reload relit la configuration et ne touche que ce qui a changé ; les certificats et connexions des trois autres domaines ne sont pas perturbés.

Sauvegardes et mises à jour

Le seul état qui vaille la peine d’être sauvegardé ici, c’est le volume caddy_data (les certificats, qui se régénèrent de toute façon) et tout ce que chaque application stocke elle-même, comme un volume de base de données si l’une d’elles en a un. Sauvegardez-les de la même façon que pour n’importe quelle application : un dump planifié ou un instantané de volume copié hors du serveur.

Pour mettre à jour Caddy lui-même, récupérez la nouvelle étiquette d’image et recréez seulement ce conteneur :

docker compose pull caddy
docker compose up -d caddy

Les certificats existants dans caddy_data sont conservés ; Caddy n’a pas besoin d’en réémettre juste parce que le binaire a changé de version.

Le raccourci : avec Railyard

Tout ce qui précède (le certificat par domaine, le reverse proxy, la vérification de propriété qui empêche quelqu’un d’autre de pointer un domaine qu’il ne contrôle pas vers votre serveur) est ce que Railyard automatise par application plutôt que par un Caddyfile partagé. Chaque application obtient https://<nom>.<ip-du-serveur>.sslip.io avec un certificat dès son déploiement, sans qu’aucun enregistrement DNS ne soit encore nécessaire. Ajouter votre propre domaine veut dire ouvrir l’onglet Domains de l’application, ajouter le nom d’hôte, et créer l’enregistrement A ou CNAME affiché ; un enregistrement TXT (_railyard-verify.<domaine>) prouve que vous contrôlez le domaine avant que le certificat ou le routage ne démarrent, vérifié automatiquement toutes les 30 secondes. Faire tourner plusieurs applications sur le même serveur ne change rien à tout ça : chacune est routée et certifiée indépendamment, de la même façon que le Caddyfile ci-dessus gère trois domaines dans une seule configuration, simplement sans que vous ayez à écrire ou recharger cette configuration vous-même.

Pour un domaine générique (*.example.com routant chaque sous-domaine vers une application) ou de nombreux sous-domaines qui signifieraient sinon un certificat par visite, Networking couvre les deux options : le TLS à la demande (un certificat émis la première fois que chaque sous-domaine est visité, le comportement par défaut) ou un seul certificat pour tout le domaine générique via un défi DNS-01, avec un jeton API Cloudflare ou une clé d’accès Route 53 ajoutée sous Team settings → DNS credentials.

Dépannage

Le port 80 ou 443 est déjà utilisé. Autre chose (souvent un nginx ou Apache installé par l’image du serveur, ou un reste d’une configuration précédente) y est déjà lié. Trouvez-le avec ss -tlnp | grep -E ':80|:443' et arrêtez-le avant de démarrer Caddy.

Un certificat ne s’émet jamais pour un domaine mais les autres fonctionnent. Vérifiez séparément le DNS de ce domaine précis : dig blog.example.com. Une faute de frappe dans un enregistrement A est la cause la plus fréquente, et ça ne casse que ce domaine-là puisque Caddy demande les certificats indépendamment par bloc de site.

« too many certificates already issued » de Let’s Encrypt. C’est une limite de taux (actuellement 5 certificats identiques par domaine exact par semaine), généralement atteinte en détruisant et recréant le volume caddy_data à répétition pendant des tests. Gardez le volume entre les redémarrages, et utilisez l’autorité de certification interne de Caddy (tls internal dans le bloc du Caddyfile) pendant que vous ajustez la configuration, en passant à de vrais certificats une fois que ça fonctionne.

Erreurs de permissions sur la page de statut ou un volume monté. file_server a besoin d’un accès en lecture à ce qui est monté à sa racine ; un volume créé par un autre conteneur tournant sous un utilisateur différent en est habituellement la cause. Corrigez le propriétaire côté hôte, ou montez-le en :ro comme dans l’exemple ci-dessus si rien n’a besoin d’y écrire.

Le serveur manque de mémoire avec plusieurs conteneurs en plus de Caddy. Chaque petit conteneur d’application va généralement bien, mais vérifiez l’usage réel avec docker stats avant de supposer que c’est Caddy ; un conteneur de base de données est un coupable plus probable que le proxy lui-même. Passez à un serveur plus gros, ou déplacez l’application la plus lourde vers son propre serveur en continuant de pointer son sous-domaine vers l’endroit où elle tourne réellement.

FAQ

Ai-je besoin de Docker, ou est-ce que ça marche avec un Caddy installé au niveau du système ? Le principe est identique dans les deux cas : un Caddyfile avec un bloc par domaine, chacun pointant vers un port local plutôt qu’un nom de service Docker. systemd gère le processus Caddy à la place de Compose.

Toutes les applications doivent-elles être sur le même serveur ? Pour ce guide, oui, puisque l’idée est d’avoir une seule instance Caddy partagée. Rien ne vous empêche de faire tourner Caddy sur un serveur et de rediriger vers des applications sur d’autres via un réseau privé, mais à ce stade vous construisez le genre de routage qu’une plateforme comme Railyard gère déjà.

Que se passe-t-il si j’ajoute une centaine de sous-domaines ? Le TLS à la demande émet un certificat par sous-domaine à sa première visite, ce qui fonctionne mais veut dire une centaine de certificats Let’s Encrypt distincts à suivre. Un certificat générique via DNS-01 les couvre tous avec un seul, ce qui est le meilleur choix au-delà d’une poignée de sous-domaines, que ce soit dans un Caddyfile écrit à la main ou via l’option d’identifiants DNS propre à Railyard.

Est-ce que ça redirige le HTTP vers le HTTPS automatiquement ? Oui, Caddy le fait par défaut pour chaque domaine du Caddyfile, sans configuration supplémentaire nécessaire.

Trois domaines derrière une instance Caddy et trente derrière une seule ne sont pas des configurations vraiment différentes, le Caddyfile s’allonge juste un peu. Ce qui vaut la peine d’être retenu, c’est qu’un reverse proxy qui émet lui-même ses certificats par domaine est plus simple à faire tourner qu’une tâche cron certbot et une configuration nginx par site, que vous écriviez ce Caddyfile vous-même ou que vous laissiez autre chose l’écrire à votre place.

Pourquoi les gens choisissent Railyard pour ça

  • Votre propre compte infonuagique. Déployez sur DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou n'importe quel serveur Linux par SSH, facturé directement par votre fournisseur, sans majoration.
  • Un prix fixe par équipe. L'offre gratuite est gratuite pour toujours : un serveur plus un serveur de base de données, trois applications, sans carte. Pro coûte 19 $ par mois pour jusqu'à cinq serveurs et un nombre illimité d'applications. Team coûte 49 $ par mois avec des serveurs illimités. Aucuns frais par application, par déploiement, par siège ou par gigaoctet.
  • Des gabarits en un clic. Déployez des applications open source, comme des bases de données, des outils d'analyse et des outils de développement, sur votre propre serveur en un clic.
  • Des bases de données gérées. Postgres, MySQL et Redis, avec des sauvegardes planifiées, vérifiées par restauration, et la récupération à un point dans le temps.
  • Déploiement par push. Déployez depuis GitHub, GitLab ou la CLI, avec HTTPS automatique et des mises en production sans interruption.

Évitez le Caddyfile partagé

Chaque application déployée sur Railyard obtient automatiquement sa propre adresse HTTPS, sans Caddyfile à entretenir à la main.

Offre gratuite, sans carte, et 14 jours de Pro pour chaque nouvelle équipe. Prix fixe par équipe.