Virtualización

cloud-init frente a nuageinit: lo que instala Debian para leer un YAML de once líneas y cómo lo lee FreeBSD (también en Proxmox)

Por Equipo Cloud Privado · · 12 min de lectura
Una hoja de configuración con dos caminos: uno carga una pila de cajas y otro entra directo en un único bloque

Las claves en 30 segundos

  • La nota es la misma. Un fichero #cloud-config en YAML con el nombre de la máquina, la zona horaria, los usuarios, las claves SSH y los paquetes. Es el idioma común de casi todas las nubes y de Proxmox VE.
  • En Debian 13, leerla cuesta un intérprete. cloud-init 25.1.4 trae Python 3.13 y una veintena larga de librerías. En nuestra prueba sobre una base con los paquetes required e important y el servidor OpenSSH, son 39 paquetes y 58,7 MiB con las dependencias estrictas y 57 paquetes y 115,7 MiB con la instalación por defecto de apt, que añade los recomendados. Vivian Voss, con su método, cuenta 46 paquetes y 61,2 MiB.
  • Y se ejecuta en cada arranque, porque tiene que comprobar si la máquina sigue siendo la misma instancia que ayer.
  • FreeBSD lo resolvió en el sistema base. Desde la 14.1 (junio de 2024) incluye nuageinit: 1.685 líneas entre programa, librería y scripts de arranque, en Lua y con el lector de YAML que ya trae el sistema. Solo corre en el primer arranque. No hay nada que instalar.
  • En Proxmox VE hay una trampa: Proxmox escribe la red del disco cloud-init en el formato versión 1 y nuageinit solo entiende el formato versión 2. La IP fija se ignora y la VM se queda con DHCP. Se arregla con un fragmento de red propio (cicustom), y lo explicamos más abajo.

Toda máquina virtual empieza su vida leyendo una nota parecida a esta:

#cloud-config
hostname: web01
timezone: Europe/Madrid
users:
  - name: admin
    shell: /bin/sh
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... admin@portatil
packages:
  - tmux
  - rsync

Un nombre, una zona horaria, un usuario con su clave y dos paquetes. Cualquier administrador lo hace a mano en un par de minutos, y nadie lo hace, porque es justo el trabajo que una máquina debería resolver sola al despertarse. Es lo que hace posible clonar plantillas en Proxmox y que cada clon arranque con su nombre y su IP sin que nadie abra la consola.

Lo que la máquina tiene que cargar para leer esas once líneas depende mucho del sistema. Vivian Voss lo ha medido en un análisis publicado esta semana, y administraciondesistemas.com lo ha contado en castellano. Partimos de ahí, repetimos la medición de Debian en nuestra propia prueba, leímos el código de nuageinit en FreeBSD 15.1 y añadimos lo que más nos importa: qué pasa cuando esa VM FreeBSD vive en un clúster Proxmox.

Lo que trae cloud-init a un Debian 13

En Debian y Ubuntu, quien lee la nota es cloud-init, y las imágenes cloud oficiales de las dos distribuciones lo traen instalado. Para ver qué arrastra, partimos de un contenedor debian:13, le instalamos todos los paquetes de prioridad required e important y el servidor OpenSSH, que es lo mínimo razonable de un servidor, y pedimos a apt que simulara la instalación de cloud-init 25.1.4 (la versión de Debian 13 a fecha de hoy, 25.1.4-1+deb13u1).

InstalaciónPaquetes nuevosTamaño instalado
Solo dependencias (--no-install-recommends)3958,7 MiB
Por defecto de apt (con recomendados)57115,7 MiB
Recuento de Vivian Voss (amd64, su base)4661,2 MiB

Nuestra prueba es del 18 de octubre de 2026, sobre arm64; la de Voss, sobre amd64. Las cifras exactas cambian con la base de partida y con la arquitectura, pero el orden de magnitud es el mismo.

En la versión estricta, el grueso es Python:

  • El intérprete Python 3.13 con su biblioteca estándar, unos 23 MiB. Un Debian mínimo de servidor no lo instala: ningún paquete required ni important lo necesita.
  • Veintitrés librerías de Python, unos 11 MiB más: python3-requests, python3-jinja2, python3-cryptography, python3-jsonschema, python3-yaml, python3-oauthlib y compañía.
  • El resto: locales, gdisk, eject, lsb-release y un invitado curioso, netcat-openbsd.

