Pourquoi OpenTofu plutôt que de continuer en SSH manuel
Jusqu'à cinq ou six clients, la gestion manuelle tient : vous vous souvenez de ce qui tourne où, et la checklist de mise en service est connue par cœur. Au-delà, la mémoire devient un risque opérationnel. Un VPS provisionné à la main n'a pas d'état lisible : vous ne savez pas, sans vous connecter, si le pare-feu est configuré, si Docker a bien la version attendue, ou si ce serveur fait encore partie de la rotation.
OpenTofu résout un problème différent d'Ansible. Ansible gère la *configuration* d'un serveur qui existe déjà — ce qui tourne dessus, les fichiers présents, les services démarrés. OpenTofu gère le *cycle de vie* du serveur lui-même : création, mise à jour des attributs, destruction. Il tient un fichier d'état (terraform.tfstate) qui sait exactement ce qui est provisionné et ce qui ne l'est plus. Les deux outils sont complémentaires : OpenTofu provisionne, Ansible configure.
En août 2023, HashiCorp a changé la licence de Terraform de MPL-2.0 vers BUSL-1.1, une licence non libre qui interdit certains usages commerciaux. La communauté a répondu en créant OpenTofu, un fork compatible HCL pour les versions antérieures à Terraform 1.6, aujourd'hui hébergé sous la CNCF (Cloud Native Computing Foundation) et la Linux Foundation. La commande est tofu, pas terraform, mais les fichiers .tf et la logique sont identiques.
Ce que vous gagnez avec un état déclaratif
- Inventaire fiable — le fichier
terraform.tfstatedit exactement quels serveurs existent, avec quelles IPs et quels attributs, sans aller se connecter un par un. - Reproductibilité — un nouveau client ou un nouveau projet reçoit le même VPS configuré de la même façon, depuis le même fichier
.tfversionné dans Git. - Destruction propre —
tofu destroyretire les ressources dans le bon ordre, sans laisser de serveurs orphelins qui continuent à être facturés. - Diff lisible —
tofu planaffiche exactement ce qui sera créé, modifié ou détruit avant d'agir, comme ungit diffde votre infrastructure. - Complémentarité Ansible — OpenTofu crée le VPS et pose les métadonnées (clé SSH, nom, réseau) ; Ansible prend la main pour déployer Docker, Nginx et vos applications.
- Versionnable et auditable — votre infrastructure devient un dépôt Git avec des commits, des revues de code et un historique des changements.
- Licence open source pérenne — OpenTofu est sous MPL-2.0, sans restriction commerciale, avec une gouvernance communautaire sous la CNCF.
Prérequis avant de commencer
Ce guide suppose que vous disposez d'un VPS avec accès root et IPv4 dédiée pour faire tourner vos workloads, et d'un poste de développement (macOS, Linux ou Windows avec WSL2) depuis lequel vous lancez tofu. OpenTofu lui-même ne tourne pas sur le VPS cible : il s'exécute localement et parle à l'API du fournisseur ou au daemon Docker du serveur.
Pour suivre les exemples, vous avez besoin de : OpenTofu installé en local (voir opentofu.org/docs/intro/install), d'un accès API ou de credentials SSH vers le VPS cible, et de Git pour versionner vos fichiers .tf. Aucune dépendance supplémentaire n'est requise — OpenTofu télécharge lui-même les providers au tofu init.
Pour piloter Docker sur un VPS distant via OpenTofu, le daemon Docker du serveur doit écouter sur son socket Unix (par défaut) ou sur un port TCP sécurisé. Le provider kreuzwerker/docker se connecte à ce socket via SSH ou TCP.
Mettre en place OpenTofu sur une flotte de VPS
Installer OpenTofu en local
Rendez-vous sur opentofu.org/docs/intro/install pour les instructions selon votre OS. Sur macOS avec Homebrew : brew install opentofu. Sur Debian/Ubuntu : le dépôt officiel OpenTofu fournit le paquet opentofu. Vérifiez l'installation avec tofu version — la commande doit retourner la version installée.
Structurer votre projet Infrastructure as Code
Créez un répertoire dédié et quatre fichiers standards.
# main.tf — ressources principales
# variables.tf — déclarations de variables
# outputs.tf — valeurs exposées après apply
# terraform.tfvars — valeurs concrètes (gitignorer si secrets)Cette structure n'est pas obligatoire pour OpenTofu, mais elle est la convention largement adoptée : main.tf porte les ressources, variables.tf déclare les types et valeurs par défaut, outputs.tf expose ce qu'Ansible ou un autre outil doit lire après le provisionnement (IP du serveur, nom d'hôte…), et terraform.tfvars contient les valeurs concrètes que vous ne souhaitez pas coder en dur dans main.tf.
Déclarer le provider et générer une clé SSH
OpenTofu installe les providers nécessaires au tofu init. Le provider hashicorp/tls génère une paire de clés SSH en local, ce qui évite de gérer des clés manuellement.
terraform {
required_providers {
tls = {
source = "hashicorp/tls"
version = "~> 4.0"
}
}
}
resource "tls_private_key" "vps_key" {
algorithm = "ED25519"
}
output "private_key_pem" {
value = tls_private_key.vps_key.private_key_pem
sensitive = true
}Lancez tofu init pour télécharger le provider, puis tofu apply pour générer la clé. Récupérez-la avec tofu output -raw private_key_pem > ~/.ssh/vps_key && chmod 600 ~/.ssh/vps_key.
Provisionner le VPS avec null et local-exec
Si votre fournisseur VPS n'a pas de provider OpenTofu officiel, le provider hashicorp/null avec un local-exec permet d'exécuter une commande locale (un appel API curl, un script shell) et de modéliser la ressource dans le state.
resource "null_resource" "vps_provision" {
triggers = {
server_name = var.server_name
}
provisioner "local-exec" {
command = <<EOT
curl -s -X POST https://api.votre-fournisseur.com/v1/servers \
-H "Authorization: Bearer ${var.api_token}" \
-d '{"name": "${var.server_name}", "image": "ubuntu-22.04"}'
EOT
}
}Ce pattern est adapté à une phase transitoire. Si votre fournisseur expose une API REST, un script shell appelé en local-exec suffit pour créer le serveur et écrire son IP dans un fichier que outputs.tf exposera ensuite.
Piloter Docker sur le VPS avec le provider kreuzwerker
Une fois le VPS provisionné et Docker installé (par Ansible, typiquement), le provider kreuzwerker/docker vous permet de déclarer des conteneurs, des réseaux et des volumes dans OpenTofu.
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
provider "docker" {
host = "ssh://root@${var.server_ip}:22"
}
resource "docker_container" "app" {
name = "mon-app"
image = docker_image.app.image_id
}
resource "docker_image" "app" {
name = "nginx:alpine"
}Le provider se connecte au daemon Docker du VPS via SSH — aucun port TCP supplémentaire à ouvrir. La version stable du provider est disponible sur le Terraform Registry.
Versionner le state pour une équipe
En développement solo, le state local (terraform.tfstate) suffit. En équipe ou en CI/CD, deux développeurs qui lancent tofu apply simultanément corrompent le state. La solution est un backend distant avec verrouillage.
OpenTofu supporte nativement S3 (AWS, MinIO auto-hébergé) et le GitLab Managed Terraform State. Avec un bucket MinIO sur un VPS :
terraform {
backend "s3" {
bucket = "tofu-state"
key = "production/terraform.tfstate"
region = "eu-west-1"
endpoint = "https://minio.votre-domaine.com"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
force_path_style = true
}
}Le verrouillage est automatique : si un tofu apply est en cours, un second est refusé jusqu'à la fin du premier.
Intégrer OpenTofu dans votre pipeline CI/CD
Un pipeline Woodpecker CI ou Forgejo Actions typique exécute tofu plan sur chaque pull request (pour revue humaine du diff d'infrastructure) et tofu apply sur merge dans la branche principale.
steps:
- name: tofu-plan
image: ghcr.io/opentofu/opentofu:latest
commands:
- tofu init
- tofu plan -out=tfplan
- name: tofu-apply
image: ghcr.io/opentofu/opentofu:latest
commands:
- tofu apply tfplan
when:
branch: main
event: pushLe state distant (étape précédente) est indispensable ici : le runner CI n'a pas accès au state local de votre poste.
Séparez les workspaces par environnement
OpenTofu propose les workspaces pour isoler plusieurs états dans le même backend : tofu workspace new staging crée un espace isolé, tofu workspace select production bascule vers la production. C'est plus léger que de dupliquer les répertoires .tf. En pratique, un workspace par client ou par environnement (staging, prod) évite qu'un tofu destroy en staging touche la production. Nommez vos ressources avec ${terraform.workspace} pour qu'elles restent distinctes dans le state.
Ansible et OpenTofu : la frontière entre les deux
La question revient souvent : Ansible fait déjà le travail, pourquoi ajouter un outil ? La frontière est nette une fois qu'on la pose clairement.
OpenTofu répond à « qu'est-ce qui existe ? » : il crée le serveur, lui assigne une IP, pose une clé SSH, enregistre son état. Si vous le supprimez de votre fichier .tf et lancez tofu apply, le serveur disparaît — OpenTofu tient le cycle de vie.
Ansible répond à « dans quel état est ce qui existe ? » : il installe Docker, configure Nginx, dépose un fichier .env, redémarre un service. Si le serveur est déjà là, Ansible en fait ce que vous lui demandez — mais s'il n'est pas là, Ansible ne peut pas le créer.
Le flux naturel pour une agence : OpenTofu provisionne le VPS et expose son IP en output, un playbook Ansible consomme cet output via un inventaire dynamique, configure le serveur et déploie les applications. L'article Ansible : automatiser la configuration de vos serveurs VPS couvre la partie configuration en détail.
Dépannage : les erreurs fréquentes
Trois situations reviennent régulièrement quand on adopte OpenTofu sur une flotte existante.
Erreurs courantes et leur correction
Error acquiring the state lock— un crash en cours d'applylaisse un fichier.terraform.tfstate.lock.infosur le backend. OpenTofu refuse de continuer tant que le verrou existe. Après avoir vérifié qu'aucun autreapplyne tourne, supprimez le verrou avectofu force-unlock <LOCK_ID>(l'ID apparaît dans le message d'erreur). Sur un backend S3/MinIO, le fichier est visible dans le bucket.tofu planaffichedestroy + createinattendu — certains attributs d'une ressource forcent une recréation (force new resource) quand ils changent : le nom du serveur, le type d'image OS, la région. Si vous modifiez l'un de ces attributs, OpenTofu ne peut pas faire une mise à jour en place — il détruit et recrée. Lisez le plan attentivement avant d'appliquer et utiliseztofu plan -target=resource.namepour limiter la portée.- State perdu ou désynchronisé — si le fichier state est perdu et que les serveurs existent encore,
tofu state listliste ce qu'OpenTofu croit provisionné, ettofu import <resource.type.name> <id-externe>importe une ressource existante dans le state sans la recréer. C'est l'outil de récupération quand la réalité et le state ont divergé. - Provider non trouvé après
tofu init— vérifiez que lesourcedu provider est exact (ex.kreuzwerker/dockeret nondocker/docker) et que vous avez accès à internet depuis la machine qui lancetofu init. Dans un environnement air-gapped, pré-téléchargez les providers et utilisezplugin_cache_dir. Error: No valid credential sources found— OpenTofu ne trouve pas les credentials pour le provider. Vérifiez les variables d'environnement attendues par le provider (souventTF_VAR_api_tokenou un fichier de credentials spécifique) et queterraform.tfvarsest bien lu (il doit être dans le même répertoire quemain.tf).
Votre infrastructure devient du code versionné
OpenTofu ne remplace pas SSH, il rend SSH exceptionnel. Le flux quotidien devient : modifier un fichier .tf, lancer tofu plan pour lire le diff, valider, lancer tofu apply. Les serveurs que vous n'avez plus à gérer sont détruits par tofu destroy et disparaissent de la facturation.
Pour une agence qui dépasse la dizaine de clients, c'est le levier qui fait passer la gestion d'infrastructure d'un travail de mémoire à un travail de code. Un VPS Cloud ServOrbit avec accès root, IPv4 dédiée et choix d'OS est l'unité de base que vos fichiers .tf provisionnent et détruisent à la demande. Votre infrastructure devient du code versionné.