Comment je provisionne le « VPC » complet d’un client — stack PHP, MariaDB master/slave, HAProxy — en quelques minutes avec Terraform et Proxmox, couplé à Ansible pour une config identique entre nœuds. Et le piège qui m’a rattrapé en prod : la migration de LXC entre nœuds.

Le besoin : arrêter de monter les environnements à la main

Chaque nouveau client, c’était le même rituel : créer quatre conteneurs dans l’interface Proxmox, les adresser, les démarrer, puis installer ce qui tourne dedans. Long, et surtout jamais deux fois pareil. Une IP décalée ici, un template différent là, et six mois plus tard personne ne sait comment l’environnement a été monté — moi compris.

Mon cluster : trois nœuds Proxmox. Par client, un socle de quatre LXC :

ConteneurRôle
haproxyrépartition et terminaison TLS
php-1…napplicatif, multiplié selon la charge
mariadb-masterécritures
mariadb-slavelectures et réplication

Quatre, c’est le plancher. Les conteneurs PHP sont ceux qu’on ajoute quand la charge monte — et c’est exactement ce qui justifie HAProxy devant : passer d’un à trois applicatifs ne change rien en amont. C’est aussi là qu’Ansible cesse d’être un confort pour devenir indispensable, parce que trois PHP qui divergent, ce sont trois comportements différents en production et un bug qui ne se reproduit qu’une fois sur trois.

Un sous-réseau par client, isolé des autres : c’est ce qui donne l’effet « VPC ». Rien d’exotique, mais décrit dans un dépôt git plutôt que dans ma mémoire.

Terraform pose les conteneurs, Ansible installe les services

La séparation que j’applique, et à laquelle je tiens :

  • Terraform crée les coquilles — conteneurs LXC, CPU, RAM, disque, réseau. C’est tout.
  • Ansible provisionne les services — PHP et ses extensions, MariaDB et sa réplication, HAProxy et ses backends. Et surtout, il les provisionne à l’identique : le même rôle joué sur le premier et sur le troisième conteneur PHP donne exactement la même machine.

Terraform sait créer un conteneur, pas y installer PHP-FPM avec la bonne version et la bonne config. Ansible sait configurer une machine, pas la faire exister. Vouloir tout faire avec l’un des deux, c’est se compliquer la vie pour rien.

Côté Terraform, les conteneurs applicatifs ressemblent à ça — un count suffit à en ajouter un, et la répartition sur les trois nœuds se fait au passage :

code
resource "proxmox_lxc" "php" {
  count = var.php_count

  target_node  = "pve-0${(count.index % 3) + 1}"
  hostname     = "${var.client}-php-${count.index + 1}"
  ostemplate   = "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
  unprivileged = true

  cores  = 4
  memory = 4096

  rootfs {
    storage = "local-lvm"
    size    = "20G"
  }

  network {
    name   = "eth0"
    bridge = "vmbr0"
    ip     = "10.0.${var.client_id}.1${count.index + 1}/24"
    gw     = "10.0.${var.client_id}.1"
  }
}

Passer un client de un à trois applicatifs, c’est donc changer un chiffre. Les autres conteneurs suivent le même moule, avec leurs ressources propres, et Ansible prend le relais derrière avec un rôle par service.

Le résultat : 4 minutes contre 40

C’est le chiffre qui justifie tout le reste. Un environnement client complet — quatre conteneurs créés, adressés, démarrés et configurés — passe de 40 minutes à la main à 4 minutes en une commande.

Le gain de temps est agréable. Mais ce n’est pas le vrai bénéfice.

Le vrai bénéfice arrive six mois plus tard, quand on cherche pourquoi tel conteneur a telle IP, ou quand il faut remonter un environnement identique pour reproduire un bug. La réponse est dans le dépôt, pas dans un souvenir. Décrire son infrastructure vaut surtout pour la relire.

Le piège : migrer un conteneur et perdre Terraform

