Cloud

Infrastructure-as-Code self-hosted

Projet Ansible complet pour provisionner et maintenir un VPS self-hosted (Ubuntu 24.04). 26 rôles partagés, App Platform déclaratif, observabilité complète. Montre Claude Code comme outil d'infrastructure-as-code.

AnsibleDockerTraefikAuthentik SSOPrometheusGrafanaLokiResticWireGuardGitLab CI

cloud est un projet Ansible complet pour provisionner et maintenir un VPS self-hosted, illustrant l'utilisation de Claude Code comme outil d'infrastructure-as-code avec 831 sessions.

Stack Ansible + Docker + Traefik
Architecture 26 rôles partagés + rôles client
Commits 2026 159
Sessions Claude 831
Statut Production

1. Contexte

Self-hosting complet plutôt que services managés. Philosophie "build don't buy". Un VPS unique (tordu-jardin.fr) héberge tout l'écosystème : applications, bases de données, monitoring, backups, SSO.

Pourquoi le self-hosting ?

💰

Coût maîtrisé

Un seul VPS vs. multiples services cloud managés. Réduction drastique des coûts mensuels.

🔒

Souveraineté

Données hébergées sur infrastructure contrôlée. Pas de dépendance à des tiers pour les services critiques.

🧩

Flexibilité

Configuration sur mesure pour chaque service. Pas de limites imposées par les plateformes managées.

📚

Apprentissage

Maîtrise complète de la stack infrastructure. Chaque rôle Ansible est un savoir capitalisé.

2. Architecture 26 rôles

Le projet est structuré en rôles Ansible partagés et en instances client. Le framework multi-instance permet de déployer la même infrastructure pour plusieurs clients.

Structure du projet
cloud/
├── playbook.yml              # 23 rôles + post_tasks
├── Makefile                   # deploy, dry-run, vault-edit, lint
├── instances/
   ├── _template/             # Template nouveau client
   └── tordu-jardin/          # Instance production
       ├── hosts.yml
       ├── group_vars/all/
   ├── vars.yml
   └── vault.yml      # Secrets chiffrés
       └── roles/granit-golem/ # Rôle client spécifique
└── roles/                     # 26 rôles partagés
    ├── base-hardening/
    ├── wireguard/
    ├── firewall/
    ├── docker/
    ├── traefik/
    ├── authentik/
    ├── monitoring/
    ├── backup/
    └── app-platform/

Rôles principaux

base-hardening

SSH hardening, UFW, Fail2ban, AppArmor. Sécurisation du système de base avant tout déploiement.

docker + traefik

Runtime conteneurs et reverse proxy avec TLS automatique via Let's Encrypt. Point d'entrée unique.

authentik

SSO centralisée pour les 12 services. OAuth2/OIDC, SAML. Authentification unique pour tout l'écosystème.

monitoring

Prometheus + Grafana + Loki + Promtail + Tempo + cAdvisor + Node Exporter. Observabilité complète.

backup

Restic daily snapshots + WAL-G pour PostgreSQL PITR. Rétention 7d/4w/6m. Notifications via Ntfy.

app-platform

Provisionnement déclaratif des applications. Un fichier YAML suffit pour déployer un nouveau service.

3. App Platform declaratif

Les projets déclarent un .cloud/app.yml et sont auto-provisionnés. Ce pattern permet d'onboarder un projet en un seul fichier YAML : base de données, monitoring, domaine et stratégie de déploiement sont configurés automatiquement.

.cloud/app.yml — déclaration d'une app
# .cloud/app.yml — déclaration d'une app
name: granit-golem
type: compose
domain: granit-golem.tordu-jardin.fr
database:
  type: postgresql
  version: "17"
monitoring:
  prometheus: true
  alerts: true
deploy:
  strategy: blue-green

4. Observabilite

La stack d'observabilité couvre les métriques, les logs, le tracing et les alertes. Tous les composants sont déployés via Docker Compose et configurés par Ansible.

Métriques

Prometheus Node Exporter cAdvisor

Logs

Loki Promtail

Tracing

Tempo

Visualisation

Grafana

Alerting

Alertmanager Ntfy
roles/monitoring/tasks/main.yml
# roles/monitoring/tasks/main.yml
- name: Deploy Prometheus stack
  docker_compose:
    project_name: monitoring
    definition:
      services:
        prometheus:
          image: prom/prometheus:v2.51.0
          volumes:
            - ./prometheus.yml:/etc/prometheus/prometheus.yml
            - prometheus_data:/prometheus
          command:
            - '--config.file=/etc/prometheus/prometheus.yml'
            - '--storage.tsdb.retention.time=30d'

        grafana:
          image: grafana/grafana:11.0.0
          environment:
            GF_AUTH_GENERIC_OAUTH_ENABLED: "true"
            GF_AUTH_GENERIC_OAUTH_NAME: "Authentik"
          labels:
            traefik.enable: "true"
            traefik.http.routers.grafana.rule: "Host(`grafana.tordu-jardin.fr`)"

        loki:
          image: grafana/loki:3.0.0
          volumes:
            - loki_data:/loki

        promtail:
          image: grafana/promtail:3.0.0
          volumes:
            - /var/log:/var/log:ro
            - /var/lib/docker/containers:/var/lib/docker/containers:ro

5. Sécurité

La sécurité est appliquée en profondeur avec plusieurs couches complémentaires, du réseau jusqu'à l'authentification applicative.

1

Réseau

UFW (deny all incoming) + WireGuard VPN pour l'accès SSH. Aucun port exposé sauf 80/443.

2

Système

Fail2ban (rate limiting) + AppArmor (confinement conteneurs) + SSH key-only.