Con los recomendados, apt suma sudo, cloud-guest-utils (para crecer la partición al tamaño del disco) y otra tanda de librerías de Python que jsonschema usa para validar formatos de fecha, URI o color.

El programa en sí es grande porque hace mucho. En nuestra cuenta, el paquete trae 69.122 líneas de Python (Voss cuenta 69.102), 31 fuentes de datos (datasources, cada una sabe dónde guarda la nota un tipo de nube) y 59 módulos que saben qué hacer con ella una vez encontrada: usuarios, paquetes, discos, Chef, Puppet, LXD y bastante más.

Por qué hay un netcat de OpenBSD en un servidor Linux

Hasta la versión 24.3, publicada el 30 de agosto de 2024, cloud-init arrancaba como cuatro procesos de Python en cada arranque, uno por etapa (local, network, config y final). Cada uno cargaba el intérprete y sus librerías desde cero.

El cambio single process optimization (integrado el 2 de agosto de 2024) los fundió en uno: un único proceso de Python que espera en un socket Unix a que systemd le diga cuándo empezar cada etapa. Para hablar con ese socket desde las unidades de systemd, el proyecto eligió nc de OpenBSD. Su justificación en la propia petición de cambio es muy práctica: entiende sockets Unix y es «siete veces más rápido» que una implementación mínima en Python puro.

En un Debian 13 se ve en las unidades instaladas: además de las cuatro etapas, está cloud-init-main.service, el proceso que espera, y cloud-init-hotplugd.service para la conexión en caliente de interfaces.

Por qué se ejecuta en cada arranque

cloud-init no sabe de antemano si la máquina que arranca es la de ayer o un clon nuevo de la misma imagen. En cada arranque compara el identificador de instancia que le da la nube (o el disco de Proxmox) con el que guardó la última vez. Si coincide, hace poco más; si cambia, vuelve a aplicar la configuración por instancia.

Eso tiene una ventaja real en Proxmox: si cambias la IP de una VM Debian en la pestaña Cloud-Init y regeneras la imagen, Proxmox cambia el identificador de instancia y la VM aplica la red nueva en el siguiente arranque. Lo veremos al hablar de FreeBSD, donde no pasa.

Y el precio también es real: en el mismo contenedor, cloud-init --version, que solo carga el programa e imprime la versión, tarda de 120 a 150 milisegundos por ejecución. Voss mide 206 ms en un Intel Core i5-13500 con el port de FreeBSD y Python 3.12. Es poco por arranque, pero es tiempo de carga del intérprete, antes de leer una sola línea de YAML.

Por qué cloud-init es así

cloud-init se ha ganado su tamaño. Cada nube grande inventó su propia forma de entregar la configuración a una máquina nueva, con su dirección de metadatos y su dialecto, y cloud-init, que Canonical empezó para Ubuntu en Amazon, las fue aprendiendo una a una. Su nota se convirtió en el idioma común del sector: quien construye imágenes escribe #cloud-config una vez y la misma imagen sirve en cualquier proveedor que cloud-init conozca.

La condición es que la imagen lleve cloud-init y todo lo que necesita durante toda la vida de la máquina. Quien construye la imagen y la nube ganan con eso: un agente para todas las plataformas. Quien opera la máquina paga la factura: un intérprete y varias decenas de paquetes más que actualizar y cuyos avisos de seguridad seguir, en un servidor que estaba pensado para otra cosa. Es la misma cuenta que hacíamos en la línea base de bastionado para Debian y Ubuntu: cada paquete que no necesitas es superficie que mantener.

Cómo lo lee FreeBSD: nuageinit

FreeBSD 14.1, publicada el 4 de junio de 2024, estrenó en el sistema base nuageinit (nuage es «nube» en francés). Lee la misma nota, y su página de manual dice que «se cree que cumple con la especificación de Cloud Init», que es más o menos lo más parecido a un juramento que suele hacer una página de manual.

Está escrito en Lua y lo ejecuta flua, el intérprete que FreeBSD guarda en el sistema base para sus propias herramientas. El YAML lo lee lyaml, un enlace a la librería en C libyaml que también viene de serie, y el JSON de OpenStack, el lector UCL del sistema. Hemos contado las líneas en el árbol releng/15.1:

PiezaLíneas
nuageinit (el programa)809
nuage.lua (la librería)721
Tres scripts de arranque en rc.d155
Total1.685