Voilà ce qui m’a rattrapé en production, et que je n’avais pas vu venir.

Avec le provider Telmate/proxmox, target_node fait partie de l’identité de la ressource. Or déplacer un conteneur d’un nœud vers un autre est une opération banale : on rééquilibre la charge, on vide un nœud pour une maintenance. Proxmox le fait sans broncher — il éteint le conteneur, le déplace, le redémarre, puisqu’un LXC ne se live-migre pas.

Terraform, lui, ne voit rien passer. Au plan suivant, il interroge l’ancien nœud, n’y trouve plus rien, et en conclut que le conteneur a été supprimé. Il planifie donc une recréation. Sauf que le VMID est toujours pris ailleurs sur le cluster : l’apply échoue. L’état Terraform et la réalité ont divergé, et le dépôt ne décrit plus l’infrastructure.

Le piège est d’autant plus sournois que l’opération qui casse tout se fait en deux clics dans l’interface, sans le moindre avertissement. Et plus on ajoute de conteneurs applicatifs répartis sur les trois nœuds, plus on a d’occasions de les déplacer — donc de tomber dedans.

Ma parade : dire à Terraform de cesser de surveiller ce champ.

code
resource "proxmox_lxc" "php" {
  # ...

  lifecycle {
    ignore_changes = [target_node]
  }
}

C’est un compromis assumé. Le fichier ne reflète plus sur quel nœud tourne réellement le conteneur — mais il ne détruit plus rien par surprise, et c’est très largement préférable. L’alternative, resynchroniser l’état à chaque déplacement avec terraform state rm puis terraform import, suppose d’y penser à chaque fois. En pratique, on n’y pense pas.

Quel provider en 2026 ?

J’utilise Telmate/proxmox par habitude — c’était le choix par défaut quand j’ai commencé. Si je devais repartir de zéro aujourd’hui, je prendrais bpg/proxmox.

Le contraste est net :

  • Telmate en est à v3.0.2-rc10 (août 2026). Le provider n’est pas abandonné, mais sa branche 3.x traîne en release candidate depuis des mois — avec un trou de sept mois entre rc07 et rc08 — et son périmètre reste limité aux VMs, LXC, pools et cloud-init.
  • bpg publie à un tout autre rythme : v0.114.0, v0.113.1, v0.112.0 sur le seul mois de septembre 2026. Et il couvre bien plus large : cluster, hôtes, ACL, users, réseau, SDN.

Un mot d’honnêteté, en revanche : changer de provider ne règle pas le piège de la migration. Chez bpg aussi, modifier le nœud force un destroy/recreate, et il n’existe pas pour les conteneurs d’équivalent au flag migrate des VMs. Une pull request intitulée « feat(lxc): migrate container on node change » est ouverte depuis le 16 septembre 2026 — elle n’est pas encore fusionnée. ignore_changes reste donc nécessaire, quel que soit votre choix.

Ce que j’en retiens

  • 4 minutes contre 40, mais le gain de temps est le moindre des bénéfices. Ce qui compte, c’est qu’un environnement client soit lisible dans un dépôt six mois plus tard.
  • Terraform ne surveille que ce qu’il a créé. Toute action faite depuis l’interface Proxmox — migration, redimensionnement — passe sous son radar et finit par produire un plan incohérent. La discipline compte autant que l’outil.
  • Terraform et Ansible ne se remplacent pas. Le premier pose les conteneurs, le second installe ce qui tourne dedans.
  • Choisir son provider est une décision qui dure. Migrer de Telmate vers bpg signifie réécrire ses ressources et reconstruire son état : autant y réfléchir au premier jour du projet plutôt qu’au centième.

Pour un environnement client répété à l’identique, le jeu en vaut largement la chandelle. Pour un serveur unique monté une fois et jamais retouché, écrire le Terraform coûtera plus cher que de cliquer dans l’interface — et ce n’est pas grave.


Dans le même esprit : Déployer sur AWS avec Ansistrano.