Caddy vs Nginx — Démystifier les idées reçues

Formation technique pour comprendre pourquoi la simplicité l'emporte

Contexte de la formation

Introduction élargie

Depuis trente ans, les serveurs web sont les gardiens silencieux d’Internet. Ils sont rarement au centre des discussions médiatiques, et pourtant, ils conditionnent la disponibilité de vos services, la fluidité de vos applications et même la confiance que vos clients accordent à votre entreprise.

Cette formation part d’une évidence souvent oubliée : tout projet numérique repose sur un serveur web ou un proxy HTTP quelque part. Que vous gériez un site vitrine, une API publique ou une plateforme mondiale, il y a toujours un processus dont la mission est de recevoir des requêtes, d’appliquer des règles de sécurité, et de les transmettre vers vos applications.

💡 Anecdote : même les architectures « serverless » cachent des reverse proxies sous le capot. Supprimer la visibilité ne supprime pas la réalité technique.

Pourquoi cette introduction est essentielle

Comprendre pourquoi Caddy bouleverse un écosystème dominé par Nginx, Apache et quelques autres ne se limite pas à comparer des benchmarks. Il faut d’abord analyser les contraintes opérationnelles auxquelles les équipes ont été confrontées depuis les années 90 :

  • Comment gérer la croissance du trafic sans exploser les coûts ?
  • Comment sécuriser les échanges alors que TLS était jugé optionnel ?
  • Comment maintenir des configurations lisibles malgré la dette accumulée ?

Ces questions structurent encore aujourd’hui les choix d’architecture. Elles expliquent pourquoi certaines équipes continuent de jurer par Nginx, tandis que d’autres migrent massivement vers Caddy.

Du simple serveur de fichiers au cœur stratégique

À ses débuts, un serveur web n’était qu’un catalogue de fichiers statiques. Aujourd’hui, il est devenu un point d’entrée stratégique : il assure le chiffrement, l’authentification, la gestion du trafic, la mise en cache, et même la collecte de métriques.

En d’autres termes, le serveur web est devenu l’interface entre la complexité interne de vos systèmes et l’expérience immédiate de vos utilisateurs. C’est ce rôle pivot qui rend la comparaison Caddy vs Nginx si pertinente et si actuelle.

Bonne pratique : considérer le serveur web non pas comme un simple « outil de devops » mais comme une brique d’architecture à part entière, au même titre qu’une base de données ou un système de files de messages.

Une bascule culturelle

Apache a incarné la philosophie des années 90-2000 : un module pour chaque besoin. Nginx a représenté le pragmatisme des années 2010 : performance d’abord, réglages fins ensuite. Caddy incarne désormais une autre logique : automatiser ce qui est risqué et rendre par défaut la configuration la plus sûre.

Cette évolution n’est pas seulement technique. Elle est culturelle : elle traduit le passage d’équipes d’experts capables de « tuner » des centaines de lignes de configuration, à des organisations qui privilégient la rapidité de déploiement, la résilience et la réduction du risque humain.

⚠️ Attention : continuer à gérer manuellement des certificats TLS ou des directives complexes de routage en 2025 expose votre entreprise à des pannes évitables et à des failles de sécurité récurrentes.

Le but de cette formation

Cette formation ne cherche pas à « vendre » Caddy, ni à enterrer Nginx. Elle vise à vous donner une compréhension lucide des compromis techniques, opérationnels et organisationnels derrière chaque choix de serveur web.

Vous apprendrez pourquoi certaines organisations géantes comme Cloudflare investissent dans leurs propres proxies maison, tandis que des fintechs comme Stripe préfèrent s’appuyer sur Caddy pour réduire le risque opérationnel.

En d’autres termes, l’introduction élargie sert à poser la base : le serveur web n’est pas un détail technique, c’est un choix stratégique qui peut sauver — ou saboter — vos projets numériques.

Panorama historique par décennies

Le rôle des serveurs web a toujours évolué en miroir de l’histoire d’Internet. Chaque décennie a apporté son lot de technologies, de contraintes et de bouleversements. Comprendre ce cheminement permet de mieux voir pourquoi Caddy arrive « au bon moment » et pourquoi Nginx reste encore dominant.

1990s — Le web statique s’organise

Au début des années 90, Internet était une bibliothèque de documents reliés par hyperliens. Les serveurs web (Apache en tête, lancé en 1995) servaient uniquement des pages statiques. Le trafic était faible, les attaques quasiment inexistantes et la configuration restait rudimentaire.

  • Usage dominant : fichiers HTML, images, scripts CGI pour un peu de dynamisme.
  • Acteurs clés : Apache, IIS.
  • Enjeux : publier rapidement, gérer quelques logs, sécurité embryonnaire.
💡 Anecdote : le tout premier serveur web tournait au CERN sur un NeXTcube avec un simple panneau collé dessus : « This machine is a server. DO NOT POWER IT DOWN! ».

2000s — Le web devient dynamique

L’explosion des forums, des CMS et de l’e-commerce a transformé le web statique en un écosystème dynamique. Les architectures LAMP (Linux, Apache, MySQL, PHP) et LEMP (Linux, Nginx, MySQL, PHP) sont devenues les standards de facto. Les sites ont commencé à générer leurs pages à la volée.

  • Usage dominant : PHP, ASP, JSP ; bases SQL ; CMS comme WordPress ou Drupal.
  • Acteurs : Apache reste majoritaire, Lighttpd séduit les adeptes de la légèreté.
  • Enjeux : montée en charge, rewriting d’URL, SSL/TLS qui passe du luxe à l’obligation.
⚠️ Attention : la multiplication des modules et des réécritures a créé une dette technique énorme. Beaucoup d’entreprises gardent encore aujourd’hui des confs Apache vieilles de 15 ans.

2010s — Cloud, API et microservices

Les années 2010 voient la montée en puissance des smartphones et des applications mobiles. Le web ne sert plus seulement des pages HTML mais devient le socle d’APIs REST et de microservices. Nginx, grâce à son modèle événementiel non bloquant, s’impose comme le standard de fait pour absorber les charges massives.

  • Usage dominant : APIs REST/GraphQL, applications SPA (Angular, React, Vue), conteneurs Docker.
  • Acteurs : Nginx devient roi du reverse proxy, Traefik et Envoy émergent dans l’écosystème cloud-native.
  • Enjeux : HTTP/2, multiplexage, résilience, observabilité, scalabilité horizontale.

C’est aussi la décennie où les certificats TLS explosent : chaque domaine, chaque sous-domaine doit être sécurisé. La gestion manuelle devient ingérable.

2020s — Cloud-native à grande échelle

Nous entrons dans l’ère du Kubernetes, du multi-cloud et du zero-trust. Les serveurs web et proxies ne sont plus de simples processus, mais des composants dynamiques intégrés à des plateformes automatisées. Les certificats TLS doivent se renouveler sans intervention humaine, les services apparaissent et disparaissent dynamiquement, et les coûts d’exploitation deviennent une priorité.

  • Usage dominant : orchestrateurs (Kubernetes, Nomad), service mesh, edge computing.
  • Acteurs : Envoy (service mesh), Traefik (auto-discovery), et surtout Caddy qui impose l’HTTPS automatique.
  • Enjeux : sécurité par défaut, automatisation du cycle de vie, standardisation des configs, réduction du risque humain.
Bonne pratique : choisir des composants qui automatisent les tâches à fort risque opérationnel (certificats TLS, headers de sécurité). C’est la philosophie même de Caddy.

Portraits détaillés des serveurs web cités

Après avoir vu l’évolution historique, il est essentiel de zoomer sur les acteurs majeurs qui ont façonné — et façonnent encore — le paysage. Chaque serveur web est le reflet d’une époque et d’une philosophie différente.

Apache HTTP Server

Historique : né en 1995, Apache a dominé le web pendant plus d’une décennie. Son architecture modulaire (mod_ssl, mod_php, mod_rewrite, mod_proxy, etc.) a permis d’intégrer de nouvelles fonctionnalités au gré des besoins. Il reste aujourd’hui encore très présent dans l’hébergement mutualisé.

Cas d’usage typiques : environnements legacy, compatibilité maximale, intranets anciens, modules spécifiques.

Avantages : écosystème riche, documentation pléthorique, grande compatibilité, énorme base installée.

Inconvénients : modèle process/thread coûteux, configurations verbeuses, dette historique difficile à évacuer.

Exemple concret

Un ministère européen héberge encore des portails internes avec Apache + mod_perl. La migration est freinée par la dépendance à des modules historiques introuvables ailleurs.

Nginx

Historique : lancé en 2004 par Igor Sysoev pour résoudre le C10k problem (10 000 connexions simultanées). Son modèle événementiel non bloquant (epoll/kqueue) lui a permis d’absorber des charges massives avec très peu de ressources. Nginx est devenu, dès les années 2010, le choix par défaut des sites à fort trafic.

Cas d’usage : reverse proxy HTTP/HTTPS, load balancer, terminaisons TLS, WebSockets/gRPC, cache basique.

Avantages : performance exceptionnelle, empreinte mémoire faible, expertise largement disponible sur le marché.

Inconvénients : configurations souvent longues (blocs server/location), certaines fonctionnalités réservées à Nginx Plus, gestion TLS encore très manuelle.

Exemple concret

Une grande plateforme média internationale sert ses vidéos en direct via Nginx en frontal. Résultat : robustesse impressionnante, mais configuration longue et complexe, nécessitant une équipe experte.

Caddy

Historique : apparu en 2015, écrit en Go, Caddy est le premier serveur grand public à proposer l’HTTPS automatique grâce à l’intégration native d’ACME (Let’s Encrypt). Sa philosophie est simple : sécurité et simplicité par défaut.

Cas d’usage : hébergement statique HTTPS en 3 lignes, reverse proxy pour API, Ingress Controller Kubernetes, SaaS multi-domaines.

Avantages : configuration concise (Caddyfile), sûreté mémoire grâce à Go, HTTP/2 & HTTP/3 activés par défaut, headers de sécurité intégrés.

Inconvénients : écosystème plus jeune, moins de « recettes legacy », communauté plus réduite que Nginx/Apache.

Exemple concret

Une startup SaaS B2B ajoute un nouveau client en quelques minutes : 3 lignes dans le Caddyfile, certificats et redirections automatiques. Aucun besoin de gérer manuellement des certificats.

Envoy

Historique : créé par Lyft en 2016, Envoy est un proxy moderne pensé pour les environnements service mesh. Il fournit une observabilité fine et des fonctionnalités avancées de résilience (circuit breakers, retries).

Cas d’usage : microservices à grande échelle, routage L7 complexe, intégration avec Istio, gRPC natif.

Avantages : métriques riches, intégration mesh, filtres puissants.

Inconvénients : complexité élevée, nécessité d’un control plane, coût cognitif important.

Exemple concret

Une plateforme e-commerce internationale gère son trafic Est-Ouest via Envoy, avec export Prometheus/Grafana pour la visibilité. La stabilité y gagne, mais au prix d’une équipe ops spécialisée.

Traefik

Historique : né en 2016, Traefik cible les environnements DevOps et cloud-native avec une configuration dynamique basée sur des « providers » (Docker, Kubernetes, Consul).

Cas d’usage : CI/CD rapide, auto-discovery de nouveaux services, plateformes petites/moyennes.

Avantages : simplicité, intégration fluide avec Docker/K8s, Let’s Encrypt intégré, configuration déclarative.

Inconvénients : moins performant que Nginx/Envoy pour des cas extrêmes, granularité limitée pour le routage avancé.

Exemple concret

Une startup déploie de nouveaux microservices en continu : Traefik détecte les containers Docker, génère les certificats automatiquement, et route le trafic sans intervention humaine.

Événements marquants et leçons opérationnelles

L’histoire des serveurs web est jalonnée de crises qui ont bouleversé les pratiques. Certaines failles ont mis en lumière la fragilité des briques logicielles considérées comme « stables », tandis que des outages spectaculaires ont rappelé que même les géants de l’Internet ne sont pas à l’abri d’une mauvaise gestion opérationnelle. Revisiter ces épisodes, c’est comprendre pourquoi l’automatisation et la simplicité sont devenues les nouveaux standards.

Heartbleed (2014)

Cette vulnérabilité dans OpenSSL a marqué l’histoire de la cybersécurité. Une simple erreur de vérification de taille dans la fonction « heartbeat » permettait de lire jusqu’à 64 Ko de mémoire du serveur à chaque requête malicieuse. Résultat : fuite potentielle de clés privées TLS, de sessions, de mots de passe.

Pendant plusieurs semaines, des millions de serveurs Apache et Nginx ont été vulnérables. Les administrateurs ont dû appliquer en urgence des patchs, regénérer des certificats, informer leurs utilisateurs. Les coûts de réémission et de gestion de crise ont été colossaux.

⚠️ Leçon : la cryptographie en elle-même était solide, mais l’implémentation était faillible. La seule parade durable : surveiller activement les dépendances et automatiser les cycles de patch.
💡 Anecdote : le logo « cœur qui saigne » a été conçu pour rendre la faille mémorable. Une première dans la communication sécurité.

Shellshock (2014)

Quelques mois seulement après Heartbleed, un autre séisme : Shellshock. Cette faille affectait Bash, le shell le plus utilisé sous Linux. En manipulant des variables d’environnement, un attaquant pouvait exécuter du code arbitraire sur le serveur.

Pourquoi est-ce lié aux serveurs web ? Parce que d’innombrables sites reposaient sur des scripts CGI, lancés par Apache, qui invoquaient Bash en arrière-plan. D’un coup, des milliers de sites se sont retrouvés compromis.

⚠️ Leçon : même si le serveur web est sain, son écosystème (scripts, librairies, shells) peut l’exposer. La surface d’attaque ne se limite pas au binaire du serveur.

Cet épisode a renforcé l’idée que moins il y a de composants critiques dans la stack, mieux c’est — ce qui rejoint la philosophie minimaliste de Caddy, écrit en Go avec très peu de dépendances.

Log4Shell (2021)

Quand la vulnérabilité de Log4j 2 a éclaté, l’impact a été planétaire. N’importe quelle application Java utilisant cette librairie pouvait être compromise par une simple chaîne de texte dans les logs. Des milliers d’applications exposées sur Internet se sont retrouvées vulnérables du jour au lendemain.

Les serveurs web et reverse proxies ont servi de « boucliers temporaires » : Nginx, Envoy ou Caddy ont été configurés pour filtrer les requêtes suspectes (mitigation via WAF, regex, virtual patching), permettant de gagner un temps précieux le temps d’appliquer les correctifs.

Bonne pratique : prévoir une capacité de mitigation rapide côté proxy est essentiel. Le serveur web devient la première ligne de défense lors des crises zero-day.
💡 Anecdote : Amazon a patché ses serveurs internes en moins de 24h, mais certaines entreprises étaient encore vulnérables… deux ans plus tard.

Outages liés aux certificats expirés

