Por qué OpenTofu en lugar de seguir con SSH manual
Hasta cinco o seis clientes, la gestión manual aguanta: usted recuerda qué se ejecuta dónde y la lista de puesta en servicio se sabe de memoria. Más allá, la memoria se convierte en un riesgo operativo. Un VPS aprovisionado a mano no tiene un estado legible: usted no sabe, sin conectarse, si el firewall está configurado, si Docker tiene la versión esperada o si ese servidor sigue formando parte de la rotación.
OpenTofu resuelve un problema distinto al de Ansible. Ansible gestiona la *configuración* de un servidor que ya existe: lo que se ejecuta en él, los archivos presentes, los servicios arrancados. OpenTofu gestiona el *ciclo de vida* del servidor mismo: creación, actualización de atributos, destrucción. Mantiene un archivo de estado (terraform.tfstate) que sabe exactamente qué está aprovisionado y qué ya no lo está. Ambas herramientas son complementarias: OpenTofu aprovisiona, Ansible configura.
En agosto de 2023, HashiCorp cambió la licencia de Terraform de MPL-2.0 a BUSL-1.1, una licencia no libre que prohíbe ciertos usos comerciales. La comunidad respondió creando OpenTofu, un fork compatible con el HCL de las versiones anteriores a Terraform 1.6, hoy alojado bajo la CNCF (Cloud Native Computing Foundation) y la Linux Foundation. El comando es tofu, no terraform, pero los archivos .tf y la lógica son idénticos.
Lo que gana con un estado declarativo
- Inventario fiable — el archivo
terraform.tfstatedice exactamente qué servidores existen, con qué IP y qué atributos, sin tener que conectarse a cada uno. - Reproducibilidad — un nuevo cliente o un nuevo proyecto recibe el mismo VPS configurado de la misma forma, desde el mismo archivo
.tfversionado en Git. - Destrucción limpia —
tofu destroyretira los recursos en el orden correcto, sin dejar servidores huérfanos que se siguen facturando. - Diff legible —
tofu planmuestra exactamente lo que se creará, modificará o destruirá antes de actuar, como ungit diffde su infraestructura. - Complementariedad con Ansible — OpenTofu crea el VPS y coloca los metadatos (clave SSH, nombre, red); Ansible toma el relevo para desplegar Docker, Nginx y sus aplicaciones.
- Versionable y auditable — su infraestructura se convierte en un repositorio Git con commits, revisiones de código y un historial de cambios.
- Licencia open source duradera — OpenTofu está bajo MPL-2.0, sin restricción comercial, con una gobernanza comunitaria bajo la CNCF.
Requisitos previos antes de empezar
Esta guía supone que dispone de un VPS con acceso root e IPv4 dedicada para ejecutar sus cargas de trabajo, y de un equipo de desarrollo (macOS, Linux o Windows con WSL2) desde el que lanza tofu. OpenTofu no se ejecuta en el VPS de destino: corre localmente y habla con la API del proveedor o con el daemon Docker del servidor.
Para seguir los ejemplos necesita: OpenTofu instalado en local (consulte opentofu.org/docs/intro/install), un acceso API o credenciales SSH hacia el VPS de destino, y Git para versionar sus archivos .tf. No se requiere ninguna dependencia adicional — OpenTofu descarga él mismo los providers en el tofu init.
Para pilotar Docker en un VPS remoto mediante OpenTofu, el daemon Docker del servidor debe escuchar en su socket Unix (por defecto) o en un puerto TCP seguro. El provider kreuzwerker/docker se conecta a ese socket por SSH o TCP.
Implementar OpenTofu en una flota de VPS
Instalar OpenTofu en local
Diríjase a opentofu.org/docs/intro/install para las instrucciones según su OS. En macOS con Homebrew:
brew install opentofu. En Debian/Ubuntu: el repositorio oficial de OpenTofu proporciona el paqueteopentofu. Verifique la instalación contofu version— el comando debe devolver la versión instalada.Estructurar su proyecto Infrastructure as Code
Cree un directorio dedicado y cuatro archivos estándar.
# main.tf — recursos principales # variables.tf — declaraciones de variables # outputs.tf — valores expuestos tras el apply # terraform.tfvars — valores concretos (añadir a gitignore si son secretos)Esta estructura no es obligatoria para OpenTofu, pero es la convención ampliamente adoptada:
main.tfcontiene los recursos,variables.tfdeclara los tipos y valores por defecto,outputs.tfexpone lo que Ansible u otra herramienta debe leer tras el aprovisionamiento (IP del servidor, nombre de host…), yterraform.tfvarscontiene los valores concretos que no desea codificar de forma fija enmain.tf.Declarar el provider y generar una clave SSH
OpenTofu instala los providers necesarios en el
tofu init. El providerhashicorp/tlsgenera un par de claves SSH en local, lo que evita gestionar claves manualmente.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 }Lance
tofu initpara descargar el provider y luegotofu applypara generar la clave. Recupérela contofu output -raw private_key_pem > ~/.ssh/vps_key && chmod 600 ~/.ssh/vps_key.Aprovisionar el VPS con null y local-exec
Si su proveedor de VPS no tiene un provider OpenTofu oficial, el provider
hashicorp/nullcon unlocal-execpermite ejecutar un comando local (una llamada API con curl, un script shell) y modelar el recurso en el 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 } }Este patrón es adecuado para una fase transitoria. Si su proveedor expone una API REST, basta con un script shell llamado en
local-execpara crear el servidor y escribir su IP en un archivo queoutputs.tfexpondrá después.Pilotar Docker en el VPS con el provider kreuzwerker
Una vez aprovisionado el VPS e instalado Docker (por Ansible, normalmente), el provider
kreuzwerker/dockerle permite declarar contenedores, redes y volúmenes en 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" }El provider se conecta al daemon Docker del VPS por SSH — ningún puerto TCP adicional que abrir. La versión estable del provider está disponible en el Terraform Registry.
Versionar el state para un equipo
En desarrollo en solitario, el state local (
terraform.tfstate) basta. En equipo o en CI/CD, dos desarrolladores que lanzantofu applya la vez corrompen el state. La solución es un backend remoto con bloqueo.OpenTofu soporta de forma nativa S3 (AWS, MinIO autoalojado) y el GitLab Managed Terraform State. Con un bucket MinIO en 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 } }El bloqueo es automático: si hay un
tofu applyen curso, un segundo se rechaza hasta que termine el primero.Integrar OpenTofu en su pipeline CI/CD
Un pipeline típico de Woodpecker CI o Forgejo Actions ejecuta
tofu planen cada pull request (para revisión humana del diff de infraestructura) ytofu applyal hacer merge en la rama principal.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: pushEl state remoto (paso anterior) es indispensable aquí: el runner de CI no tiene acceso al state local de su equipo.
Separe los workspaces por entorno
OpenTofu ofrece los workspaces para aislar varios estados en el mismo backend: tofu workspace new staging crea un espacio aislado y tofu workspace select production cambia a producción. Es más ligero que duplicar los directorios .tf. En la práctica, un workspace por cliente o por entorno (staging, prod) evita que un tofu destroy en staging afecte a producción. Nombre sus recursos con ${terraform.workspace} para que sigan siendo distintos en el state.
Ansible y OpenTofu: la frontera entre ambos
La pregunta surge a menudo: si Ansible ya hace el trabajo, ¿para qué añadir otra herramienta? La frontera es nítida una vez que se plantea con claridad.
OpenTofu responde a «¿qué existe?»: crea el servidor, le asigna una IP, coloca una clave SSH, registra su estado. Si lo elimina de su archivo .tf y lanza tofu apply, el servidor desaparece — OpenTofu gobierna el ciclo de vida.
Ansible responde a «¿en qué estado está lo que existe?»: instala Docker, configura Nginx, deja un archivo .env, reinicia un servicio. Si el servidor ya está ahí, Ansible hace con él lo que usted le pida — pero si no está, Ansible no puede crearlo.
El flujo natural para una agencia: OpenTofu aprovisiona el VPS y expone su IP como output, un playbook de Ansible consume ese output mediante un inventario dinámico, configura el servidor y despliega las aplicaciones. El artículo Ansible: automatizar la configuración de sus servidores VPS cubre la parte de configuración en detalle.
Solución de problemas: los errores frecuentes
Tres situaciones se repiten con regularidad cuando se adopta OpenTofu en una flota existente.
Errores comunes y su corrección
Error acquiring the state lock— un fallo durante unapplydeja un archivo.terraform.tfstate.lock.infoen el backend. OpenTofu se niega a continuar mientras exista el bloqueo. Tras comprobar que ningún otroapplyestá en curso, elimine el bloqueo contofu force-unlock <LOCK_ID>(el ID aparece en el mensaje de error). En un backend S3/MinIO, el archivo es visible en el bucket.tofu planmuestra undestroy + createinesperado — algunos atributos de un recurso fuerzan una recreación (force new resource) cuando cambian: el nombre del servidor, el tipo de imagen del OS, la región. Si modifica uno de esos atributos, OpenTofu no puede hacer una actualización en el sitio — destruye y vuelve a crear. Lea el plan con atención antes de aplicar y utilicetofu plan -target=resource.namepara limitar el alcance.- State perdido o desincronizado — si el archivo de state se pierde y los servidores siguen existiendo,
tofu state listenumera lo que OpenTofu cree aprovisionado, ytofu import <resource.type.name> <id-externe>importa un recurso existente en el state sin volver a crearlo. Es la herramienta de recuperación cuando la realidad y el state han divergido. - Provider no encontrado tras
tofu init— compruebe que elsourcedel provider es exacto (p. ej.kreuzwerker/dockery nodocker/docker) y que tiene acceso a internet desde la máquina que lanzatofu init. En un entorno air-gapped, descargue los providers por adelantado y utiliceplugin_cache_dir. Error: No valid credential sources found— OpenTofu no encuentra las credenciales del provider. Compruebe las variables de entorno que espera el provider (a menudoTF_VAR_api_tokeno un archivo de credenciales específico) y queterraform.tfvarsse lee correctamente (debe estar en el mismo directorio quemain.tf).
Su infraestructura se convierte en código versionado
OpenTofu no sustituye a SSH: hace que SSH sea excepcional. El flujo diario pasa a ser: modificar un archivo .tf, lanzar tofu plan para leer el diff, validar, lanzar tofu apply. Los servidores que ya no tiene que gestionar se destruyen con tofu destroy y desaparecen de la facturación.
Para una agencia que supera la decena de clientes, es la palanca que hace pasar la gestión de infraestructura de un trabajo de memoria a un trabajo de código. Un VPS Cloud ServOrbit con acceso root, IPv4 dedicada y elección de OS es la unidad base que sus archivos .tf aprovisionan y destruyen a demanda. Su infraestructura se convierte en código versionado.