3

Applicatif

Authentik SSO pour les 12 services. OAuth2/OIDC avec MFA. Traefik middleware d'authentification.

4

Secrets

Ansible Vault pour tous les secrets. Aucun mot de passe en clair dans le dépôt.

Sécurité multi-couches
# roles/base-hardening/tasks/main.yml
- name: SSH hardening
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
  notify: restart sshd

- name: Configure UFW defaults
  ufw:
    state: enabled
    policy: deny
    direction: incoming

- name: Allow SSH via WireGuard only
  ufw:
    rule: allow
    port: "{{ ssh_port }}"
    interface: wg0

- name: Configure Fail2ban
  template:
    src: jail.local.j2
    dest: /etc/fail2ban/jail.local
  notify: restart fail2ban

- name: Enable AppArmor profiles
  command: aa-enforce /etc/apparmor.d/docker-*
  when: apparmor_profiles | length > 0

# roles/authentik/tasks/main.yml
- name: Deploy Authentik SSO
  docker_compose:
    project_name: authentik
    definition:
      services:
        server:
          image: ghcr.io/goauthentik/server:2024.4
          environment:
            AUTHENTIK_SECRET_KEY: "{{ vault_authentik_secret }}"
          labels:
            traefik.enable: "true"
            traefik.http.routers.authentik.rule: "Host(`auth.tordu-jardin.fr`)"

6. Disaster Recovery

La stratégie de reprise après sinistre combine des snapshots réguliers et la réplication des bases de données pour minimiser la perte de données.

Restic Snapshots

  • Backups quotidiens à 3h du matin
  • Rétention : 7 daily, 4 weekly, 6 monthly
  • Cibles : /opt/docker, /etc/traefik, /etc/wireguard
  • Notification Ntfy en cas de succès ou d'échec

WAL-G (PostgreSQL PITR)

  • Point-in-Time Recovery pour toutes les bases
  • WAL archiving continu vers stockage distant
  • Restauration à n'importe quel instant
roles/backup/tasks/main.yml
# roles/backup/tasks/main.yml
- name: Configure Restic backup
  template:
    src: backup.sh.j2
    dest: /opt/backup/backup.sh
    mode: '0700'

- name: Setup backup cron (daily 3am)
  cron:
    name: "restic-backup"
    hour: "3"
    minute: "0"
    job: "/opt/backup/backup.sh >> /var/log/backup.log 2>&1"

# templates/backup.sh.j2
#!/bin/bash
set -euo pipefail

# Restic snapshot
restic backup \
  --tag {{ inventory_hostname }} \
  --exclude-caches \
  /opt/docker /etc/traefik /etc/wireguard

# PostgreSQL WAL-G PITR
for db in {{ postgresql_databases | join(' ') }}; do
  wal-g backup-push --pgdata /var/lib/postgresql/data
done

# Retention: 7 daily, 4 weekly, 6 monthly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

# Notify success
curl -s -d "Backup OK: {{ inventory_hostname }}" \
  {{ ntfy_url }}/backup-{{ inventory_hostname }}

7. Claude Code pour l'IaC

Avec 831 sessions, le projet cloud démontre que Claude Code est particulièrement efficace pour l'infrastructure automation.

Patterns d'utilisation

Génération de rôles Ansible

Claude génère des rôles complets avec tasks, handlers, templates Jinja2, defaults et molecule tests. La structure est respectée systématiquement grâce au CLAUDE.md.

Debugging Docker Compose

Résolution de problèmes de networking, volumes, dépendances entre services. Claude lit les logs et propose des corrections ciblées.

Templates Jinja2

Écriture de templates de configuration complexes avec conditionnels, boucles et filtres. Particulièrement efficace pour les configurations Traefik et Prometheus.

Documentation operationnelle

Runbooks, ADR, Architecture Decision Records, post-mortems générés efficacement avec Claude. La documentation suit un format standardisé.

CI/CD Pipeline

.gitlab-ci.yml
# .gitlab-ci.yml
stages:
  - lint
  - validate
  - deploy

lint:
  stage: lint
  script:
    - ansible-lint playbook.yml
    - yamllint -c .yamllint.yml .

syntax-check:
  stage: validate
  script:
    - ansible-playbook playbook.yml --syntax-check
    - ansible-playbook playbook.yml --check --diff
  variables:
    ANSIBLE_HOST_KEY_CHECKING: "false"

deploy:
  stage: deploy
  script:
    - make deploy INSTANCE=tordu-jardin
  when: manual
  only:
    - main
  environment:
    name: production
    url: https://tordu-jardin.fr

8. Métriques

159 Commits 2026
831 Sessions Claude
26 Rôles Ansible
12+ Apps hébergées
1s RTO mesuré Post-mortem disk full
4 CI checks lint, syntax, yamllint, dry-run
7/4/6 Rétention backup daily/weekly/monthly
12 Services SSO Authentik OAuth2

9. Enseignements

Claude Code excelle en IaC Ansible

Templates Jinja2, handlers, conditions, variables : Claude produit des rôles Ansible de qualité production avec peu de corrections nécessaires.

App Platform = onboarding en 1 fichier YAML

Le pattern déclaratif permet de provisionner un projet complet, base de données, monitoring, domaine, déploiement, avec un seul fichier de configuration.

Multi-instance framework prêt

Le framework est conçu pour supporter plusieurs clients ou instances. Le template _template/ permet de créer une nouvelle instance en quelques minutes.

Documentation générée efficacement

Runbooks, ADR, post-mortems : Claude Code génère la documentation opérationnelle dans un format standardisé, directement intégrée au dépôt.