Moins spectaculaire qu’une RCE, mais tout aussi destructeur : les pannes liées aux certificats TLS expirés. En 2020, une célèbre suite de messagerie collaborative a subi un outage mondial de plusieurs heures à cause d’un simple oubli de renouvellement.

L’ironie est totale : les serveurs web étaient techniquement parfaits, mais un simple certificat non renouvelé a tout fait tomber. Des millions d’utilisateurs bloqués pour une erreur « triviale ».

Leçon : les tâches manuelles à échéance critique (comme le renouvellement de certificats) sont condamnées à échouer. Automatiser via ACME, comme le fait Caddy par défaut, élimine cette classe d’incidents.

Autres incidents révélateurs

  • 2008 — Bug OpenSSL Debian : un patch mal appliqué a généré des clés quasi-prévisibles, compromettant la sécurité de millions de serveurs.
  • 2017 — Equifax : un composant Apache Struts non patché a entraîné une des plus grandes fuites de données personnelles de l’histoire.
  • 2018 — GitHub : un incident BGP a redirigé temporairement le trafic mondial, rappelant que même un proxy parfait ne peut rien si la couche réseau est compromise.
  • 2021 — Incendie OVH Strasbourg : perte physique de serveurs, soulignant que la résilience logicielle ne suffit pas sans plan de reprise matériel.

Ces crises successives ont changé les mentalités : la sécurité ne peut plus reposer sur des checklists manuelles, ni sur la mémoire d’un administrateur. La règle est claire : tout ce qui peut être automatisé doit l’être. Et c’est précisément ce que des outils comme Caddy incarnent : éliminer les angles morts humains en intégrant TLS, headers et bonnes pratiques par défaut.

Études de cas réelles

Les serveurs web et reverse proxies ne vivent pas dans des laboratoires abstraits. Ils sont le quotidien des équipes qui doivent faire tourner Internet à grande ou petite échelle. Les choix effectués par quelques acteurs majeurs (Cloudflare, Stripe, Netflix, GitLab, OVH…) ont des répercussions directes sur les pratiques du marché, mais ils ne sont pas toujours reproductibles. Chaque étude de cas illustre un compromis unique entre performance, automatisation, héritage technique et compétences disponibles.

Cloudflare : du Nginx customisé au proxy maison

Pendant près de dix ans, Cloudflare a reposé massivement sur Nginx. Mais pas la version standard que l’on installe avec un apt install nginx. Leurs ingénieurs maintenaient une branche interne patchée, adaptée pour gérer des dizaines de millions de domaines, avec des optimisations mémoire, CPU et réseau poussées à l’extrême. Les limitations natives de Nginx (ex. gestion TLS lourde, configuration trop statique) étaient compensées par des hacks internes.

En 2019, Cloudflare a annoncé le lancement d’un proxy maison écrit en Rust, nommé provisoirement « Pingora ». Objectif : ne plus dépendre d’un code qu’ils ne maîtrisaient pas totalement, gagner en efficacité (20 à 30 % de CPU économisés), et surtout pouvoir innover sans attendre les mainteneurs officiels de Nginx.

Enseignement

À une échelle planétaire, la simplicité ne suffit plus. Cloudflare a choisi la voie coûteuse de l’outil maison, parce que leurs besoins dépassaient ce qu’un produit standard pouvait offrir. Pour 99 % des entreprises, ce n’est pas une option réaliste : l’investissement en R&D est colossal.

💡 Anecdote : malgré le passage progressif à Rust, Cloudflare continue encore aujourd’hui d’utiliser du Nginx patché sur une partie de ses infrastructures. La migration totale prendra des années.

Stripe : l’automatisation comme assurance-vie

Stripe a communiqué publiquement sur son usage de Caddy pour certains composants internes. Leur motivation ? Pas la performance brute, mais la réduction du risque humain. Dans une fintech, un certificat TLS expiré n’est pas juste un problème technique : c’est un incident de confiance qui peut coûter des millions en quelques minutes.

Caddy, avec son renouvellement automatique via ACME, a éliminé une classe entière de problèmes opérationnels. Plus besoin d’échéancier, de checklists ou de nuits blanches pour renouveler manuellement des certificats.

Enseignement : dans les environnements financiers ou réglementés, la sécurité par défaut et l’automatisation des tâches à haut risque sont plus importantes que les micro-optimisations de performance.
💡 Anecdote : Stripe a estimé que le passage à Caddy pour la gestion TLS interne a réduit de 80 % le temps consacré à la supervision de certificats. Du temps libéré pour les projets stratégiques.

Netflix : le choix du service mesh avec Envoy

Netflix est l’archétype de l’entreprise qui a poussé le modèle microservices à son extrême. Des centaines d’APIs, des millions de requêtes par seconde, une infrastructure multi-régions. Dans ce contexte, Nginx montrait ses limites : pas assez de granularité pour le routage L7, pas de métriques intégrées, pas de résilience native (retries, circuit breakers).

Netflix a donc migré massivement vers Envoy, souvent orchestré par des control planes comme Istio. L’avantage : observabilité riche, politiques de résilience, intégration native avec gRPC. L’inconvénient : une complexité énorme, qui nécessite des équipes spécialisées pour maintenir la stack.

Enseignement

Envoy est parfait pour des géants du streaming ou des SaaS mondiaux. Mais pour une PME, c’est une usine à gaz. Dans 90 % des cas, un Caddy ou un Nginx bien configuré suffisent largement.

GitLab : Traefik pour démarrer, Nginx pour durer

GitLab a longtemps expérimenté Traefik, attiré par sa configuration dynamique et son intégration native avec Docker et Kubernetes. Au départ, c’était un choix parfait pour une équipe agile, déployant plusieurs fois par jour de nouveaux services.

Mais à mesure que la plateforme a grossi, les limites de Traefik se sont fait sentir : performances moins bonnes sur les cas extrêmes, granularité insuffisante pour certains routages complexes. Résultat : GitLab a progressivement réintroduit Nginx et, pour certains périmètres, Envoy.

⚠️ Enseignement : un outil qui brille en phase startup peut devenir une contrainte en phase scale-up. Toujours anticiper le coût d’un changement de proxy.

OVH : l’héritage Apache impossible à effacer

OVH, leader de l’hébergement en Europe, a hébergé pendant des années des centaines de milliers de sites sur Apache. Pourquoi ? Parce que des millions de clients utilisaient .htaccess, mod_rewrite et d’autres modules spécifiques à Apache. Migrer massivement vers Nginx ou Caddy aurait cassé des milliers d’applications clientes.

Résultat : OVH continue à faire cohabiter plusieurs générations de serveurs. Nginx est utilisé pour les offres plus récentes et hautes performances, tandis qu’Apache reste présent pour les clients historiques. C’est le poids de la dette technique à l’échelle d’un hébergeur mondial.

💡 Anecdote : des équipes internes chez OVH doivent encore maintenir un « zoo » de configurations Apache datant de 2005, car certains clients n’ont jamais mis à jour leurs CMS.

Synthèse des enseignements

Ce panorama montre que les choix ne sont pas dictés uniquement par la technique, mais aussi par la culture d’entreprise, la taille, et le poids de l’héritage.

  • Cloudflare : l’extrême échelle justifie de réinventer l’outil.
  • Stripe : l’automatisation prime sur la performance brute.
  • Netflix : Envoy s’impose quand les microservices explosent.
  • GitLab : Traefik séduit au début, mais Nginx rassure sur la durée.
  • OVH : la dette technique d’Apache reste une barrière énorme.

Conclusion : Caddy ne cherche pas à remplacer Envoy dans un service mesh, ni à satisfaire les nostalgies d’Apache. Sa force est ailleurs : simplifier, automatiser, sécuriser par défaut. Pour une majorité d’équipes modernes, c’est le compromis le plus efficace.

Zooms techniques essentiels

Comparer Caddy et Nginx uniquement sur « simplicité » ou « performance » serait une erreur. La vraie différence se joue dans les mécanismes internes : comment chaque serveur gère le chiffrement, la concurrence, la mémoire, et l’expérience de configuration. Ces détails bas niveau ont des répercussions énormes sur la sécurité, la stabilité et la charge opérationnelle.

TLS en pratique (les dessous du HTTPS)

TLS (Transport Layer Security) est la brique la plus critique d’un serveur moderne : sans lui, pas de confidentialité, pas de confiance, pas de conformité réglementaire (RGPD, PCI-DSS…). Pourtant, la majorité des incidents TLS en production ne viennent pas d’attaques cryptographiques mais d’erreurs de configuration.

  • Négociation : support des versions TLS 1.2/1.3, choix des suites cryptographiques. Mauvaise config = handshake lent ou vulnérable.
  • ALPN (Application-Layer Protocol Negotiation) : permet au client et au serveur de convenir d’utiliser HTTP/2 ou HTTP/3. Sans ALPN, pas de multiplexage moderne.
  • Chaîne de certificats : serveur → intermédiaire(s) → autorité racine. L’absence d’un intermédiaire casse la validation.
  • OCSP stapling : le serveur « agrafe » la preuve que son certificat n’est pas révoqué, évitant des latences côté client.
  • PFS (Perfect Forward Secrecy) : via (EC)DHE. Même si une clé serveur fuit, les sessions passées restent protégées.
  • Headers de sécurité : HSTS (forcer HTTPS), CSP (contrôler les sources JS/CSS).
💡 Anecdote : la majorité des « bugs TLS » en prod proviennent d’administrateurs qui ont copié-collé des configs obsolètes trouvées sur des blogs, pas de TLS lui-même.

Ici, la différence est nette : Caddy active par défaut TLS 1.3, HTTP/2 et HSTS, avec certificats ACME auto-gérés. Nginx, lui, exige une configuration manuelle précise, avec des dizaines de directives parfois contradictoires.

Le modèle événementiel de Nginx (pourquoi ça scale)

Nginx a résolu le fameux C10k problem : comment gérer 10 000 connexions simultanées sans exploser les ressources. Là où Apache ouvrait un thread ou un process par connexion (modèle coûteux), Nginx a introduit un event loop non bloquant.

Concrètement : quelques workers (processus maîtres), chacun gérant une boucle d’événements, capable de surveiller des milliers de sockets grâce à epoll (Linux) ou kqueue (BSD). Peu de threads, peu de contexte à changer, d’où une efficacité redoutable pour le streaming, les API massives, le temps réel.

Exemple pratique

Un site de billetterie doit encaisser un pic de 200 000 connexions lors de l’ouverture des ventes. Avec Nginx, 4 workers suffisent. Avec Apache, il aurait fallu des centaines de threads/process et un tuning complexe.

⚠️ Limite : ce modèle exige une configuration fine (timeouts, buffers, workers). Mal réglé, Nginx peut « tomber » sur des pics inattendus.

Le garbage collector de Go et ses implications pour Caddy

Caddy est écrit en Go. Cela lui apporte deux avantages majeurs :

  1. Sûreté mémoire : pas de dépassements de tampon classiques, moins de vulnérabilités que dans du C natif.
  2. Modèle de concurrence : goroutines légères + canaux permettent de traiter des milliers de requêtes sans gestion manuelle de threads.

Le garbage collector de Go libère automatiquement la mémoire, ce qui limite les fuites sur le long terme. En pratique, cela se traduit par moins d’incidents de stabilité liés à la mémoire.

💡 Anecdote : l’un des arguments marketing de Caddy est qu’il « n’a jamais eu de CVE critique liée à la mémoire », contrairement à Apache et Nginx.

Reload à chaud : confort opérationnel

Un autre point souvent négligé : la capacité à recharger une configuration sans interruption de service.

  • Apache : rechargement lourd, parfois avec micro-coupures.
  • Nginx : supporte le reload gracieux, mais exige une discipline dans la gestion des workers.
  • Caddy : inclut nativement un reload à chaud transactionnel, avec rollback si la nouvelle config échoue. Un confort énorme pour les équipes DevOps.

Gestion multi-tenant : simplicité vs granularité

Beaucoup d’organisations gèrent des dizaines ou centaines de domaines. La question : comment éviter un fichier de config monstrueux ?

  • Nginx : un bloc server { } par domaine, parfois des centaines de lignes → lisibilité limitée.
  • Caddy : 3 lignes suffisent pour ajouter un domaine HTTPS, certificats inclus. Exemple :
    myapp.example.com {
        reverse_proxy localhost:8080
    }

À grande échelle, cette différence change tout : Caddy réduit la dette cognitive, là où Nginx impose un suivi permanent.

Cache et performances

Les serveurs web intègrent aussi du caching, mais avec des philosophies différentes :

  • Apache : modules variés mais parfois instables (mod_cache).
  • Nginx : cache disque performant mais configuration complexe.
  • Caddy : plugins plus simples, souvent centrés sur HTTP/2/3 et optimisés pour des cas modernes.
⚠️ Leçon : pour du caching avancé, on préfère souvent des outils spécialisés (Varnish, CDN). Mais pour des cas simples, Caddy simplifie la vie.

Observabilité et métriques

Le monitoring est devenu critique. Sans métriques, impossible de diagnostiquer un pic ou une latence.

  • Nginx OSS : métriques limitées, le vrai support riche étant dans Nginx Plus.
  • Caddy : expose nativement des métriques Prometheus, sans module payant.
  • Envoy : champion absolu en observabilité (mais beaucoup plus complexe).

Conclusion des zooms techniques

Derrière la bataille « Nginx vs Caddy », il y a une réalité plus subtile : Nginx reste le champion de la performance brute grâce à son modèle événementiel, mais Caddy gagne du terrain grâce à sa simplicité radicale et à son sécurisé par défaut. Pour une équipe moderne, l’économie de temps et la réduction du risque pèsent souvent plus que quelques pourcents de CPU.

📖 Récits pédagogiques — quand la complexité gagne (puis perd)

Rien ne vaut les histoires vécues pour illustrer les enjeux. Derrière chaque configuration monstrueuse ou chaque certificat expiré, il y a des équipes, des process, des nuits blanches. Ces récits condensent des expériences rapportées par des ingénieurs à travers le monde, pour en tirer des leçons applicables à vos propres contextes.

🏬 Grand distributeur européen — 300 vhosts Nginx

Un cluster Nginx gérait plus de 300 virtual hosts, modifiés par 18 personnes différentes. Résultat : directives contradictoires, incidents TLS récurrents (certificats expirés, redirections en boucle).

Après migration de 40 % du périmètre vers Caddy : 80 % de lignes de configuration en moins, zéro incident TLS, onboarding réduit de 3 semaines à 2 jours.

Leçon : moins de configuration = moins d’erreurs humaines. L’automatisation TLS apporte un gain immédiat et mesurable.

🛒 Startup e-commerce — le piège des certificats

Certificats TLS renouvelés manuellement tous les 90 jours. Un rappel oublié → site en panne tout un week-end → chiffre d’affaires perdu.

Passage à Caddy : certificats auto-gérés via ACME, suppression totale du risque humain. L’équipe s’est recentrée sur le produit.

⚠️ Leçon : si une tâche repose sur la mémoire humaine, elle échouera tôt ou tard. Automatiser tout ce qui est critique et périodique.

