--- title: "Introducción a Pulumi con Python en Ubuntu 24.04" description: "Instala Pulumi en Ubuntu 24.04, crea un proyecto en Python con estado local, despliega un contenedor Docker y gestiona configuración y secretos por stack." category: "Automatización e IaC" language: es source: https://cubepath.com/es/docs/automatización-e-iac/introduccion-a-pulumi-infraestructura-como-codigo updated: 2026-09-25 --- # Introducción a Pulumi con Python en Ubuntu 24.04 Pulumi es una herramienta de infraestructura como código que describe recursos con lenguajes de programación generales (Python, TypeScript, Go, C# o Java) en lugar de un lenguaje propio como HCL. Detrás hay el mismo modelo que en Terraform: un estado que registra qué existe, un plan que muestra los cambios y proveedores que hablan con cada API. En este tutorial instalarás Pulumi en Ubuntu 24.04, crearás un proyecto en Python que despliega un contenedor Nginx con el proveedor de Docker, usarás configuración y secretos por stack, y verás en qué se diferencia de Terraform. Se usa Docker como destino para que puedas practicar sin cuenta en ninguna nube; el flujo de trabajo es idéntico con AWS, Azure, Kubernetes o cualquier otro proveedor. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con un usuario no root con privilegios `sudo`. - Python 3.12 (incluido en Ubuntu 24.04). - Al menos 1 GB de RAM libre para Docker y los plugins de Pulumi. - El puerto 8080 libre, que se usará para publicar el contenedor de prueba. ## Paso 1: Instalar Docker y las dependencias de Python Pulumi crea un entorno virtual de Python para cada proyecto, así que necesitas el módulo `venv`. Instala también Docker desde los repositorios de Ubuntu: ```bash sudo apt update sudo apt install python3-venv docker.io ``` Añade tu usuario al grupo `docker` para que Pulumi pueda usar el socket de Docker sin `sudo`: ```bash sudo usermod -aG docker "$USER" newgrp docker ``` Comprueba que Docker responde: ```bash docker info --format '{{.ServerVersion}}' ``` ```output 27.5.1 ``` > Nota: pertenecer al grupo `docker` equivale a tener acceso root en el servidor. Hazlo solo con usuarios de confianza. ## Paso 2: Instalar la CLI de Pulumi Pulumi distribuye la CLI con un script de instalación oficial. Descárgalo primero para revisarlo antes de ejecutarlo: ```bash curl -fsSL https://get.pulumi.com -o install-pulumi.sh less install-pulumi.sh ``` Ejecútalo con tu usuario normal, sin `sudo`. Instala los binarios en `~/.pulumi/bin` y añade esa ruta a tu `~/.bashrc`: ```bash sh install-pulumi.sh ``` Recarga la configuración del shell y comprueba la versión: ```bash source ~/.bashrc pulumi version ``` ```output v3.197.0 ``` La versión exacta será la última disponible. Para actualizar más adelante, vuelve a ejecutar el script. ## Paso 3: Elegir dónde se guarda el estado Por defecto Pulumi guarda el estado en Pulumi Cloud, un servicio que requiere cuenta. Para esta guía usarás el backend local, que guarda el estado en `~/.pulumi` y no depende de nada externo: ```bash pulumi login --local ``` ```output Logged in to servidor as tu_usuario (file://~) ``` Con el backend local, Pulumi cifra los secretos del stack con una frase de paso. Expórtala en la sesión para que no la pida en cada comando, y guárdala en tu gestor de contraseñas, porque sin ella no podrás leer los secretos: ```bash export PULUMI_CONFIG_PASSPHRASE="tu_frase_de_paso_segura" ``` Sustituye `tu_frase_de_paso_segura` por una frase larga y única. Para trabajar en equipo, el estado puede ir también a un bucket con `pulumi login s3://nombre-del-bucket` (u otros almacenamientos como Azure Blob o Google Cloud Storage). El resto de comandos de la guía no cambia. ## Paso 4: Crear el proyecto Crea un directorio vacío y genera un proyecto a partir de la plantilla de Python: ```bash mkdir -p ~/pulumi-web && cd ~/pulumi-web pulumi new python --name pulumi-web --stack dev --yes ``` `--yes` acepta los valores por defecto del resto de preguntas. El comando crea el stack `dev`, un entorno virtual e instala las dependencias básicas. El proyecto queda así: ```bash ls -A ``` ```output .gitignore Pulumi.yaml __main__.py requirements.txt venv ``` - `Pulumi.yaml`: nombre del proyecto y runtime (Python y el entorno virtual que usa). - `__main__.py`: el programa que define la infraestructura. - `requirements.txt`: dependencias de Python, incluido el SDK `pulumi`. Un **stack** es una instancia independiente del proyecto con su propio estado y su propia configuración, por ejemplo `dev`, `staging` y `produccion`. ## Paso 5: Añadir el proveedor de Docker Cada proveedor de Pulumi es un paquete de Python. Añade el de Docker a `requirements.txt`: ```bash nano requirements.txt ``` ```text pulumi>=3.0.0,<4.0.0 pulumi-docker>=4.0.0,<5.0.0 ``` Instala las dependencias en el entorno virtual del proyecto: ```bash pulumi install ``` `pulumi install` lee `Pulumi.yaml`, instala los paquetes de `requirements.txt` en el entorno virtual y descarga los plugins de proveedor necesarios. ## Paso 6: Escribir el programa Sustituye el contenido de `__main__.py` por un programa que descarga la imagen de Nginx y arranca un contenedor: ```bash nano __main__.py ``` ```python import pulumi import pulumi_docker as docker config = pulumi.Config() puerto = config.get_int("puerto") or 8080 imagen = docker.RemoteImage( "nginx", name="nginx:stable", keep_locally=True, ) contenedor = docker.Container( "web", name=f"web-{pulumi.get_stack()}", image=imagen.image_id, ports=[docker.ContainerPortArgs(internal=80, external=puerto)], restart="unless-stopped", ) pulumi.export("contenedor", contenedor.name) pulumi.export("url", f"http://localhost:{puerto}") ``` Fíjate en `image=imagen.image_id`: el contenedor recibe una salida de otro recurso. Pulumi deduce de ahí que debe crear la imagen antes que el contenedor, igual que Terraform con las referencias entre recursos. ## Paso 7: Previsualizar y desplegar Muestra qué cambios haría Pulumi sin aplicarlos: ```bash pulumi preview ``` ```output Previewing update (dev): Type Name Plan + pulumi:pulumi:Stack pulumi-web-dev create + ├─ docker:index:RemoteImage nginx create + └─ docker:index:Container web create Resources: + 3 to create ``` Aplica los cambios: ```bash pulumi up --yes ``` ```output Updating (dev): Type Name Status + pulumi:pulumi:Stack pulumi-web-dev created (4s) + ├─ docker:index:RemoteImage nginx created (3s) + └─ docker:index:Container web created (1s) Outputs: contenedor: "web-dev" url : "http://localhost:8080" Resources: + 3 created ``` Comprueba que el contenedor responde: ```bash curl -sI http://localhost:8080 | head -n 1 ``` ```output HTTP/1.1 200 OK ``` Consulta los outputs del stack cuando quieras con: ```bash pulumi stack output url ``` ## Paso 8: Configuración y secretos por stack El programa lee el puerto con `config.get_int("puerto")`. Cambia el valor solo para el stack `dev`: ```bash pulumi config set puerto 8081 ``` El valor se guarda en `Pulumi.dev.yaml`, que se versiona junto al código: ```bash cat Pulumi.dev.yaml ``` ```output encryptionsalt: v1:... config: pulumi-web:puerto: "8081" ``` Aplica el cambio. Pulumi reemplaza el contenedor porque el puerto publicado no se puede modificar en caliente: ```bash pulumi up --yes ``` Para valores sensibles usa `--secret`. Pulumi los cifra en el archivo de configuración y en el estado: ```bash pulumi config set --secret token_api "valor_secreto" ``` ```bash grep token_api Pulumi.dev.yaml ``` ```output pulumi-web:token_api: secure: v1:3kZ0... ``` En el programa, léelo con `config.require_secret("token_api")`. El resultado es una salida marcada como secreta: si la exportas o la pasas a un recurso, Pulumi la oculta en la consola y la mantiene cifrada en el estado. Para verla en claro: ```bash pulumi config get token_api ``` ## Paso 9: Trabajar con varios stacks Crea un segundo stack para simular otro entorno: ```bash pulumi stack init staging pulumi config set puerto 8090 pulumi up --yes ``` Ahora hay dos contenedores, `web-dev` y `web-staging`, cada uno con su estado. Lista los stacks y cambia entre ellos: ```bash pulumi stack ls pulumi stack select dev ``` ```output NAME LAST UPDATE RESOURCE COUNT dev* 2 minutes ago 3 staging 30 seconds ago 3 ``` ## Paso 10: Destruir los recursos Borra los recursos de cada stack y después el propio stack: ```bash pulumi destroy --yes --stack staging pulumi stack rm staging --yes ``` Repite con `dev` si ya no lo necesitas. `pulumi stack rm` se niega a borrar un stack que todavía tiene recursos, lo que evita dejar infraestructura huérfana sin estado. ## Pulumi frente a Terraform | Aspecto | Pulumi | Terraform | |---|---|---| | Lenguaje | Python, TypeScript, Go, C#, Java, YAML | HCL | | Lógica | Bucles, funciones, clases y paquetes del lenguaje | `count`, `for_each`, módulos y funciones integradas | | Estado | Pulumi Cloud, local o bucket (S3, Azure, GCS) | Local o backend remoto (S3, HCP Terraform, pg, etc.) | | Secretos | Cifrados en configuración y estado | En texto plano en el estado; `sensitive` solo oculta la salida | | Pruebas | Tests unitarios con las herramientas del lenguaje (pytest, Jest) | `terraform test` | | Licencia | Apache 2.0 | BSL 1.1 (OpenTofu es la bifurcación libre) | Elige Pulumi si tu equipo ya programa en uno de esos lenguajes y la infraestructura tiene lógica que en HCL resulta forzada, o si quieres reutilizar componentes como paquetes normales. Elige Terraform u OpenTofu si prefieres un lenguaje declarativo que cualquiera pueda leer sin saber programar, o si ya tienes módulos y experiencia en HCL. ## Solución de problemas **`error: getting secrets manager: passphrase must be set`**: falta la variable `PULUMI_CONFIG_PASSPHRASE` en la sesión. Expórtala con la misma frase que usaste al crear el stack. **`Cannot connect to the Docker daemon at unix:///var/run/docker.sock`**: tu usuario no está en el grupo `docker` en la sesión actual. Cierra sesión y vuelve a entrar, o ejecuta `newgrp docker`. **`ModuleNotFoundError: No module named 'pulumi_docker'`**: el paquete no está en el entorno virtual del proyecto. Comprueba `requirements.txt` y ejecuta `pulumi install` de nuevo. **`Bind for 0.0.0.0:8080 failed: port is already allocated`**: otro proceso usa el puerto. Cambia el valor con `pulumi config set puerto` y vuelve a aplicar. ## Conclusión Has instalado Pulumi, creado un proyecto en Python con estado local, desplegado y modificado un contenedor, cifrado secretos en la configuración y gestionado dos stacks independientes. Como siguientes pasos, mueve el estado a un bucket compartido con `pulumi login s3://...`, sustituye el proveedor de Docker por el de tu nube o por `pulumi-kubernetes`, y agrupa recursos relacionados en un `ComponentResource` para reutilizarlos entre proyectos.