Coincide al dígito con el recuento de Voss.

Tres scripts y un fichero centinela

El trabajo se reparte en tres scripts de arranque, en el orden del arranque:

  1. nuageinit corre cuando están montados los sistemas de ficheros locales y antes de la red: nombre de máquina, zona horaria, grupos, usuario por defecto, claves de host SSH, configuración de red, runcmd y los ficheros que no esperan a la red.
  2. nuageinit_post_net corre con la red levantada y antes de los servicios: paquetes (con package_update y package_upgrade), usuarios, contraseñas y los ficheros diferidos.
  3. nuageinit_user_data_script corre al final, si lo que trae la nota es un script de shell (#!) en lugar de un #cloud-config.

Los tres llevan la palabra clave firstboot. El sistema de arranque rc(8) solo los ejecuta mientras existe el fichero /firstboot, y lo borra al terminar el primer arranque. A partir del segundo, nuageinit no cuesta nada.

Qué cubre

Cubre lo que pide casi cualquier primer arranque: hostname y fqdn, timezone, groups, users (con gecos, shell, grupos, claves, contraseña, sudo y doas), ssh_authorized_keys, ssh_keys para las claves de host, ssh_pwauth, chpasswd, write_files (con codificación, permisos y propietario), network con ethernets, packages, package_update, package_upgrade y runcmd.

Lo que no cubre es lo raro: Chef, Puppet, LXD, particionado de discos y el resto de los 59 módulos de cloud-init. Si tu nota depende de eso, en FreeBSD está el port de cloud-init (25.2 en py312-cloud-init), a un pkg install de distancia. Voss cuenta que trajo 36 paquetes en su prueba.

Dónde busca la nota

nuageinit conoce dos sitios:

  • Un disco con sistema de ficheros ISO 9660 o FAT etiquetado cidata (el formato NoCloud) o config-2 (el config drive de OpenStack).
  • OpenStack, que detecta por la descripción SMBIOS del producto y de cuyo servicio de metadatos lee la configuración.

cloud-init conoce 31, entre ellas las de Amazon y Azure. En esas nubes, FreeBSD usa el port de cloud-init, como cualquier otro sistema.

Para un clúster Proxmox, con los dos sitios basta: el disco cloud-init de Proxmox es exactamente uno de esos dos formatos.

Las imágenes oficiales

FreeBSD publica imágenes de máquina virtual preparadas para esto, las BASIC-CLOUDINIT. Su receta de construcción (release/tools/basic-cloudinit.conf) activa nuageinit, el crecimiento automático del sistema de ficheros (growfs) y sshd, y añade un solo paquete a la imagen: firstboot-freebsd-update, para que la máquina se baje los parches de seguridad en el primer arranque.

Dos detalles de esa receta que conviene conocer antes de ponerla en producción:

  • La red por defecto es DHCP (ifconfig_DEFAULT="SYNCDHCP accept_rtadv"). Si nuageinit no aplica una IP fija, la VM se queda con lo que le dé el DHCP, sin avisar.
  • La imagen deja en sshd_config PasswordAuthentication yes y UsePAM no. Si tu nota no lleva contraseñas, añade ssh_pwauth: false o un write_files que lo corrija. Es la misma idea que repasábamos en SSH seguro en servidores Linux: solo claves, desde el primer minuto.

Cuánto tarda en arrancar cada uno

Voss instaló el port de cloud-init en una jail de FreeBSD 15.0 sobre un Intel Core i5-13500 y lanzó cada programa veinte veces:

LectorQué hace la prueba20 ejecucionesPor ejecución
cloud-init 25.2 (port, Python 3.12)cloud-init --version4,13 s206 ms
nuageinit (FreeBSD 15.0)sin argumentos: carga la librería y los lectores de YAML y UCL, ve que no tiene trabajo y lo dice0,10 s5 ms

Unas cuarenta veces más, antes de leer una línea de YAML. Conviene leerlo en su justa medida: es lo que tarda en arrancar cada programa, no un primer arranque completo. La espera a la red y la descarga de paquetes del mirror cuestan lo mismo en los dos sistemas y son, con diferencia, lo que más tarda. Hasta la 24.3, eso sí, el lado de Python pagaba ese arranque cuatro veces en cada inicio.

FreeBSD en Proxmox VE: la IP fija que se pierde

Aquí está la parte que más nos importa a quienes montamos clústeres Proxmox. Si importas una imagen BASIC-CLOUDINIT de FreeBSD 15, le pones un disco cloud-init, rellenas usuario, clave e IP fija en la pestaña Cloud-Init y arrancas, el usuario y la clave llegan… pero la VM arranca con una IP de DHCP o sin IP.

El motivo está en el código de las dos partes:

  • Proxmox VE, para sistemas que no son Windows, usa por defecto el formato NoCloud (disco cidata) y escribe el fichero network-config en el formato versión 1 de cloud-init: una lista config: con entradas type: physical. Lo hemos comprobado en el módulo Cloudinit.pm de qemu-server, que tiene también un generador para la versión 2 que no se usa por defecto.
  • nuageinit, en FreeBSD 15.1, lee ese network-config y lo trata como el formato versión 2: espera un bloque ethernets: con las interfaces. Con la versión 1 no encuentra nada que configurar.
  • La alternativa configdrive2 de Proxmox tampoco sirve para la red: para sistemas que no son Windows escribe la red en formato interfaces de Debian, y nuageinit, en un disco config-2, busca network_data.json, que Proxmox no genera en ese caso. Si no lo encuentra, sigue sin red configurada y sin avisar.

Christian Hofstede-Kuhn documentó el mismo síntoma en diciembre de 2025 con FreeBSD 15.0 y Proxmox VE 9.1, y lo resolvió generando el ISO cidata por su cuenta. Hay un camino más corto con las herramientas de Proxmox: sustituir solo el fragmento de red por uno propio en versión 2, con cicustom, y dejar que Proxmox siga generando el usuario y los metadatos.

El fragmento de red en versión 2

Primero, un almacenamiento que admita fragmentos (snippets). En el local de un nodo basta con añadir snippets a los tipos de contenido que ya tenga (la orden sustituye la lista entera, así que revísala antes en /etc/pve/storage.cfg):

pvesm set local --content iso,vztmpl,backup,snippets

Después, el fichero con la red en el formato que entiende nuageinit. En FreeBSD, las interfaces VirtIO se llaman vtnet0, vtnet1…:

# /var/lib/vz/snippets/freebsd-web01-red.yaml
version: 2
ethernets:
  vtnet0:
    addresses:
      - 192.0.2.20/24
    gateway4: 192.0.2.1
    nameservers:
      search: [ejemplo.lan]
      addresses: [192.0.2.53]

Y por último, decirle a la VM que use ese fragmento para la red y regenerar el disco:

qm set 9020 --cicustom "network=local:snippets/freebsd-web01-red.yaml"
qm cloudinit update 9020

El usuario, la clave SSH y el nombre siguen saliendo de la pestaña Cloud-Init; solo la red viene del fragmento. Si hay varias interfaces o no quieres depender del nombre, nuageinit admite match: con macaddress:, name: o driver:, y set-name: para renombrarla.

Este procedimiento se deduce del código de las dos partes a fecha de hoy (FreeBSD 15.1 y la rama principal de qemu-server). Pruébalo en una VM antes de llevarlo a una plantilla, y vuelve a comprobarlo cuando actualices Proxmox o FreeBSD: es justo el tipo de detalle que puede cambiar en cualquiera de los dos lados.

Cambiar la IP después no funciona igual

La otra diferencia con Debian viene del diseño de solo primer arranque. En una VM Debian, si cambias la IP en Proxmox, cloud-init la aplica en el siguiente arranque porque cambia el identificador de instancia. En una VM FreeBSD, nuageinit ya no se ejecuta: el fichero /firstboot desapareció tras el primer arranque.

Para una plantilla eso es lo que quieres: el clon se configura una vez y luego es suyo. Para cambiar la IP de una VM ya desplegada, toca hacerlo dentro (en /etc/rc.conf, con sysrc) o con tu herramienta de gestión de configuración. También se puede volver a crear /firstboot y reiniciar, pero nuageinit repetiría todo lo demás que hay en la nota, paquetes y runcmd incluidos.

¿Y en Debian? Lo que sí se puede ahorrar

Si tus VM Debian o Ubuntu salen de plantillas en Proxmox y no cambian de identidad después, hay dos ajustes razonables:

  • Desactivarlo tras el primer arranque. La documentación de cloud-init admite crear /etc/cloud/cloud-init.disabled, o pasar cloud-init=disabled al kernel. Las unidades de systemd siguen ahí, pero no hacen nada. Lo que pierdes es justo la ventaja de antes: si cambias la IP en Proxmox, ya no se aplicará sola.
  • Instalar sin recomendados cuando construyas la plantilla tú mismo, partiendo de una instalación mínima en lugar de la imagen cloud. En nuestra prueba es la diferencia entre 57 y 39 paquetes.

Desinstalarlo del todo es otra conversación: cloud-init deja escrita la configuración de red que generó, y conviene revisar qué ficheros son suyos antes de purgar el paquete.

Entonces, ¿importa?

Para una VM con 8 GB de disco, 60 o 115 MiB no cambian nada, y 200 ms de arranque tampoco. Lo que sí pesa es lo que no se ve en el df: un intérprete y varias decenas de paquetes más que entran en cada aviso de seguridad, en cada actualización y en cada auditoría, multiplicados por cada VM del clúster. Es la misma tesis que defendía Voss sobre ZFS en Linux frente a FreeBSD: FreeBSD pagó la integración una vez, en su sistema base, con un lenguaje que ya llevaba para el cargador de arranque y sus propias herramientas.

Para quien opera un clúster Proxmox, la conclusión práctica es más concreta:

  • Las VM Debian y Ubuntu van bien con cloud-init y la integración de Proxmox es la más probada. Si no las reconfiguras desde Proxmox después de crearlas, desactivarlo tras el primer arranque es un ahorro limpio.
  • Las VM FreeBSD se aprovisionan con la misma pestaña Cloud-Init y sin instalar nada, siempre que la red llegue en versión 2 por cicustom. Es una buena noticia para quien usa FreeBSD como servidor de almacenamiento, cortafuegos o jails, y un detalle que conviene dejar resuelto en la plantilla.

En Cloud Privado montamos clústeres Proxmox con plantillas para Linux y FreeBSD, y dejamos el aprovisionamiento por cloud-init funcionando desde el primer día. Si quieres llevarlo además a código, el siguiente paso es Terraform u OpenTofu sobre Proxmox. Y si estás valorando una plataforma, cuéntanos qué necesitas.

Preguntas frecuentes

¿Qué es nuageinit?

Es el lector de configuración de primer arranque que FreeBSD incluye en su sistema base desde la 14.1, de junio de 2024. Lee la misma nota #cloud-config que cloud-init, desde un disco cidata o config-2 o desde OpenStack, y aplica usuarios, claves, red, ficheros, paquetes y comandos. Está escrito en Lua y solo se ejecuta en el primer arranque.

¿nuageinit sustituye por completo a cloud-init?

No. Cubre las claves que pide casi cualquier primer arranque, pero no los módulos menos habituales de cloud-init, como Chef, Puppet, LXD o el particionado de discos, ni las fuentes de datos de Amazon o Azure. Para eso, FreeBSD ofrece el port de cloud-init, que se instala con pkg.

¿Cuánto ocupa cloud-init en Debian 13?

Depende de la base y de si instalas los paquetes recomendados. En nuestra prueba sobre una base con los paquetes required e important y OpenSSH, cloud-init 25.1.4 añadió 39 paquetes y 58,7 MiB con las dependencias estrictas, y 57 paquetes y 115,7 MiB con la instalación por defecto de apt. La mayor parte es Python 3.13 y sus librerías.

¿Por qué Proxmox no le pone la IP fija a mi VM FreeBSD?

Porque Proxmox escribe la red del disco cloud-init en el formato versión 1 y nuageinit solo entiende el formato versión 2. La configuración se ignora y la imagen oficial se queda con DHCP. La solución es pasar la red como fragmento propio en versión 2 con qm set <vmid> --cicustom "network=local:snippets/fichero.yaml".

¿Puedo desactivar cloud-init después del primer arranque en Debian?

Sí. La documentación de cloud-init admite crear el fichero /etc/cloud/cloud-init.disabled o añadir cloud-init=disabled a la línea de arranque del kernel. A cambio, los cambios que hagas después en la pestaña Cloud-Init de Proxmox ya no se aplicarán solos.

¿Por qué cloud-init necesita netcat?

Desde la 24.3, cloud-init funciona como un único proceso de Python que espera en un socket Unix, y las unidades de systemd le avisan de cada etapa con nc de OpenBSD, que entiende sockets Unix. Antes eran cuatro procesos de Python independientes en cada arranque.

Fuentes