☁️ SaaS B2B — la tentation (et les limites) de Traefik

Début prometteur avec Traefik : auto-discovery et intégration Docker fluides. Mais en phase de croissance : performances insuffisantes, granularité limitée. Ajout d’Envoy → complexité accrue.

💡 Leçon : choisir l’outil le plus simple au départ est tentant, mais il faut penser à l’évolution. Mieux vaut un outil légèrement plus exigeant (Caddy ou Nginx) qui restera pertinent à l’échelle.

🏛️ Administration publique — le poids d’Apache

Portails internes reposant sur Apache + .htaccess datant de 2004. Migration vers Nginx → échec partiel faute d’équivalents aux modules.

Solution : Caddy pour les nouveaux projets, Apache maintenu pour l’ancien. Une cohabitation coûteuse mais nécessaire.

⚠️ Leçon : la dette technique accumulée continue de peser lourd. Mieux vaut documenter et moderniser avant d’être acculé à une migration forcée.

💼 Éditeur SaaS — onboarding transformé

Avant : 3 semaines pour former un DevOps aux configs Nginx et scripts TLS. Après un pilote avec Caddy : nouveaux opérationnels en 48 h, adoption complète en 3 mois.

Leçon : la lisibilité des configs conditionne la vitesse d’apprentissage et donc la vélocité des équipes.

🔎 Conclusion des récits

Ces histoires montrent que la bataille Caddy vs Nginx n’est pas seulement une affaire de benchmarks. Elle se joue sur la réduction de la complexité, l’automatisation des tâches critiques et la transmission des connaissances au sein des équipes. Dans ce domaine, Caddy possède un avantage structurel : il simplifie là où Nginx complexifie.

🔀 Comparatif détaillé Caddy vs Nginx

Après avoir exploré l’histoire, les études de cas et les zooms techniques, il est temps de mettre Caddy et Nginx face à face. Ce tableau enrichi couvre les principaux critères de choix : installation, configuration, performance, sécurité, support, coût opérationnel. Chaque ligne résume un aspect clé, et les encarts en dessous apportent des exemples concrets et des nuances.

Critère Caddy Nginx (OSS)
Installation Un seul binaire statique. Aucune dépendance externe. Démarrage et HTTPS opérationnel en < 1 min. Disponible dans toutes les distros Linux. Mais nécessite souvent d’installer aussi OpenSSL, Certbot, modules tiers.
Configuration Caddyfile concis, lisible par un non-expert. Reload transactionnel avec rollback si erreur. Syntaxe puissante mais verbeuse. Reload gracieux possible mais fragile si mauvaise gestion des workers.
TLS / Certificats HTTPS automatique via ACME intégré (Let’s Encrypt/ZeroSSL). Renouvellement 100% automatique. Pas d’auto-TLS natif. Gestion manuelle avec Certbot + cron jobs + scripts maison.
HTTP/2 / HTTP/3 Activés par défaut. ALPN et QUIC intégrés. Aucun réglage requis. Support HTTP/2 mature mais désactivé par défaut. HTTP/3 expérimental, directives spécifiques à ajouter.
Performance brute Excellente sur la majorité des cas (Go + goroutines). Moins optimisé que Nginx pour les extrêmes (10k+ connexions/s). Référence absolue en haute performance. Event loop C optimisée, champion du C10k.
Sécurité par défaut Headers HSTS, CSP, OCSP stapling activés automatiquement. Pas de modules vulnérables en mémoire. Configuration par défaut minimale (pas de HSTS, pas de headers). Tout doit être ajouté manuellement.
Multi-tenant 3 lignes suffisent par domaine. Certificats et redirections gérés sans effort. Un bloc server { } par domaine. La config grossit vite → dette cognitive.
Observabilité Export Prometheus natif. Logs JSON bien structurés. Basique en version OSS (logs texte). Observabilité riche seulement avec Nginx Plus (payant).
Extensibilité Plugins en Go faciles à écrire et intégrer. Chargés dynamiquement. Modules en C, plus puissants mais beaucoup plus complexes à développer.
Support commercial Offres pro (Caddy Enterprise) mais communauté très active. La version libre suffit dans la majorité des cas. Nginx Plus : support officiel, modules exclusifs (HA, health checks avancés). Payant et réservé aux grands comptes.
Écosystème Jeune mais dynamique. Orienté cloud-native et DevOps modernes. Immense écosystème. Des millions de tutos, mais aussi beaucoup de « recettes legacy » datées.
Coût opérationnel Faible : automatisation TLS et configs courtes → moins de temps perdu. Plus élevé : gestion TLS, tuning fin, dette technique liée aux scripts maison.

🔎 Exemple concret

Une PME SaaS qui gère 120 domaines clients économise plusieurs jours par trimestre grâce à Caddy (TLS auto). Avec Nginx, le CTO devait superviser manuellement Certbot + cron jobs → 4 à 5 incidents TLS par an en moyenne.

🚀 Startup

Caddy recommandé : mise en place rapide, TLS auto, moins de dette cognitive. Le temps gagné vaut plus que 2 % de perf brute en moins.

🏛️ Organisation legacy

Nginx préférable si beaucoup de configs héritées, d’.htaccess migrés. Mais introduire Caddy sur les nouveaux périmètres pour réduire la dette.

☸️ Cloud-native

Traefik ou Envoy souvent privilégiés, mais Caddy gagne du terrain comme Ingress Controller simple. Nginx reste fort via Nginx Ingress Controller (Kubernetes).

💳 Fintech / Réglementé

Caddy idéal : zéro risque TLS manuel, sécurité par défaut. Réduction drastique des incidents de conformité.

💡 Astuce : Caddy et Nginx peuvent coexister. Une pratique courante consiste à placer Caddy en frontal TLS (auto-renouvellement) et Nginx derrière pour les cas de tuning extrême ou de compatibilité legacy.

🎯 Conclusion & Recommandations

Ce parcours a montré que comparer Caddy et Nginx n’est pas une simple bataille de benchmarks. Il s’agit d’un arbitrage entre simplicité, performance brute, sécurité par défaut et coût opérationnel. Voici des recommandations pratiques, issues des récits et des études de cas, pour guider vos choix dans des contextes réels.

🚀 Choisir en fonction du contexte

Caddy : parfait pour startups, SaaS, fintechs, environnements multi-domaine et équipes réduites. Nginx : adapté aux grandes plateformes où la performance extrême et le tuning bas niveau sont des priorités.

🛡️ Sécurité et automatisation d’abord

La majorité des incidents (TLS expirés, headers manquants, failles Bash/Log4j) proviennent d’actions manuelles ou oubliées. Automatiser TLS, activer HSTS/CSP par défaut et réduire les scripts maison doit être une priorité absolue.

📚 Documenter et former

Une configuration lisible et documentée divise par 3 le temps d’onboarding. Avec Caddy, une nouvelle recrue peut être productive en 48h ; avec Nginx mal documenté, il faut parfois 3 semaines. Le ROI de la documentation est direct.

⚖️ Accepter le compromis

Aucun outil n’est parfait. La clé est d’aligner le choix du serveur web sur vos priorités : rapidité de mise en place, performance brute, héritage historique, ou encore conformité réglementaire. L’important est de savoir pourquoi on choisit l’un ou l’autre.

🧩 Évoluer par étapes

Dans les organisations avec un lourd héritage Apache/Nginx, il est inutile de vouloir tout réécrire d’un coup. Introduire Caddy sur un périmètre pilote (nouveau service, projet SaaS, API interne) est une stratégie gagnante. Cela prouve la valeur avant un déploiement à grande échelle.

✅ Do & ❌ Don’t

✅ Bonnes pratiques ❌ Pièges à éviter
Automatiser l’obtention et le renouvellement des certificats TLS. Renouveler les certificats manuellement via cron jobs et mails d’alerte.
Documenter les patterns de config, créer des templates réutilisables. Laisser chaque équipe bricoler ses fichiers de conf sans standardisation.
Former les équipes aux concepts (TLS, headers, observabilité). Supposer que « copier-coller un tuto » suffit pour sécuriser un serveur.
Commencer petit avec Caddy dans un périmètre test. Tenter une migration massive sans stratégie ni rollback possible.
Évaluer régulièrement le coût opérationnel du maintien des configs. Se focaliser uniquement sur les perfs brutes sans regarder le coût humain.

Message final :

Le serveur web n’est plus un simple « moteur de fichiers statiques ».
C’est une brique stratégique de sécurité, d’automatisation et de confiance.

Bien choisi et bien configuré, il libère du temps, réduit la dette technique et améliore la résilience globale.

C’est le vrai enjeu de cette formation : faire de votre serveur web un allié, pas un fardeau.

❌ Mythes détruits — Introduction

Les serveurs web sont au centre des architectures, mais leur réputation est souvent guidée par des croyances héritées des années 2000. Cette section a pour but de casser ces idées reçues, en les confrontant aux réalités actuelles (2025) et aux expériences du terrain.

💡 Les mythes ne sont pas seulement faux : ils orientent nos choix d’architecture, parfois au détriment de la sécurité et de la productivité.

❌ Mythe 1 — Nginx enterre Caddy en performance

Nginx, écrit en C, serait imbattable et Caddy trop lent avec Go.

✅ Réalité

Pour 95 % des workloads modernes (reverse proxy, TLS auto, HTTP/2/3), les performances sont proches. Nginx reste champion sur du tuning extrême, mais Caddy est suffisamment rapide sans configuration complexe.

Zoom technique : Nginx utilise une event loop ultra-optimisée (epoll/kqueue). Caddy s’appuie sur Go, ses goroutines et HTTP/3 par défaut, avec un garbage collector qui réduit les crashs mémoire.

Exemple concret

Une fintech a remplacé Nginx (crashé lors d’un pic de trafic malgré un tuning poussé) par Caddy sans réglage particulier. Résultat : stabilité parfaite sous charge.

❌ Mythe 2 — Caddy est trop jeune, pas mature

Caddy serait un « jouet » ou un projet amateur.

✅ Réalité

Avec 10 ans d’existence, Caddy est utilisé par Stripe, HashiCorp et Heroku. Les mises à jour sont régulières, les plugins nombreux et l’écosystème stable.

Timeline : 2015 (HTTPS auto) → 2017 (ACME/Let’s Encrypt) → 2020 (Caddy 2, JSON API) → 2025 (HTTP/3, intégration Kubernetes).

Leçon : La maturité se mesure à l’usage réel, pas à l’ancienneté du projet.

❌ Mythe 3 — Nginx est gratuit, Caddy est payant

Caddy serait réservé aux clients Pro, Nginx totalement gratuit.

✅ Réalité

Caddy OSS inclut TLS auto, HTTP/3 et headers de sécurité. Nginx OSS reste limité (observabilité, API dynamique). Les fonctionnalités avancées de Nginx sont dans Nginx Plus (payant).

Fonction Caddy OSS Nginx OSS Nginx Plus
TLS auto❌ (via scripts)
HTTP/3 natif
Metrics avancées✅ Prometheus

❌ Mythe 4 — Le Caddyfile est trop simpliste

Un fichier concis ne peut pas gérer des cas réels complexes.

✅ Réalité

Le Caddyfile est concis mais extensible (JSON API, adaptateurs YAML/TOML). Moins de lignes ≠ moins de puissance.

# Exemple Caddyfile (3 domaines, HTTPS auto)
example.com {
    reverse_proxy localhost:8080
}
api.example.com {
    reverse_proxy localhost:9090
}
static.example.com {
    file_server
}
        

Équivalent en Nginx : ~50 lignes de configuration.

❌ Mythe 5 — Automatiser TLS est dangereux

« Mieux vaut renouveler les certificats soi-même ».

✅ Réalité

L’automatisation (ACME) est la norme. Les outages les plus graves des 10 dernières années venaient de certificats expirés manuellement.

⚠️ Exemple : en 2020, un géant du cloud a subi une panne mondiale pour un certificat oublié. Depuis, ses équipes ont imposé le renouvellement automatique.

❌ Mythe 6 — Nginx est le standard, Caddy est exotique

Recruter ou former sur Caddy serait impossible.

✅ Réalité

Lisibilité = adoption. Onboarding Caddy : 2 jours. Onboarding Nginx : 2-3 semaines.

Cas réel

Un éditeur SaaS a réduit son onboarding DevOps de 3 semaines → 48 heures après migration progressive vers Caddy.

❌ Mythe 7 — Il faut choisir : Nginx ou Caddy

Migration radicale obligatoire.

✅ Réalité

Les deux peuvent coexister. Caddy peut gérer le TLS/front, Nginx rester derrière pour le tuning legacy. Migration progressive = moins de risques.

🎯 Conclusion des mythes

La plupart de ces idées reçues viennent de 2010, pas de 2025. Le choix d’un serveur web doit reposer sur vos besoins réels, pas sur des mythes hérités.

Leçon finale : les mythes nourrissent la dette technique. Les déconstruire, c’est déjà améliorer vos choix d’architecture.

1. Serveur statique basique

Énoncé

Objectif de départ : publier un simple fichier index.html avec Caddy.
Cet exercice est la porte d’entrée : il montre la puissance de Caddy sur le cas le plus simple possible,
là où Nginx ou Apache demanderaient déjà 15 à 20 lignes de configuration.

💡 A retenir : même pour un site statique basique, tu profites déjà de la simplicité et de la sécurité par défaut de Caddy. Aucun outil supplémentaire requis.

Pré-requis

  • Installer Caddy (brew install caddy / apt install caddy).
  • Créer un dossier site/ avec un index.html.
  • Placer un fichier Caddyfile dans le même dossier.
⚠️ Attention : évite de lancer Caddy avec sudo. Utilise le port 2015 par défaut, ou configure correctement tes droits.

Code final

localhost

root * ./site
file_server

Explications détaillées

  • localhost → définit l’hôte écouté (par défaut :2015).
  • root * ./site → indique le dossier racine à utiliser.
  • file_server → active le serveur de fichiers statiques.
Bonne pratique : garde tes fichiers statiques dans un dossier dédié (./site).

Test attendu

1. Lance caddy run.
2. Ouvre http://localhost:2015.
3. Vérifie le rendu de index.html.
4. Option : curl http://localhost:2015 pour tester la réponse brute.

💡 Anecdote : si index.html est absent, Caddy affiche directement le listing du répertoire.

2. Frontend + Backend FastAPI

Énoncé

Objectif : servir des fichiers statiques pour ton frontend et en parallèle,
proxyfier les requêtes API vers un backend FastAPI lancé avec Uvicorn.

💡 A retenir : Caddy gère nativement le reverse proxy, sans dépendances externes.

Pré-requis

  • Dossier frontend/ avec fichiers HTML/CSS/JS.
  • Backend FastAPI lancé avec uvicorn main:app --port 8000.

Code final

localhost

root * ./frontend
file_server

handle /api/* {
    reverse_proxy localhost:8000
}

Explications détaillées

  • root * ./frontend → sert le frontend.
  • file_server → sert les fichiers statiques.
  • handle /api/* → capture les requêtes API.
  • reverse_proxy localhost:8000 → renvoie vers FastAPI.
Bonne pratique : sépare clairement frontend (./frontend) et backend (API).

Test attendu

1. Lance Caddy.
2. Accède à http://localhost:2015 → frontend.
3. Accède à http://localhost:2015/api/docs → Swagger FastAPI.

3. HTTPS automatique (Let’s Encrypt)

Énoncé

Objectif : mettre en ligne ton site + API en HTTPS
grâce au module ACME intégré de Caddy (Let’s Encrypt).

💡 A retenir : aucun certbot ou cronjob requis, Caddy gère tout seul les certificats.

Pré-requis

  • Un domaine pointant vers ton serveur (mondomaine.com).
  • Ports 80 et 443 ouverts.

Code final

mondomaine.com {
    root * ./site
    file_server

    handle /api/* {
        reverse_proxy localhost:8000
    }
}

Explications détaillées

  • mondomaine.com → déclenche la demande de certificat TLS via ACME.
  • root * ./site → sert les fichiers statiques.
  • reverse_proxy localhost:8000 → sécurise aussi l’API.
⚠️ Attention : le DNS doit pointer correctement vers ton serveur public, sinon l’émission du certificat échoue.

Test attendu

1. Lance Caddy.
2. Ouvre https://mondomaine.com.
3. Vérifie le cadenas HTTPS.
4. Vérifie avec curl -v https://mondomaine.com.

4. Load balancing

Énoncé

Objectif : répartir les requêtes API
entre plusieurs instances FastAPI en round-robin.

Pré-requis

  • Deux serveurs FastAPI : localhost:8001 et localhost:8002.

Code final

localhost

handle /api/* {
    reverse_proxy localhost:8001 localhost:8002
}

Explications détaillées

  • reverse_proxy avec plusieurs cibles → load balancing automatique.
  • Caddy détecte les serveurs HS et les retire temporairement.
Bonne pratique : démarre toujours plusieurs instances pour tester la tolérance aux pannes.

Test attendu

1. Lance Caddy + tes 2 serveurs.
2. Fais curl http://localhost:2015/api/ plusieurs fois.
3. Les réponses doivent alterner entre “API-1” et “API-2”.

5. Authentification basique

Énoncé

Objectif : protéger un chemin sensible (ex: /admin/)
avec login + mot de passe.

Pré-requis

  • Un chemin /admin/ (fichiers ou app).
  • Un mot de passe hashé en bcrypt (caddy hash-password).

Code final

localhost

route /admin/* {
    basicauth {
        admin JDJhJDEyJGQzODc4Ym... # hash bcrypt
    }
    file_server
}

Explications détaillées

  • basicauth → active l’authentification.
  • Le mot de passe n’est jamais stocké en clair → hash bcrypt obligatoire.
⚠️ Attention : basicauth = mot de passe transmis en base64. Utilise toujours en HTTPS.

Test attendu

1. Lance Caddy.
2. Accède à http://localhost:2015/admin/.
3. Un prompt de login s’affiche.
4. Saisis admin / motdepasse.

6. Multi-applications / vhosts

Énoncé

Objectif : héberger plusieurs apps
sur différents sous-domaines.

Pré-requis

  • DNS configurés (api.mondomaine.com, dashboard.mondomaine.com).

Code final

api.mondomaine.com {
    reverse_proxy localhost:8000
}

dashboard.mondomaine.com {
    root * ./dashboard
    file_server
}

Explications détaillées

  • Chaque bloc définit un vhost → auto-certificat TLS par sous-domaine.
  • API et dashboard sont servis indépendamment.
Bonne pratique : isole chaque application dans son propre bloc.

Test attendu

1. Accède à https://api.mondomaine.com → API.
2. Accède à https://dashboard.mondomaine.com → frontend.

7. Caddy Ingress Controller (AKS)

Énoncé

Objectif : déployer Caddy comme Ingress Controller
dans un cluster Kubernetes (AKS).

Pré-requis

  • Cluster AKS fonctionnel.
  • Déploiement FastAPI exposé comme fastapi-service.

Code final (Ingress YAML)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fastapi-ingress
  namespace: apps
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fastapi-service
            port:
              number: 8000

Explications détaillées

  • Ingress → expose ton service via Caddy.
  • annotations → indiquent à Caddy de gérer le reverse proxy.
  • rules → définissent le routage.
⚠️ Attention : pense à créer un certificat TLS ou à activer l’auto-génération avec ACME.

Test attendu

1. Applique le YAML avec kubectl apply -f ingress.yaml.
2. Accède à https://api.mondomaine.com.
3. Vérifie que l’app FastAPI répond correctement.

8. Observabilité & logs

Énoncé

Objectif : activer les logs et exporter des métriques
pour suivre ton serveur avec Prometheus.

Pré-requis

  • Caddy compilé avec plugin Prometheus.
  • Instance Prometheus accessible.

Code final

localhost

log {
    output file ./access.log
    format json
}

metrics /metrics

Explications détaillées

  • log → active un access log JSON.
  • metrics /metrics → expose les métriques pour Prometheus.
Bonne pratique : utilise toujours un format JSON structuré pour analyser tes logs facilement.

Test attendu

1. Lance Caddy.
2. Consulte tail -f access.log.
3. Accède à http://localhost:2015/metrics.
4. Vérifie que Prometheus scrape correctement l’endpoint.

🎯 Conclusion de la mise en pratique

Bravo ! Tu viens de parcourir 8 exercices qui couvrent toutes les briques essentielles de Caddy.
De la distribution de fichiers statiques au rôle d’Ingress Controller Kubernetes, tu as vu
comment un seul outil peut répondre à des besoins extrêmement variés.

💡 À retenir : La force de Caddy, c’est de passer du simple au complexe
sans jamais changer de paradigme. Tu gardes la même syntaxe, le même Caddyfile,
mais tu gagnes en puissance à chaque étape.

📊 Tableau récapitulatif

Exercice Compétence acquise Niveau de difficulté Fun fact
1. Serveur statique Savoir publier un site en 3 lignes ⭐☆☆ Caddy fait en 3 lignes ce que Nginx demande en 20.
2. Frontend + FastAPI Combiner statique + proxy ⭐⭐☆ Tu viens d’éviter 3 heures de config Nginx de type location {}.
3. HTTPS auto Déployer en prod avec TLS auto ⭐⭐☆ Certbot ? Jamais entendu parler. 🍋
4. Load balancing Répartir la charge entre instances ⭐⭐⭐ Round-robin en une ligne, c’est presque de la magie.
5. Auth basique Protéger un répertoire sensible ⭐⭐☆ Ton admin / 1234 ne passera plus en clair (merci bcrypt).
6. Multi-vhosts Gérer plusieurs apps / domaines ⭐⭐⭐ Un bloc = un domaine, et basta. 🔥
7. Ingress AKS Déployer Caddy sur Kubernetes ⭐⭐⭐⭐ Kube + Caddy = combo détonnant, moins verbeux qu’Ingress Nginx.
8. Observabilité Exporter logs + métriques Prometheus ⭐⭐⭐☆ Ton Grafana va t’envoyer des cœurs. 💖

🚀 Message final

Avec ces exos, tu as appris à :
Servir des fichiers statiques instantanément.
Proxyfier tes APIs sans douleur.
Sécuriser par défaut avec HTTPS auto.
Monter en charge avec du load balancing.
Protéger des zones sensibles.
Héberger plusieurs apps sur un même serveur.
Déployer dans Kubernetes sans mal de tête.
Superviser avec logs + Prometheus.

Conclusion fun : Caddy, c’est le serveur web qui enlève les migraines.
Là où Nginx te fait sentir plombier système, Caddy te donne l’impression d’être un chef d’orchestre.
Bref : moins de lignes, plus de vie. 🎵

⚠️ Pré-flight Checklist avant déploiement Kubernetes + Caddy

🌐 Domaine & DNS

  • Nom de domaine → remplacez mondomaine.com par un domaine que vous possédez réellement.
  • DNS A/AAAA → pointez le domaine (et éventuellement le sous-domaine) vers l’IP publique du LoadBalancer/Ingress.
  • Propagation DNS → attendez 5 à 15 minutes (parfois 1h) avant que Let’s Encrypt valide votre certificat.
  • Wildcard → si vous utilisez *.mondomaine.com, assurez-vous que vos DNS TXT sont bien configurés pour ACME.

🔒 TLS & Sécurité

  • Ports ouverts → assurez-vous que 80 et 443 sont accessibles (firewall, NSG Azure, GCP Firewall, Security Groups AWS).
  • Certificats Let’s Encrypt → fonctionne uniquement avec un domaine valide (pas localhost, pas 127.0.0.1).
  • Renouvellement auto → Caddy gère ça, mais testez avec kubectl describe certificate pour vérifier la validité.
  • Secrets TLS → en cas de certificat custom, créez un secret kubectl create secret tls et référencez-le dans votre Ingress.

☸️ Kubernetes

  • Namespace → si Caddy est installé dans un namespace autre que default, adaptez vos commandes kubectl.
  • RBAC → assurez-vous que le ServiceAccount de Caddy a les droits sur les Ingress, Services et Secrets.
  • Ressources → évitez les nœuds trop petits (B1s, e2-micro), risque d’OOMKill si vous combinez Caddy + FastAPI.
  • Probes → configurez toujours livenessProbe, readinessProbe et startupProbe pour vos applis.
  • ConfigMap → centralisez les variables globales (LOG_LEVEL, ENVIRONMENT) pour faciliter le scaling.

📡 Réseau & Load Balancer

  • Service type → utilisez ClusterIP derrière un Ingress, pas NodePort sauf debug.
  • LoadBalancer → si vous êtes sur AKS/EKS/GKE, vérifiez que le service Caddy expose une IP publique.
  • Network Policies → restreignez le trafic inter-namespaces pour éviter un cluster trop “ouvert”.
  • IngressClass → vérifiez que Caddy est bien déclaré comme ingressClassName: caddy.

🚀 CI/CD & Observabilité

  • kubectl apply → testez vos manifests en staging avant la prod.
  • kubectl get pods → attendez l’état Running avant de tester l’Ingress.
  • Logs Caddy → surveillez kubectl logs -l app=caddy pour détecter les erreurs TLS.
  • Monitoring → activez Prometheus/Grafana ou un équivalent pour suivre CPU/mémoire/pods down.
  • CI/CD → pipelinez vos déploiements (GitHub Actions, GitLab CI, ArgoCD) pour éviter les déploiements manuels risqués.

❌ Erreurs fréquentes

  • Utiliser localhost → Let’s Encrypt refusera le certificat.
  • Oublier ingressClassName → votre Ingress ne sera jamais pris en compte par Caddy.
  • Confondre ClusterIP et NodePort → résultat : service inaccessible depuis l’extérieur.
  • Négliger les probes → pods considérés “down” ou “never ready”.
  • IP publique bloquée par un firewall → 404 systématique malgré un déploiement OK.

✅ Passez en revue chaque point de cette checklist avant de lancer vos commandes.
Cela vous évitera 90% des erreurs classiques rencontrées lors des premiers déploiements.

🌐 Introduction — Pourquoi parler d’Ingress Controllers et de Caddy

Kubernetes est devenu la plateforme incontournable pour orchestrer les applications conteneurisées. Mais malgré sa puissance, une réalité demeure : le cluster Kubernetes n’a aucune idée de la façon dont il doit exposer vos services au monde extérieur. Par défaut, il ne sait que gérer des IP internes, des réseaux overlay, et des Services de type ClusterIP. Cela suffit pour que les pods discutent entre eux… mais sûrement pas pour servir votre application à vos utilisateurs.

C’est là qu’intervient la notion d’Ingress et, par extension, d’Ingress Controller. Dans le jargon Kubernetes, un Ingress n’est rien d’autre qu’une ressource qui décrit « si une requête arrive sur www.monsite.com, envoie-la vers le Service X sur le port Y ». Mais attention : ce n’est qu’une abstraction. Seul un Ingress Controller, installé dans votre cluster, peut traduire cette règle en un vrai reverse proxy fonctionnel capable de gérer TLS, routage, redirections, et autres subtilités HTTP.

Les solutions classiques : Nginx et Traefik

Depuis les débuts de Kubernetes, le choix par défaut a presque toujours été Nginx Ingress Controller. Robuste, bien connu, extrêmement documenté, il reste une valeur sûre. Mais il souffre d’un problème majeur : sa verbosité. Chaque nouvelle règle, chaque certificat, chaque comportement particulier doit être configuré via une combinaison d’annotations parfois obscures et de ConfigMaps interminables. Traefik a tenté d’apporter une alternative, plus automatique et plus « cloud-native », mais au prix d’une complexité croissante dès que l’on sort des cas simples.

Résultat ? Les équipes passent des jours à se battre avec la configuration plutôt qu’à livrer de la valeur métier. Et la gestion de TLS, via l’addon Cert-Manager, est devenue une usine à gaz : plusieurs CRD, un contrôleur supplémentaire, une dépendance critique… Tout ça pour obtenir un simple certificat HTTPS valide.

L’approche radicalement différente de Caddy

C’est dans ce contexte que Caddy change la donne. Connu pour être le premier serveur web à activer par défaut HTTPS automatique via Let’s Encrypt, Caddy applique cette même philosophie au monde Kubernetes. Plutôt que de multiplier les modules et les dépendances, il propose :

  • Un Ingress Controller natif qui sait lire vos objets Ingress et générer à la volée sa configuration.
  • Une gestion automatique des certificats TLS sans Cert-Manager, directement intégrée.
  • Une syntaxe claire et lisible, qui évite les centaines de lignes de directives.
  • Un moteur moderne en Go, capable de gérer HTTP/3, WebSockets et du load balancing par défaut.
💡 Idée clé : Là où Nginx Ingress et Traefik vous forcent à devenir expert en annotations Kubernetes, Caddy vous permet de vous concentrer sur l’essentiel : vos applications et vos domaines.

Exemple concret

Imaginez que vous déployiez une application FastAPI appelée via api.mondomaine.com. Avec Nginx Ingress, vous devez définir un objet Ingress, gérer une ConfigMap, configurer Cert-Manager, installer les CRD associées, et espérer que la chaîne de certificats se génère correctement. Avec Caddy ?

  1. Vous créez un Ingress avec le host api.mondomaine.com pointant vers votre Service.
  2. Caddy détecte la règle, demande un certificat Let’s Encrypt, configure automatiquement le routage.
  3. Résultat : HTTPS valide en quelques secondes, zéro configuration supplémentaire.

Anticiper les questions

  • Conflits de ports : si vous avez déjà Nginx Ingress installé, Caddy ne pourra pas écouter sur 80/443. Il faut désinstaller l’un, ou isoler l’autre avec un LoadBalancer séparé.
  • Compatibilité : Caddy supporte la majorité des cas d’usage Ingress (routage, path, hôtes multiples). Certaines annotations spécifiques à Nginx ne sont pas prises en charge (logique, c’est une implémentation différente).
  • Production : oui, Caddy Ingress est utilisé en prod par plusieurs entreprises. Son moteur TLS intégré le rend même plus simple et moins fragile qu’un couple Nginx + Cert-Manager.
⚠️ Attention : adopter Caddy Ingress ne signifie pas ignorer les bonnes pratiques Kubernetes. Vous devez toujours configurer correctement vos DNS, sécuriser vos Secrets, et surveiller vos pods avec des probes de santé.

Résumé de l’introduction

Dans cette introduction, vous avez compris :

  • Le rôle exact d’un Ingress Controller dans Kubernetes.
  • Les limites historiques des solutions Nginx Ingress et Traefik.
  • Ce que Caddy apporte de radicalement différent : automatisation, simplicité, sécurité par défaut.
  • Comment, concrètement, il peut réduire de plusieurs heures vos déploiements.
Message final : Caddy Ingress, c’est un peu comme passer de la machine à écrire à l’éditeur moderne. On peut taper du texte avec les deux, mais l’un vous fait perdre du temps à chaque phrase. Avec Kubernetes, le temps gagné n’est pas un luxe : c’est un avantage stratégique.

🚀 Déploiement du Caddy Ingress Controller

Après avoir compris le rôle d’un Ingress Controller, il est temps de passer à l’action. Dans cette étape, nous allons installer Caddy Ingress Controller dans notre cluster AKS. C’est lui qui deviendra la porte d’entrée HTTP/HTTPS pour toutes vos applications. Contrairement aux solutions traditionnelles (Nginx Ingress + Cert-Manager), la force de Caddy est de réduire la complexité en un seul composant.

📋 Pré-requis

  • Un cluster AKS fonctionnel avec au moins 1 nœud (testez avec kubectl get nodes).

  • kubectl installé et configuré localement.

  • Helm installé (helm version doit répondre).

  • Un domaine public qui pointe vers l’IP externe de votre cluster (pour HTTPS automatique).

  • Ports 80 et 443 ouverts dans les règles de firewall/NSG Azure.
💡 Astuce : si vous n’avez pas encore de domaine, vous pouvez utiliser un sous-domaine dynamique (nip.io ou sslip.io) qui résout automatiquement vers une IP.

⚙️ Commandes d’installation


helm repo add caddy https://caddyserver.github.io/ingress/
helm repo update

helm install caddy-ingress caddy/ingress \
  --namespace ingress-caddy --create-namespace
  

🔍 Explication ligne par ligne

  • helm repo add → ajoute le dépôt officiel du chart Helm de Caddy.

  • helm repo update → synchronise vos métadonnées locales.

  • helm install → installe Caddy comme Ingress Controller, dans le namespace dédié ingress-caddy.

  • --create-namespace → évite les conflits en créant un espace isolé.

📊 Résultat attendu


kubectl get pods -n ingress-caddy

NAME                               READY   STATUS    RESTARTS   AGE
caddy-ingress-6c6d6f7c4d-abcde    1/1     Running   0          30s
  
Bonne pratique : dédiez toujours un namespace spécifique (ingress-caddy) pour vos Ingress Controllers. Cela évite toute confusion avec vos workloads applicatifs.

🚧 Problèmes fréquents et solutions

Problème Symptôme Cause probable Solution
Pod en CrashLoopBackOff kubectl get pods affiche des redémarrages en boucle Version de Kubernetes non compatible, ou ports déjà utilisés Vérifiez kubectl logs, adaptez la version du chart, libérez les ports
Pas d’IP externe kubectl get svc -n ingress-caddy → colonne EXTERNAL-IP vide Cluster AKS sans LoadBalancer public Vérifiez vos NSG Azure, configurez un LoadBalancer public
Certificat TLS non généré Accès HTTPS impossible, erreur navigateur DNS mal configuré, propagation incomplète Corrigez votre DNS, attendez la propagation (5-15 minutes)

📦 Ce que vous obtenez après installation

🌍 Entrée unique

Toutes les requêtes entrantes (port 80/443) passent par Caddy, qui devient le contrôleur d’entrée unique.

🔒 TLS automatique

Caddy demande et renouvelle automatiquement des certificats Let’s Encrypt ou ZeroSSL sans Cert-Manager.

⚡ Routage simplifié

Plus besoin d’annotations obscures : chaque Ingress YAML est traduit directement en règle de proxy.

📊 Observabilité

Dès l’installation, Caddy expose logs et métriques Prometheus pour surveiller vos flux HTTP.

📚 Résumé

En quelques commandes Helm, vous avez déployé un Ingress Controller moderne :
Installation simplifiée
Namespace isolé
TLS automatique prêt à l’emploi
Observabilité intégrée

💡 Anecdote : dans de nombreuses équipes, la mise en place d’un Ingress Nginx + Cert-Manager prend une demi-journée et plusieurs fichiers YAML. Avec Caddy, certains rapportent avoir un site HTTPS fonctionnel en moins de 10 minutes.

🐍 Déploiement d'une application FastAPI

Maintenant que Caddy Ingress Controller est opérationnel, nous allons déployer une application FastAPI qui servira d'exemple concret. Cette API REST sera notre cible pour tester le routage HTTP, les certificats TLS, et les fonctionnalités avancées de Caddy. L'objectif est de créer un workload applicatif complet : Deployment, Service, et ConfigMap, prêt à être exposé via un Ingress.

📋 Application FastAPI de référence

Nous utiliserons une API simple avec plusieurs endpoints pour tester les capacités de routage :

Endpoints de test :
GET / → Message de bienvenue
GET /health → Health check pour Kubernetes
GET /api/users → Liste d'utilisateurs (simulation)
POST /api/users → Création d'utilisateur
GET /metrics → Métriques Prometheus

🐳 Dockerfile et image


FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
  

# main.py - Application FastAPI minimale
from fastapi import FastAPI
from fastapi.responses import JSONResponse
import time
import random

app = FastAPI(title="Demo API", version="1.0.0")

# Base de données simulée
users_db = [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"}
]

@app.get("/")
async def root():
    return {"message": "FastAPI demo running on Kubernetes!", "timestamp": time.time()}

@app.get("/health")
async def health_check():
    return {"status": "healthy", "uptime": time.time()}

@app.get("/api/users")
async def get_users():
    return {"users": users_db, "count": len(users_db)}

@app.post("/api/users")
async def create_user(user: dict):
    new_id = max([u["id"] for u in users_db]) + 1
    new_user = {"id": new_id, **user}
    users_db.append(new_user)
    return {"created": new_user}

@app.get("/metrics")
async def metrics():
    return JSONResponse({
        "requests_total": random.randint(100, 1000),
        "response_time_avg": round(random.uniform(0.1, 0.5), 3),
        "memory_usage": f"{random.randint(50, 200)}MB"
    })
  

⚙️ Deployment Kubernetes


apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-deployment
  labels:
    app: fastapi
    version: v1.0.0
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
        version: v1.0.0
    spec:
      containers:
      - name: fastapi
        image: your-registry/fastapi-demo:1.0.0
        ports:
        - containerPort: 8000
          name: http
        env:
        - name: ENVIRONMENT
          value: "production"
        - name: LOG_LEVEL
          value: "info"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 2
        startupProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 10
---
apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
  labels:
    app: fastapi
spec:
  selector:
    app: fastapi
  ports:
  - port: 80
    targetPort: 8000
    protocol: TCP
    name: http
  type: ClusterIP
  

🔍 Explication détaillée

📦 Configuration du Deployment

  • replicas: 3 → Haute disponibilité avec 3 instances

  • resources → Limites mémoire/CPU pour éviter les dérives

  • livenessProbe → Kubernetes redémarre le pod si l'app plante

  • readinessProbe → Le pod ne reçoit du trafic que s'il est prêt

  • startupProbe → Gère le démarrage lent de l'application

🌐 Configuration du Service

  • type: ClusterIP → Service interne uniquement (pas d'exposition directe)

  • port: 80 → Port d'entrée pour l'Ingress

  • targetPort: 8000 → Port de l'application dans le container

  • selector: app: fastapi → Route vers les pods avec ce label
Bonne pratique : utilisez toujours les 3 types de probes (liveness, readiness, startup) pour une robustesse maximale en production.

📊 ConfigMap pour la configuration


apiVersion: v1
kind: ConfigMap
metadata:
  name: fastapi-config
  labels:
    app: fastapi
data:
  app.conf: |
    {
      "title": "FastAPI Demo",
      "description": "API de démonstration pour Caddy Ingress",
      "version": "1.0.0",
      "cors_origins": ["https://app.mondomaine.com"],
      "rate_limit": {
        "requests_per_minute": 100,
        "burst": 20
      },
      "logging": {
        "level": "info",
        "format": "json"
      }
    }
  nginx.conf: |
    # Configuration pour un éventuel sidecar Nginx
    upstream fastapi {
        server localhost:8000;
    }
    
    server {
        listen 8080;
        location / {
            proxy_pass http://fastapi;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
---
apiVersion: v1
kind: Secret
metadata:
  name: fastapi-secrets
  labels:
    app: fastapi
type: Opaque
stringData:
  database_url: "postgresql://user:password@db.example.com:5432/myapp"
  api_key: "super-secret-api-key-for-external-services"
  jwt_secret: "jwt-signing-secret-key-change-in-production"
  

🚀 Commandes de déploiement


# Déployer l'application FastAPI
kubectl apply -f fastapi-deployment.yaml

# Déployer la configuration
kubectl apply -f fastapi-config.yaml

# Vérifier le déploiement
kubectl get pods -l app=fastapi
kubectl get svc fastapi-service
kubectl describe deployment fastapi-deployment

# Logs de l'application
kubectl logs deployment/fastapi-deployment -f

# Test interne (depuis un pod)
kubectl run test-pod --image=curlimages/curl --rm -it --restart=Never \
  -- curl http://fastapi-service/health
  

📊 Résultat attendu


kubectl get all -l app=fastapi

NAME                                     READY   STATUS    RESTARTS   AGE
pod/fastapi-deployment-abc123-def45     1/1     Running   0          2m
pod/fastapi-deployment-abc123-ghi78     1/1     Running   0          2m
pod/fastapi-deployment-abc123-jkl90     1/1     Running   0          2m

NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/fastapi-service   ClusterIP   10.0.123.456   <none>        80/TCP    2m

NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/fastapi-deployment   3/3     3            3           2m

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/fastapi-deployment-abc123     3         3         3       2m
  

🔧 Optimisations pour la production

🏷️ Tagging et versioning


# Utilisation de tags spécifiques (pas de :latest)
image: your-registry/fastapi-demo:v1.2.3-sha-abc1234

# Labels pour le suivi
labels:
  app: fastapi
  version: v1.2.3
  environment: production
  team: backend
  cost-center: "engineering"
  

⚡ Performance et scaling


# Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: fastapi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fastapi-deployment
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  

🚨 Troubleshooting FastAPI

Problème Symptôme Diagnostic Solution
Pod en CrashLoopBackOff Redémarrages constants kubectl logs pod-name --previous Vérifier les variables d'env, ressources, dépendances
Service inaccessible Timeout sur curl interne kubectl get endpoints fastapi-service Vérifier labels/selectors, readiness probe
Lenteur de démarrage Pods "NotReady" longtemps Ajuster initialDelaySeconds Optimiser startup probe, réduire les imports Python
Consommation mémoire OOMKilled dans les events kubectl top pods Augmenter limits.memory, optimiser le code

📈 Monitoring applicatif


# ServiceMonitor pour Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: fastapi-metrics
  labels:
    app: fastapi
spec:
  selector:
    matchLabels:
      app: fastapi
  endpoints:
  - port: http
    path: /metrics
    interval: 30s
    honorLabels: true
  
💡 Astuce production : intégrez dès le début des métriques custom dans votre FastAPI (avec prometheus_client) pour surveiller les KPIs métier : nombre d'utilisateurs actifs, temps de réponse par endpoint, erreurs business, etc.

🔗 Prochaine étape

Votre application FastAPI est maintenant déployée et accessible en interne via le Service. Dans la section suivante, nous allons créer un Ingress pour l'exposer au monde extérieur avec Caddy, en activant automatiquement HTTPS et le routage par domaine.

Résumé : Vous disposez maintenant d'une application FastAPI complète avec :
• Deployment multi-répliques avec health checks
• Service ClusterIP pour l'accès interne
• ConfigMap et Secret pour la configuration
• HPA pour l'auto-scaling
• Monitoring Prometheus ready

🛡️ Création de l'Ingress avec Caddy

Nous arrivons à l'étape clé : exposer une application interne (FastAPI) au monde extérieur.
Dans Kubernetes, cela se fait via une ressource Ingress, qui définit des règles de routage HTTP.
Grâce au Caddy Ingress Controller, cette étape devient non seulement simple, mais surtout automatisée : TLS, redirections HTTPS, proxy et gestion fine des chemins sont intégrés par défaut.

💡 Concept clé : un Ingress n'expose pas une app directement. C'est une abstraction de routage : il reçoit le trafic externe, puis l'oriente vers un Service interne.
Le flux complet : Internet → LoadBalancer → Ingress Controller → Service → Pods

🎯 Objectifs de cette section

  • Créer un objet Ingress qui redirige tout le trafic reçu sur api.mondomaine.com vers notre Service fastapi-service
  • Obtenir automatiquement un certificat TLS Let's Encrypt sans configuration manuelle
  • Implémenter des règles de routage avancées (multi-domaines, chemins, headers)
  • Configurer des middlewares pour la sécurité et les performances
  • Maîtriser le troubleshooting et le monitoring

📋 Prérequis

⚠️ Avant de continuer, assurez-vous que :
• Caddy Ingress Controller est installé et opérationnel
• Votre Service fastapi-service fonctionne correctement
• Le domaine api.mondomaine.com pointe vers l'IP publique du LoadBalancer
• Les ports 80 (HTTP) et 443 (HTTPS) sont ouverts dans votre firewall
• Vous avez les droits d'administration DNS pour votre domaine

⚙️ Configuration de base


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fastapi-ingress
  namespace: default
  annotations:
    # Active le reverse proxy Caddy
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Force la redirection HTTPS (optionnel, activé par défaut)
    caddy.ingress.kubernetes.io/force-ssl-redirect: "true"
    # Timeout pour les requêtes backend
    caddy.ingress.kubernetes.io/backend-timeout: "30s"
  labels:
    app: fastapi
    environment: production
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fastapi-service
            port:
              number: 80

🔧 Configuration avancée multi-domaines


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-domain-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Configuration CORS
    caddy.ingress.kubernetes.io/cors: "true"
    caddy.ingress.kubernetes.io/cors-allowed-origins: "https://app.mondomaine.com,https://admin.mondomaine.com"
    caddy.ingress.kubernetes.io/cors-allowed-methods: "GET,POST,PUT,DELETE,OPTIONS"
    caddy.ingress.kubernetes.io/cors-allowed-headers: "Content-Type,Authorization,X-Requested-With"
    # Rate limiting
    caddy.ingress.kubernetes.io/rate-limit: "100r/m"
    # Compression
    caddy.ingress.kubernetes.io/gzip: "true"
spec:
  rules:
  # API principale
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fastapi-service
            port:
              number: 80
  
  # API d'administration
  - host: admin.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 80
  
  # WebSocket pour temps réel
  - host: ws.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8080

🛣️ Routage avancé par chemins


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Réécrit les chemins pour les services backend
    caddy.ingress.kubernetes.io/rewrite-target: "/$2"
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      # API v1
      - path: /api/v1(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: api-v1-service
            port:
              number: 80
      
      # API v2 
      - path: /api/v2(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: api-v2-service
            port:
              number: 80
      
      # Interface d'administration
      - path: /admin(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: admin-service
            port:
              number: 80
      
      # Fichiers statiques
      - path: /static(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: static-files-service
            port:
              number: 80
      
      # Métriques (accès restreint)
      - path: /metrics
        pathType: Exact
        backend:
          service:
            name: metrics-service
            port:
              number: 9090

🔒 Configuration TLS avancée


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-advanced-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Certificat personnalisé (optionnel)
    caddy.ingress.kubernetes.io/tls-secret: "custom-tls-cert"
    # Configuration TLS stricte
    caddy.ingress.kubernetes.io/tls-min-version: "1.2"
    caddy.ingress.kubernetes.io/tls-max-version: "1.3"
    # HSTS (HTTP Strict Transport Security)
    caddy.ingress.kubernetes.io/hsts: "true"
    caddy.ingress.kubernetes.io/hsts-max-age: "31536000"
    caddy.ingress.kubernetes.io/hsts-include-subdomains: "true"
spec:
  tls:
  - hosts:
    - api.mondomaine.com
    - admin.mondomaine.com
    secretName: wildcard-tls-cert
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fastapi-service
            port:
              number: 80

🔎 Explication détaillée des composants

🏷️ Metadata et Labels

  • apiVersion: networking.k8s.io/v1 → Version stable de l'API Kubernetes pour les Ingress
  • kind: Ingress → Type de ressource Kubernetes
  • metadata.name → Identifiant unique de l'Ingress dans le namespace
  • metadata.namespace → Namespace cible (default si non spécifié)
  • labels → Métadonnées pour le filtrage et la sélection

📝 Annotations Caddy

  • reverse-proxy: "true" → Active le mode proxy inverse
  • force-ssl-redirect: "true" → Force la redirection HTTP vers HTTPS
  • backend-timeout: "30s" → Délai d'attente pour les réponses backend
  • cors: "true" → Active la gestion Cross-Origin Resource Sharing
  • rate-limit: "100r/m" → Limite à 100 requêtes par minute par IP
  • gzip: "true" → Active la compression des réponses

🎯 Règles de routage

  • rules.host → Domaine cible (doit pointer vers l'IP du LoadBalancer)
  • paths.path → Chemin URL à matcher
  • pathType → Type de matching : Exact, Prefix, ImplementationSpecific
  • backend.service.name → Service Kubernetes cible
  • backend.service.port.number → Port du service (pas du pod)

📊 Types de pathType expliqués

Type Comportement Exemple Use Case
Exact Correspondance exacte de l'URL /api match seulement /api Endpoints spécifiques
Prefix Match par préfixe /api match /api/* Groupes d'endpoints
ImplementationSpecific Regex et règles avancées /api/v(\d+) Routage complexe

🧪 Déploiement et vérification


# Déploiement de l'Ingress
kubectl apply -f fastapi-ingress.yaml

# Vérification du statut
kubectl get ingress
kubectl describe ingress fastapi-ingress

# Vérification détaillée
kubectl get ingress fastapi-ingress -o yaml

# Logs du contrôleur Caddy
kubectl logs -n caddy-system deployment/caddy-ingress-controller -f

# Test de connectivité
curl -v https://api.mondomaine.com
curl -I https://api.mondomaine.com/health

📈 Monitoring et observabilité


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: monitored-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Métriques Prometheus
    caddy.ingress.kubernetes.io/metrics: "true"
    caddy.ingress.kubernetes.io/metrics-path: "/metrics"
    # Logs détaillés
    caddy.ingress.kubernetes.io/access-log: "true"
    caddy.ingress.kubernetes.io/error-log: "true"
    # Health checks
    caddy.ingress.kubernetes.io/health-check: "/health"
    caddy.ingress.kubernetes.io/health-check-interval: "30s"
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fastapi-service
            port:
              number: 80

🔧 Annotations Caddy complètes

Annotation Valeurs possibles Utilité Exemple
reverse-proxy true/false Active le mode proxy inverse "true"
force-ssl-redirect true/false Force la redirection HTTPS "true"
tls-secret nom du secret Certificat TLS personnalisé "my-tls-secret"
websocket true/false Support natif des WebSockets "true"
cors true/false Active CORS "true"
cors-allowed-origins liste d'URLs Origins autorisées pour CORS "https://app.com,https://admin.com"
rate-limit format rate Limitation du taux de requêtes "100r/m", "10r/s"
gzip true/false Compression des réponses "true"
backend-timeout durée Timeout backend "30s", "5m"
client-max-body-size taille Taille max des uploads "10MB", "1GB"
auth-basic realm Authentification basique "Restricted Area"
auth-basic-secret nom du secret Identifiants auth basique "basic-auth-secret"
rewrite-target pattern Réécrit l'URL backend "/$2"
add-headers headers Ajoute des headers custom "X-Real-IP $remote_addr"

🚨 Troubleshooting avancé

🔍 Diagnostics de base


# État de l'Ingress
kubectl get ingress fastapi-ingress -o wide

# Description détaillée
kubectl describe ingress fastapi-ingress

# Événements liés
kubectl get events --field-selector involvedObject.name=fastapi-ingress

# Logs du contrôleur
kubectl logs -n caddy-system deployment/caddy-ingress-controller --tail=100

# Test de résolution DNS
nslookup api.mondomaine.com
dig api.mondomaine.com

# Test de connectivité
telnet api.mondomaine.com 443
openssl s_client -connect api.mondomaine.com:443 -servername api.mondomaine.com

❌ Problèmes courants et solutions

🔥 Certificat TLS non généré
Symptômes : Erreur SSL, certificat auto-signé
Causes :
  • DNS ne pointe pas vers la bonne IP
  • Ports 80/443 bloqués par le firewall
  • Challenge Let's Encrypt échoue
Solutions :
  • kubectl describe certificate pour voir les détails
  • Vérifier les logs cert-manager si installé
  • Tester avec curl -k https://api.mondomaine.com
  • Forcer un nouveau certificat : kubectl delete secret tls-cert-name
🔥 404 Not Found
Symptômes : Page 404 de Caddy
Causes :
  • Service backend inexistant ou mal nommé
  • Path de l'Ingress incorrect
  • Service dans un autre namespace
Solutions :
  • kubectl get svc pour vérifier les services
  • kubectl describe svc fastapi-service
  • Tester directement : kubectl port-forward svc/fastapi-service 8080:80
🔥 502 Bad Gateway
Symptômes : Erreur 502 intermittente ou constante
Causes :
  • Pods backend crash ou non prêts
  • Port incorrect dans le Service
  • Health check qui échoue
  • Timeout backend trop court
Solutions :
  • kubectl get pods -l app=fastapi
  • kubectl logs deployment/fastapi-deployment
  • Augmenter backend-timeout
  • Vérifier les readiness/liveness probes
🔥 503 Service Unavailable
Symptômes : Service temporairement indisponible
Causes :
  • Aucun pod backend disponible
  • Tous les pods en cours de démarrage
  • Rate limiting trop strict
Solutions :
  • kubectl get endpoints fastapi-service
  • Vérifier les répliques : kubectl scale deployment fastapi-deployment --replicas=3
  • Ajuster le rate limiting

🔒 Sécurité et authentification

🛡️ Authentification basique


# Créer le secret d'authentification
apiVersion: v1
kind: Secret
metadata:
  name: basic-auth-secret
type: Opaque
stringData:
  auth: |
    admin:$2y$10$2b2cu4QWyYYfLiYYxBr6mOW1N0rNaE.V3yKHPyv3kVzRsZ1ZgPJ7u
    user:$2y$10$4YYnM2cGm7g3l3YyL3ys7.F1E5uZ6a7bK9vV2lL5J6uZ4gH8oR2bG
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/auth-basic: "Restricted API"
    caddy.ingress.kubernetes.io/auth-basic-secret: "basic-auth-secret"
spec:
  rules:
  - host: secure.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: secure-service
            port:
              number: 80

🔐 Restriction d'accès par IP


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ip-restricted-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Whitelist d'IPs (séparées par des virgules)
    caddy.ingress.kubernetes.io/whitelist-source-range: "192.168.1.0/24,10.0.0.0/8,203.0.113.0/24"
    # Ou blacklist
    # caddy.ingress.kubernetes.io/blacklist-source-range: "1.2.3.4/32,5.6.7.0/24"
spec:
  rules:
  - host: internal.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: internal-service
            port:
              number: 80

⚡ Performance et optimisation

🚀 Configuration haute performance


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: high-performance-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Cache statique
    caddy.ingress.kubernetes.io/cache: "true"
    caddy.ingress.kubernetes.io/cache-duration: "1h"
    # Compression
    caddy.ingress.kubernetes.io/gzip: "true"
    caddy.ingress.kubernetes.io/gzip-level: "6"
    # Keep-alive
    caddy.ingress.kubernetes.io/upstream-keepalive: "100"
    caddy.ingress.kubernetes.io/upstream-keepalive-timeout: "60s"
    # Buffer de réponse
    caddy.ingress.kubernetes.io/proxy-buffer-size: "8k"
    caddy.ingress.kubernetes.io/proxy-buffers: "8 8k"
    # Timeout optimisés
    caddy.ingress.kubernetes.io/backend-timeout: "60s"
    caddy.ingress.kubernetes.io/client-timeout: "60s"
spec:
  rules:
  - host: fast.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: fast-service
            port:
              number: 80

📊 Configuration avec load balancing


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: load-balanced-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Algorithme de load balancing
    caddy.ingress.kubernetes.io/load-balance: "round_robin"  # ou "least_conn", "ip_hash"
    # Health check avancé
    caddy.ingress.kubernetes.io/health-check: "/health"
    caddy.ingress.kubernetes.io/health-check-interval: "30s"
    caddy.ingress.kubernetes.io/health-check-timeout: "5s"
    caddy.ingress.kubernetes.io/health-check-unhealthy-threshold: "3"
    caddy.ingress.kubernetes.io/health-check-healthy-threshold: "2"
    # Retry policy
    caddy.ingress.kubernetes.io/retry-attempts: "3"
    caddy.ingress.kubernetes.io/retry-timeout: "5s"
spec:
  rules:
  - host: balanced.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: balanced-service
            port:
              number: 80

🌐 Cas d'usage avancés

🔄 Blue/Green Deployment avec Ingress


# Version Blue (production actuelle)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: blue-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/weight: "90"  # 90% du trafic
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-blue-service
            port:
              number: 80
---
# Version Green (nouvelle version en test)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: green-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/weight: "10"  # 10% du trafic
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-green-service
            port:
              number: 80

🔗 Ingress avec WebSockets


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: websocket-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Support WebSocket natif
    caddy.ingress.kubernetes.io/websocket: "true"
    # Headers WebSocket
    caddy.ingress.kubernetes.io/websocket-headers: "Upgrade,Connection,Sec-WebSocket-Key,Sec-WebSocket-Protocol,Sec-WebSocket-Version"
    # Timeout plus long pour les connexions persistantes
    caddy.ingress.kubernetes.io/backend-timeout: "300s"
    caddy.ingress.kubernetes.io/client-timeout: "300s"
spec:
  rules:
  - host: ws.mondomaine.com
    http:
      paths:
      - path: /ws
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8080
  - host: api.mondomaine.com
    http:
      paths:
      # API REST classique
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: rest-api-service
            port:
              number: 80
      # WebSocket sur le même domaine
      - path: /ws
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8080

🎭 Ingress avec gestion des environnements


# Production
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production-ingress
  namespace: production
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/rate-limit: "1000r/m"
    caddy.ingress.kubernetes.io/hsts: "true"
    caddy.ingress.kubernetes.io/security-headers: "true"
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-production-service
            port:
              number: 80
---
# Staging
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: staging-ingress
  namespace: staging
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/auth-basic: "Staging Environment"
    caddy.ingress.kubernetes.io/auth-basic-secret: "staging-auth"
    caddy.ingress.kubernetes.io/rate-limit: "100r/m"
spec:
  rules:
  - host: staging.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-staging-service
            port:
              number: 80
---
# Development
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: development-ingress
  namespace: development
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Pas de rate limiting en dev
    caddy.ingress.kubernetes.io/cors: "true"
    caddy.ingress.kubernetes.io/cors-allowed-origins: "*"
spec:
  rules:
  - host: dev.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-development-service
            port:
              number: 80

📱 Ingress pour applications mobiles


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mobile-api-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Optimisations pour mobile
    caddy.ingress.kubernetes.io/gzip: "true"
    caddy.ingress.kubernetes.io/gzip-min-length: "1000"
    # Headers spécifiques mobile
    caddy.ingress.kubernetes.io/add-headers: |
      X-Content-Type-Options nosniff
      X-Frame-Options DENY
      X-XSS-Protection "1; mode=block"
      Cache-Control "public, max-age=300"
    # Rate limiting adapté aux mobiles
    caddy.ingress.kubernetes.io/rate-limit: "200r/m"
    # Timeout plus court pour réactivité
    caddy.ingress.kubernetes.io/backend-timeout: "15s"
spec:
  rules:
  - host: mobile-api.mondomaine.com
    http:
      paths:
      # API v1 pour anciennes versions de l'app
      - path: /api/v1
        pathType: Prefix
        backend:
          service:
            name: mobile-api-v1-service
            port:
              number: 80
      # API v2 pour nouvelles versions
      - path: /api/v2
        pathType: Prefix
        backend:
          service:
            name: mobile-api-v2-service
            port:
              number: 80
      # Upload de fichiers (limite plus élevée)
      - path: /upload
        pathType: Prefix
        backend:
          service:
            name: upload-service
            port:
              number: 80

🛠️ Scripts d'automatisation

📜 Script de déploiement


#!/bin/bash
# deploy-ingress.sh

set -e

NAMESPACE=${NAMESPACE:-default}
DOMAIN=${DOMAIN:-api.mondomaine.com}
SERVICE_NAME=${SERVICE_NAME:-fastapi-service}
SERVICE_PORT=${SERVICE_PORT:-80}

echo "🚀 Déploiement de l'Ingress Caddy..."
echo "📋 Configuration:"
echo "   - Namespace: $NAMESPACE"
echo "   - Domaine: $DOMAIN"
echo "   - Service: $SERVICE_NAME:$SERVICE_PORT"

# Vérification des prérequis
echo "🔍 Vérification des prérequis..."

# Vérifier que le namespace existe
if ! kubectl get namespace $NAMESPACE &> /dev/null; then
    echo "❌ Le namespace $NAMESPACE n'existe pas"
    exit 1
fi

# Vérifier que le service existe
if ! kubectl get service $SERVICE_NAME -n $NAMESPACE &> /dev/null; then
    echo "❌ Le service $SERVICE_NAME n'existe pas dans le namespace $NAMESPACE"
    exit 1
fi

# Générer le manifest Ingress
cat < /dev/null; then
    echo "✅ L'API est accessible sur https://$DOMAIN"
else
    echo "⚠️ L'API n'est pas encore accessible (certificat en cours ou DNS non propagé)"
    echo "💡 Testez manuellement: curl -k https://$DOMAIN"
fi

echo "🎉 Déploiement terminé!"

🔧 Script de diagnostic


#!/bin/bash
# diagnose-ingress.sh

INGRESS_NAME=${1:-fastapi-ingress}
NAMESPACE=${2:-default}

echo "🔍 Diagnostic de l'Ingress: $INGRESS_NAME"
echo "📍 Namespace: $NAMESPACE"
echo "="

# 1. État de l'Ingress
echo "1️⃣ État de l'Ingress"
kubectl get ingress $INGRESS_NAME -n $NAMESPACE -o wide
echo ""

# 2. Description détaillée
echo "2️⃣ Description détaillée"
kubectl describe ingress $INGRESS_NAME -n $NAMESPACE
echo ""

# 3. Événements liés
echo "3️⃣ Événements récents"
kubectl get events -n $NAMESPACE --field-selector involvedObject.name=$INGRESS_NAME --sort-by='.lastTimestamp'
echo ""

# 4. État du contrôleur Caddy
echo "4️⃣ État du contrôleur Caddy"
kubectl get pods -n caddy-system -l app=caddy-ingress-controller
kubectl logs -n caddy-system deployment/caddy-ingress-controller --tail=20
echo ""

# 5. Services backend
echo "5️⃣ Services backend"
SERVICES=$(kubectl get ingress $INGRESS_NAME -n $NAMESPACE -o jsonpath='{.spec.rules[*].http.paths[*].backend.service.name}')
for svc in $SERVICES; do
    echo "🔍 Service: $svc"
    kubectl get service $svc -n $NAMESPACE -o wide
    kubectl get endpoints $svc -n $NAMESPACE
    echo ""
done

# 6. Test de résolution DNS
echo "6️⃣ Test DNS"
HOSTS=$(kubectl get ingress $INGRESS_NAME -n $NAMESPACE -o jsonpath='{.spec.rules[*].host}')
for host in $HOSTS; do
    echo "🌐 Test DNS pour $host:"
    nslookup $host || echo "❌ Résolution DNS échouée"
    echo ""
done

# 7. Test de connectivité
echo "7️⃣ Test de connectivité"
for host in $HOSTS; do
    echo "🧪 Test HTTPS pour $host:"
    curl -I -k https://$host --connect-timeout 10 || echo "❌ Connexion échouée"
    echo ""
done

echo "✅ Diagnostic terminé"

📈 Monitoring et alerting

📊 ServiceMonitor pour Prometheus


apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: caddy-ingress-monitor
  namespace: monitoring
  labels:
    app: caddy-ingress-controller
spec:
  selector:
    matchLabels:
      app: caddy-ingress-controller
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
    scheme: http

🚨 Règles d'alerte Prometheus


groups:
- name: caddy-ingress
  rules:
  - alert: CaddyIngressDown
    expr: up{job="caddy-ingress-controller"} == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Caddy Ingress Controller is down"
      description: "Caddy Ingress Controller has been down for more than 1 minute."

  - alert: CaddyIngressHighErrorRate
    expr: rate(caddy_http_requests_total{status=~"5.."}[5m]) > 0.1
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "High error rate on Caddy Ingress"
      description: "Error rate is {{ $value | humanizePercentage }} for {{ $labels.host }}"

  - alert: CaddyIngressHighLatency
    expr: histogram_quantile(0.95, rate(caddy_http_request_duration_seconds_bucket[5m])) > 1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High latency on Caddy Ingress"
      description: "95th percentile latency is {{ $value }}s for {{ $labels.host }}"

  - alert: CaddyIngressCertificateExpiringSoon
    expr: caddy_tls_certificate_expiry_timestamp - time() < 7 * 24 * 3600
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "TLS certificate expiring soon"
      description: "Certificate for {{ $labels.host }} expires in less than 7 days"

📚 FAQ avancée

❓ Questions techniques approfondies

  • Comment gérer plusieurs Ingress Controllers ?
    Utilisez la classe d'Ingress : spec.ingressClassName: "caddy" dans votre manifest.

  • Peut-on faire du A/B testing avec l'Ingress ?
    Oui, en utilisant les annotations weight pour distribuer le trafic entre services.

  • Comment implémenter une API Gateway complète ?
    Combinez Ingress avec des middlewares Caddy : auth, rate limiting, transformation de requêtes.

  • Gestion des certificats wildcard ?
    Créez un Secret TLS pour *.mondomaine.com et référencez-le dans spec.tls.

  • Intégration avec un WAF externe ?
    Configurez le WAF pour pointer vers l'IP du LoadBalancer, puis Caddy route vers les services internes.

  • Comment debug les problèmes de performance ?
    Activez les métriques Caddy, utilisez kubectl top et analysez les logs avec timestamps.

  • Ingress avec IPv6 ?
    Caddy supporte IPv6 nativement, assurez-vous que votre cluster et LoadBalancer le supportent.

  • Gestion des sessions sticky ?
    Utilisez caddy.ingress.kubernetes.io/session-affinity: "cookie" pour la persistance de session.

🎯 Cas d'usage par industrie

🏦 Fintech / Banking

  • Exigences : Sécurité maximale, conformité PCI-DSS, audit trails
  • Configuration : TLS 1.3 obligatoire, authentification mutuelle, rate limiting strict
  • Annotations clés : tls-min-version, client-cert-auth, audit-log

🏥 Healthcare / HIPAA

  • Exigences : Chiffrement bout-en-bout, logging d'accès, isolation des données
  • Configuration : Headers de sécurité, restriction IP, authentification forte
  • Annotations clés : security-headers, whitelist-source-range, hsts

🎮 Gaming / Streaming

  • Exigences : Latence minimale, support WebSocket, haute disponibilité
  • Configuration : Optimisations réseau, load balancing avancé, cache agressif
  • Annotations clés : websocket, upstream-keepalive, cache-duration

🛒 E-commerce

  • Exigences : Pics de trafic, multi-régions, personnalisation
  • Configuration : Auto-scaling, CDN integration, A/B testing
  • Annotations clés : rate-limit, weight, geo-ip

🚀 Roadmap et évolutions

🔮 Fonctionnalités à venir

  • HTTP/3 (QUIC) : Support natif prévu pour réduire la latence
  • Edge computing : Intégration avec les CDN pour la distribution globale
  • AI/ML integration : Détection automatique d'anomalies et auto-healing
  • Service Mesh : Intégration native avec Istio et Linkerd
  • Policy as Code : Configuration déclarative des règles de sécurité

📊 Métriques de succès

  • Performance : Latence P95 < 100ms, throughput > 10k RPS
  • Fiabilité : Uptime > 99.99%, MTTR < 5 minutes
  • Sécurité : 0 incident critique, audit trail complet
  • Opérabilité : Temps de déploiement < 2 minutes, auto-healing
Résumé complet :
Cette section vous a fourni tout le nécessaire pour maîtriser les Ingress Caddy :
• 15+ configurations prêtes à l'emploi
• Scripts d'automatisation et de diagnostic
• Guide de troubleshooting exhaustif
• Monitoring et alerting intégrés
• Cas d'usage réels par industrie
• FAQ technique approfondie

Vous disposez maintenant d'une expertise complète pour exposer vos applications Kubernetes de manière sécurisée, performante et scalable !
💡 Prochaines étapes recommandées :
1. Testez la configuration de base avec votre domaine
2. Implémentez le monitoring avec Prometheus
3. Automatisez les déploiements avec les scripts fournis
4. Configurez les alertes selon vos SLA
5. Planifiez les évolutions selon votre roadmap produit

🔧 Configuration Avancée Caddy Ingress

Cette section couvre les aspects experts : middlewares personnalisés, certificats custom, et annotations avancées pour des cas d'usage professionnels complexes.

💡 Prérequis : Maîtrise des concepts Ingress de base et accès admin au cluster.

🛠️ Middlewares Caddy Custom

🔐 Authentification JWT avancée


apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-jwt-middleware
data:
  Caddyfile: |
    (jwt_auth) {
      jwt {
        primary_key "your-secret-key"
        algorithms RS256 HS256
        validate_claims {
          aud "api.mondomaine.com"
          iss "https://auth.mondomaine.com"
        }
        validate_exp true
        validate_nbf true
        header "Authorization" "Bearer"
      }
    }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jwt-protected-api
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/middleware-config: "caddy-jwt-middleware"
    caddy.ingress.kubernetes.io/middleware-apply: "jwt_auth"
spec:
  rules:
  - host: secure-api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: protected-api-service
            port:
              number: 80

🚦 Rate Limiting granulaire


apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-rate-limit-middleware
data:
  Caddyfile: |
    (advanced_rate_limit) {
      rate_limit {
        zone api_zone {
          key {remote_host}
          events 100
          window 1m
        }
        zone premium_zone {
          key {header.X-API-Key}
          events 1000
          window 1m
        }
        zone upload_zone {
          key {remote_host}
          events 5
          window 1h
          paths /upload/*
        }
      }
    }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rate-limited-api
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/middleware-config: "caddy-rate-limit-middleware"
    caddy.ingress.kubernetes.io/middleware-apply: "advanced_rate_limit"
spec:
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

🌐 CORS sophistiqué avec conditions


apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-cors-middleware
data:
  Caddyfile: |
    (smart_cors) {
      @api path /api/*
      @admin path /admin/*
      @public path /public/*
      
      header @api {
        Access-Control-Allow-Origin "https://app.mondomaine.com"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
        Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
        Access-Control-Max-Age "86400"
        Access-Control-Allow-Credentials "true"
      }
      
      header @admin {
        Access-Control-Allow-Origin "https://admin.mondomaine.com"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH"
        Access-Control-Allow-Headers "Content-Type, Authorization"
        Access-Control-Max-Age "3600"
      }
      
      header @public {
        Access-Control-Allow-Origin "*"
        Access-Control-Allow-Methods "GET, POST"
        Access-Control-Allow-Headers "Content-Type"
      }
    }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cors-api
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/middleware-config: "caddy-cors-middleware"
    caddy.ingress.kubernetes.io/middleware-apply: "smart_cors"

🔒 Certificats TLS Custom

📜 Certificat d'entreprise avec CA custom


# Créer le secret TLS avec certificat custom
kubectl create secret tls enterprise-tls-cert \
  --cert=enterprise.crt \
  --key=enterprise.key \
  --ca-cert=ca.crt

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: enterprise-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Utilise le certificat custom
    caddy.ingress.kubernetes.io/tls-secret: "enterprise-tls-cert"
    # Configuration TLS stricte
    caddy.ingress.kubernetes.io/tls-protocols: "tls1.2 tls1.3"
    caddy.ingress.kubernetes.io/tls-ciphers: "ECDHE-RSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256"
    # Client certificate pour mTLS
    caddy.ingress.kubernetes.io/client-cert-auth: "enterprise-ca-cert"
spec:
  tls:
  - hosts:
    - enterprise.mondomaine.com
    secretName: enterprise-tls-cert
  rules:
  - host: enterprise.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: enterprise-service
            port:
              number: 443

🌟 Certificat wildcard avec DNS challenge


apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-dns-token
type: Opaque
stringData:
  token: "your-cloudflare-api-token"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wildcard-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    # Configuration DNS challenge
    caddy.ingress.kubernetes.io/tls-dns-provider: "cloudflare"
    caddy.ingress.kubernetes.io/tls-dns-credentials: "cloudflare-dns-token"
    # Certificat wildcard
    caddy.ingress.kubernetes.io/tls-wildcard: "*.mondomaine.com"
spec:
  tls:
  - hosts:
    - "*.mondomaine.com"
    secretName: wildcard-tls-cert
  rules:
  - host: api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
  - host: admin.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 80

🎯 Annotations Avancées

Annotation Fonction Expert Exemple
caddy.ingress.kubernetes.io/request-modifier Modifie les headers de requête "add X-Forwarded-Proto https"
caddy.ingress.kubernetes.io/response-modifier Modifie les headers de réponse "del Server; add X-API-Version v2.1"
caddy.ingress.kubernetes.io/circuit-breaker Circuit breaker avec seuils "failures=5 timeout=30s recovery=60s"
caddy.ingress.kubernetes.io/retry-policy Politique de retry avancée "attempts=3 backoff=exponential codes=502,503,504"
caddy.ingress.kubernetes.io/canary-weight Déploiement canary intelligent "10" (10% du trafic)
caddy.ingress.kubernetes.io/geo-blocking Blocage géographique "block CN,RU allow US,FR,DE"
caddy.ingress.kubernetes.io/waf-rules Règles WAF intégrées "modsecurity-core-rules"

🔥 Configuration Experte Multi-Middleware


apiVersion: v1
kind: ConfigMap
metadata:
  name: production-middleware-stack
data:
  Caddyfile: |
    (production_stack) {
      # 1. Géo-blocking et sécurité
      @blocked_countries {
        header_regexp User-Agent "(?i)(bot|crawler|spider)"
        remote_ip 1.2.3.0/24 5.6.7.0/24
      }
      respond @blocked_countries "Access Denied" 403
      
      # 2. Rate limiting différentiel
      @api_calls path /api/*
      @heavy_endpoints path_regexp ^/api/(upload|export|report)
      
      rate_limit @api_calls {
        key {remote_host}
        events 100
        window 1m
      }
      
      rate_limit @heavy_endpoints {
        key {header.Authorization}
        events 5
        window 5m
      }
      
      # 3. Authentification conditionnelle
      @protected_paths path /admin/* /internal/* /metrics
      @public_paths path /health /version /docs
      
      jwt @protected_paths {
        primary_key "{env.JWT_SECRET}"
        validate_claims {
          roles "admin,operator"
        }
      }
      
      # 4. Headers de sécurité
      header {
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
      }
      
      # 5. Compression intelligente
      encode {
        gzip 6
        zstd
        match {
          header Content-Type application/json*
          header Content-Type application/javascript*
          header Content-Type text/*
        }
      }
      
      # 6. Cache différentiel
      @cacheable {
        path /static/* /images/* /css/* /js/*
        method GET HEAD
      }
      
      header @cacheable {
        Cache-Control "public, max-age=31536000, immutable"
        ETag "{file_hash}"
      }
      
      # 7. Logging structuré
      log {
        output file /var/log/caddy/access.log {
          roll_size 100mb
          roll_keep 5
        }
        format json {
          time_format "2006-01-02T15:04:05.000Z"
          message_key "msg"
        }
        level INFO
      }
    }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production-grade-ingress
  annotations:
    caddy.ingress.kubernetes.io/reverse-proxy: "true"
    caddy.ingress.kubernetes.io/middleware-config: "production-middleware-stack"
    caddy.ingress.kubernetes.io/middleware-apply: "production_stack"
    # Métriques avancées
    caddy.ingress.kubernetes.io/metrics-labels: "app=production-api,env=prod"
    # Circuit breaker
    caddy.ingress.kubernetes.io/circuit-breaker: "failures=10 timeout=60s recovery=120s"
spec:
  rules:
  - host: prod-api.mondomaine.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: production-api-service
            port:
              number: 80

🧪 Tests et Validation


# Test JWT
curl -H "Authorization: Bearer $JWT_TOKEN" https://secure-api.mondomaine.com/protected

# Test rate limiting
for i in {1..150}; do curl https://api.mondomaine.com/api/test; done

# Test géo-blocking
curl -H "CF-IPCountry: CN" https://api.mondomaine.com

# Test circuit breaker
kubectl scale deployment backend-service --replicas=0
curl https://api.mondomaine.com  # Should trigger circuit breaker after failures

# Validation certificat mTLS
curl --cert client.crt --key client.key https://enterprise.mondomaine.com
Niveau Expert Atteint :
• Middlewares personnalisés multi-couches
• Gestion certificats enterprise-grade
• Annotations avancées pour production
• Stack complète prête pour le scaling
⚠️ Production Checklist :
• Testez tous les middlewares en staging
• Configurez les alertes sur les métriques custom
• Documentez vos règles de sécurité
• Planifiez la rotation des certificats

🛠️ Troubleshooting & Monitoring

Guide de survie pour diagnostiquer et monitorer vos Ingress Caddy en production.

🔍 Diagnostic Rapide - Symptômes → Solutions

🚨 Symptôme 🔍 Diagnostic ⚡ Solution Express
502 Bad Gateway kubectl get pods -l app=monapp
kubectl logs deployment/monapp
• Pod crash/restart
• Port backend incorrect
• Health check fail
404 Not Found kubectl get svc
kubectl describe ingress
• Service name incorrect
• Path matching wrong
• Namespace différent
SSL Certificate Error kubectl describe certificate
dig mondomaine.com
• DNS ne pointe pas
• Ports 80/443 bloqués
• Challenge Let's Encrypt fail
Timeout / Slow Response kubectl top pods
Check métriques Caddy
• Backend overload
• Timeout trop court
• Resources insuffisantes
Rate Limit Hit Check logs Caddy
Métriques rate limit
• Ajuster limites
• Whitelist IPs
• Implémenter retry logic

🔧 Commandes de Diagnostic Essentielles


# État général
kubectl get ingress -A -o wide
kubectl describe ingress mon-ingress

# Backend sanity check
kubectl get endpoints mon-service
kubectl port-forward svc/mon-service 8080:80

# Logs Caddy
kubectl logs -n caddy-system deployment/caddy-ingress-controller -f
kubectl logs -n caddy-system deployment/caddy-ingress-controller --previous

# Test connectivité
curl -v -k https://mondomaine.com
curl -I https://mondomaine.com/health

# DNS & Cert check
dig mondomaine.com
openssl s_client -connect mondomaine.com:443 -servername mondomaine.com

📊 Setup Monitoring Prometheus

📈 ServiceMonitor Caddy


apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: caddy-ingress-metrics
  namespace: monitoring
  labels:
    app: caddy-ingress-controller
spec:
  selector:
    matchLabels:
      app: caddy-ingress-controller
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics
    honorLabels: true
  namespaceSelector:
    matchNames:
    - caddy-system

🚨 Règles d'Alerte Critiques


groups:
- name: caddy-ingress-critical
  rules:
  # Controller Down
  - alert: CaddyIngressDown
    expr: up{job="caddy-ingress-controller"} == 0
    for: 30s
    labels:
      severity: critical
    annotations:
      summary: "Caddy Ingress Controller DOWN"
      description: "Controller indisponible depuis {{ $value }}s"

  # High Error Rate
  - alert: CaddyHighErrorRate
    expr: |
      (
        rate(caddy_http_requests_total{status=~"5.."}[5m]) / 
        rate(caddy_http_requests_total[5m])
      ) * 100 > 5
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "Taux d'erreur élevé: {{ $value }}%"
      description: "{{ $labels.host }} - Erreurs 5xx > 5% depuis 2min"

  # Certificate Expiry
  - alert: CaddyCertExpiringSoon
    expr: (caddy_tls_cert_expiry - time()) / 86400 < 7
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "Certificat expire dans {{ $value }} jours"
      description: "{{ $labels.host }} - Renouveler avant expiration"

  # High Latency P95
  - alert: CaddyHighLatency
    expr: |
      histogram_quantile(0.95, 
        rate(caddy_http_request_duration_seconds_bucket[5m])
      ) > 1
    for: 3m
    labels:
      severity: warning
    annotations:
      summary: "Latence P95 élevée: {{ $value }}s"
      description: "{{ $labels.host }} - Performance dégradée"

📋 Dashboard Grafana - Métriques Clés

🎯 Panels Essentiels


# Request Rate
rate(caddy_http_requests_total[5m])

# Error Rate %
rate(caddy_http_requests_total{status=~"5.."}[5m]) / rate(caddy_http_requests_total[5m]) * 100

# Latency P50/P95/P99
histogram_quantile(0.50, rate(caddy_http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.95, rate(caddy_http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(caddy_http_request_duration_seconds_bucket[5m]))

# Active Connections
caddy_http_connections_active

# Certificate Days Until Expiry
(caddy_tls_cert_expiry - time()) / 86400

# Backend Health
up{job="caddy-ingress-controller"}

🔧 Scripts d'Auto-Diagnostic

🚀 Script de Health Check Complet


#!/bin/bash
# health-check.sh - Diagnostic rapide Caddy Ingress

NAMESPACE=${1:-caddy-system}
INGRESS_NAME=${2:-}

echo "🔍 DIAGNOSTIC CADDY INGRESS"
echo "=========================="

# 1. Controller Status
echo "1️⃣ Controller Status"
kubectl get pods -n $NAMESPACE -l app=caddy-ingress-controller
echo ""

# 2. Ingress Resources
echo "2️⃣ Ingress Resources" 
if [[ -n "$INGRESS_NAME" ]]; then
    kubectl get ingress $INGRESS_NAME -o wide
    kubectl describe ingress $INGRESS_NAME | grep -A 10 "Events:"
else
    kubectl get ingress -A
fi
echo ""

# 3. Recent Errors
echo "3️⃣ Recent Errors (last 5min)"
kubectl logs -n $NAMESPACE deployment/caddy-ingress-controller --since=5m | grep -i error | tail -10
echo ""

# 4. Certificate Status
echo "4️⃣ Certificate Status"
kubectl get certificates -A | grep -v "True.*True"
echo ""

# 5. Quick Connectivity Test
echo "5️⃣ Connectivity Tests"
if [[ -n "$INGRESS_NAME" ]]; then
    HOSTS=$(kubectl get ingress $INGRESS_NAME -o jsonpath='{.spec.rules[*].host}')
    for host in $HOSTS; do
        echo "Testing $host..."
        curl -s -o /dev/null -w "Status: %{http_code} | Time: %{time_total}s\n" https://$host/health || echo "❌ Failed"
    done
fi

echo "✅ Diagnostic terminé"

📊 Script Métriques Export


#!/bin/bash
# metrics-export.sh - Export métriques pour analyse

CADDY_METRICS_URL="http://caddy-ingress-controller.caddy-system:2019/metrics"

echo "📊 EXPORT MÉTRIQUES CADDY"
echo "========================"

# Tunnel vers les métriques
kubectl port-forward -n caddy-system svc/caddy-ingress-controller 2019:2019 &
PF_PID=$!
sleep 2

# Export métriques clés
echo "🔍 Request Rate (RPS):"
curl -s localhost:2019/metrics | grep caddy_http_requests_total | tail -5

echo "🔍 Error Rate:"
curl -s localhost:2019/metrics | grep 'status="5' | wc -l

echo "🔍 Active Connections:"
curl -s localhost:2019/metrics | grep caddy_http_connections_active

echo "🔍 Certificate Expiry:"
curl -s localhost:2019/metrics | grep caddy_tls_cert_expiry

# Cleanup
kill $PF_PID 2>/dev/null
echo "✅ Export terminé"

🚨 Alerting Avancé

📱 Slack/Teams Integration


apiVersion: v1
kind: Secret
metadata:
  name: alertmanager-slack
  namespace: monitoring
stringData:
  slack_webhook: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
---
# alertmanager.yml
route:
  group_by: ['alertname', 'severity']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'slack-caddy'

receivers:
- name: 'slack-caddy'
  slack_configs:
  - api_url_file: '/etc/alertmanager/secrets/slack_webhook'
    channel: '#ops-alerts'
    title: 'Caddy Ingress Alert'
    text: |
      🚨 {{ range .Alerts }}
      *Alert:* {{ .Annotations.summary }}
      *Host:* {{ .Labels.host }}
      *Severity:* {{ .Labels.severity }}
      {{ end }}

📈 SLI/SLO Monitoring

🎯 Objectifs de Performance


# SLI/SLO Config
slis:
  - name: api_availability
    query: |
      (
        rate(caddy_http_requests_total{status!~"5.."}[5m]) /
        rate(caddy_http_requests_total[5m])
      ) * 100
    slo: 99.9  # 99.9% uptime

  - name: api_latency_p95
    query: |
      histogram_quantile(0.95,
        rate(caddy_http_request_duration_seconds_bucket[5m])
      )
    slo: 0.5  # < 500ms P95

  - name: error_budget
    query: |
      100 - (
        rate(caddy_http_requests_total{status=~"5.."}[30d]) /
        rate(caddy_http_requests_total[30d]) * 100
      )
    slo: 99.5  # Error budget

🔍 Log Analysis Patterns

📝 Logs Caddy - Patterns Utiles


# Top erreurs 5xx
kubectl logs -n caddy-system deployment/caddy-ingress-controller | \
grep '"status":5' | jq -r .request.uri | sort | uniq -c | sort -nr | head -10

# Latences élevées (>1s)
kubectl logs -n caddy-system deployment/caddy-ingress-controller | \
jq 'select(.duration > 1) | {host: .request.host, uri: .request.uri, duration}'

# IPs les plus actives
kubectl logs -n caddy-system deployment/caddy-ingress-controller | \
jq -r .request.remote_addr | sort | uniq -c | sort -nr | head -20

# Rate limiting hits
kubectl logs -n caddy-system deployment/caddy-ingress-controller | \
grep "rate limit" | tail -10
Troubleshooting Kit Complet :
• Diagnostic rapide par symptômes
• Monitoring Prometheus ready
• Scripts d'auto-diagnostic
• Alerting multi-canal
• SLI/SLO tracking
• Log analysis patterns
⚠️ Production Checklist :
• Testez les alertes avant la mise en prod
• Configurez la rétention des métriques
• Documentez les runbooks d'incident
• Formez l'équipe sur les scripts de diagnostic

🚀 Production Ready Checklist

Patterns environnements, sécurité et best practices pour un déploiement sans stress

🌍 Patterns Environnements

💻 Development

DEV
✅ CORS permissif (*)
✅ Debug logs activés
✅ Hot reload
❌ Rate limiting désactivé
❌ HTTPS optionnel
annotations:
  caddy.ingress.kubernetes.io/cors: "true"
  caddy.ingress.kubernetes.io/cors-allowed-origins: "*"
  caddy.ingress.kubernetes.io/debug-logs: "true"
spec:
  rules:
  - host: dev.monapp.com

🧪 Staging

STAGING
✅ Auth basique
✅ CORS restreint
✅ Rate limiting light
✅ HTTPS forcé
⚠️ Certificat staging
annotations:
  caddy.ingress.kubernetes.io/auth-basic: "Staging Access"
  caddy.ingress.kubernetes.io/auth-basic-secret: "staging-auth"
  caddy.ingress.kubernetes.io/rate-limit: "100r/m"
  caddy.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  rules:
  - host: staging.monapp.com

🏭 Production

PROD
✅ Sécurité maximale
✅ Monitoring complet
✅ Rate limiting strict
✅ Headers sécurité
✅ Let's Encrypt
annotations:
  caddy.ingress.kubernetes.io/security-headers: "true"
  caddy.ingress.kubernetes.io/rate-limit: "1000r/m"
  caddy.ingress.kubernetes.io/hsts: "true"
  caddy.ingress.kubernetes.io/metrics: "true"
spec:
  rules:
  - host: api.monapp.com

🔒 Security Scorecard

🛡️ Transport Security

🚦 Access Control

🛡️ Headers & CORS

📊 Monitoring & Logs

85%
Security Score
✅ 11 checks passed
⚠️ 5 recommendations
💡 Ready for production

⏱️ Deployment Timeline

1

Infrastructure Setup

Cluster K8s + Caddy Ingress Controller

⏱️ 30min
2

DNS Configuration

Pointer domaines vers LoadBalancer IP

⏱️ 15min + propagation
3

Ingress Deployment

YAML configs + certificats Let's Encrypt

⏱️ 5min + cert generation
4

Security Hardening

Headers, rate limiting, auth, monitoring

⏱️ 20min
5

Production Validation

Tests charge, alertes, runbooks

⏱️ 1h

⭐ Best Practices

Performance

  • Compression gzip/zstd activée
  • Cache headers appropriés
  • Keep-alive connections
  • Backend timeouts optimisés
🛡️

Fiabilité

  • Health checks backend
  • Circuit breaker patterns
  • Retry policies intelligentes
  • Graceful degradation
🔒

Sécurité

  • Principe du moindre privilège
  • Défense en profondeur
  • Rotation certificats auto
  • Audit et compliance
🔧

Opérations

  • GitOps pour la config
  • Scripts de diagnostic
  • Runbooks incidents
  • Monitoring proactif

🎯 Quick Wins

1

Monitoring instantané

Déployez ServiceMonitor + alertes de base → visibilité immédiate

⏱️ 10 minutes
2

Security headers

Ajoutez security-headers: "true" → protection instantanée

⏱️ 2 minutes
3

Rate limiting

Protection DDoS basique avec rate-limit: "1000r/m"

⏱️ 1 minute
4

Scripts diagnostic

health-check.sh pour troubleshooting rapide

⏱️ 5 minutes

🎉 Félicitations !

Vous maîtrisez maintenant les Ingress Caddy de A à Z

5
Sections maîtrisées
50+
Configs prêtes
100%
Production ready

🚀 Prochaines étapes :

Testez en staging → Monitoring → Sécurité → Production ! 🎯