Problema
En entornos homelab o pequeñas infraestructuras con Proxmox, es frecuente que cada máquina virtual o contenedor necesite información adicional: rutas de reverse‑proxy, definición de stacks Docker, asignación de GPUs, o cualquier otro dato de “intención” que no pertenece al propio hypervisor. La práctica habitual es guardar esa información en notas, etiquetas o archivos externos. Cada alternativa tiene limitaciones claras:
- Notas son texto libre, difíciles de validar y no se versionan de forma estructurada.
- Etiquetas son planas, no permiten jerarquías ni valores complejos.
- Repositorios de configuración externos obligan a mantener una sincronía manual entre Proxmox y el origen de datos.
El resultado es un “spaghetti” de scripts y archivos que se rompen al mover máquinas, al escalar nodos o al cambiar la topología de red. Lo que falta es un punto de almacenamiento que sea nativo a Proxmox, versionado con el resto del cluster y accesible tanto desde la UI como desde la línea de comandos.
Causa
Los síntomas aparecen cuando:
- Se necesita un punto único de verdad para datos que no son parte del modelo de VM/LXC (por ejemplo, puertos que expone Traefik).
- Los scripts de arranque (hook scripts) deben leer esa información antes de iniciar la VM.
- Se quiere reutilizar la metadata en varios sistemas (Traefik, Docker‑Compose, gestores de GPU) sin duplicar archivos.
En la práctica, la raíz del problema es la ausencia de un mecanismo de metadata estructurada integrado en Proxmox. Sin él, los administradores improvisan con notas o con archivos en directorios arbitrarios, lo que genera:
- Inconsistencias entre nodos cuando el clúster replica solo
/etc/pve. - Falta de validación de esquema, lo que permite errores de sintaxis que sólo aparecen en tiempo de ejecución.
- Dificultad para exponer la metadata a APIs externas sin escribir parsers ad‑hoc.
Solución
Implementar un repositorio de archivos YAML uno por VM/LXC, almacenado bajo /etc/pve y gestionado por una API nativa. Cada archivo sigue un JSON Schema que define sub‑árboles para los distintos consumidores (por ejemplo, traefik, compose, nvidia). Con este enfoque se consigue:
- Persistencia automática: los archivos se replican con el resto de la configuración del clúster.
- Validación temprana: cualquier error de sintaxis es rechazado por el esquema antes de que la VM arranque.
- Interoperabilidad: scripts de hook, servicios externos y la UI pueden leer el mismo documento sin ambigüedades.
Paso a paso
- Crear el esquema JSON que describa los bloques esperados. Un ejemplo mínimo incluye
traefik,composeynvidia. - Instalar el paquete que expone la API (
pve-meta) y añade una pestaña de edición en la UI. - Definir la convención de nombres:
vm-101.yamlpara la VM con ID 101,lxc-202.yamlpara el contenedor 202. - Poblar los archivos con la información requerida.
- Configurar los loops (scripts o systemd timers) que lean los bloques y generen los artefactos finales:
- Renderizar
compose→docker-compose.ymldentro del nodo y ejecutardocker compose up -d. - Convertir
traefik→ archivo dinámico consumido por Traefik HTTP provider. - Actualizar la configuración de passthrough GPU en
/etc/pve/lxc/202.confa partir del bloquenvidia.
- Renderizar
Este modelo es extensible: cualquier nuevo servicio solo necesita añadir su sub‑árbol al esquema y un consumidor que lo interprete.
Cuándo aplicar esta solución
- Entornos con múltiples VMs/LXCs que comparten configuraciones de red, proxy o hardware.
- Automatizaciones basadas en metadata (por ejemplo, despliegues de Docker‑Compose por VM).
- Necesidad de mantener la configuración bajo control de versiones del clúster sin repositorios externos.
No es recomendable cuando:
- Sólo se gestionan unas pocas máquinas y la sobrecarga de crear esquemas resulta innecesaria.
- Se requiere un control de acceso granular a la metadata; la solución actual carece de permisos por‑campo.
Código
# 1. Instalar pve-meta (asumiendo repositorio configurado)
apt-get update && apt-get install -y pve-meta
# 2. Crear esquema JSON (guardado en /etc/pve/meta-schema.json)
cat > /etc/pve/meta-schema.json <<'EOF'
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"traefik": {
"type": "object",
"properties": {
"router": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"rule": { "type": "string" },
"service": { "type": "string" },
"middlewares": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["rule", "service"]
}
}
}
},
"compose": {
"type": "object"
},
"nvidia": {
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": false
}
EOF
# 3. Crear metadata para la VM 101
cat > /etc/pve/vm-101.yaml <<'EOF'
traefik:
router:
web:
rule: "Host(`app.example.com`)"
service: "app-service"
compose:
version: "3.9"
services:
app:
image: "my/app:latest"
ports:
- "8080:80"
nvidia:
- "GPU-1234abcd"
EOF
# 4. Validar (pve-meta validate) y aplicar cambios
pve-meta validate /etc/pve/vm-101.yaml
pve-meta apply /etc/pve/vm-101.yaml
Verificación
- Validación del esquema: el comando
pve-meta validatedebe devolverOK. Cualquier error aparecerá con línea y campo. - Traefik: abrir
http://<node-ip>:8080/api/http/routersy confirmar que el routerwebestá presente. - Docker‑Compose: ejecutar
docker compose psdentro del nodo donde se renderizó el stack; los contenedores declarados deben estar en estadorunning. - GPU passthrough: inspeccionar la configuración del contenedor (
cat /etc/pve/lxc/101.conf) y buscar la línealxc.cgroup2.devices.allow = c 195:* rwm. El UUID listado ennvidiadebe coincidir con la tarjeta asignada.
Si alguno de los pasos falla, revisar el archivo YAML en busca de errores de sangrado o claves no definidas en el esquema.
Notas adicionales
- Backup: los archivos YAML forman parte de
/etc/pve, por lo que los snapshots del clúster ya los incluyen. Sin embargo, es buena práctica versionar los cambios en un repositorio Git externo para auditoría. - Extensión de permisos: aunque la versión actual no permite control granular, se puede envolver la API con
sudo -uo crear un wrapper que verifique el usuario antes de ejecutarpve-meta apply. - Escalado: en clusters con decenas de nodos, considera un loop centralizado (por ejemplo, un timer en cada nodo) que lea solo los archivos locales, evitando tráfico innecesario entre nodos.
- Depuración: habilita
log_level = debugen/etc/pve/pve-meta.confpara obtener trazas detalladas cuando los consumidores (Traefik, Docker) no reflejen los cambios esperados.
Con esta arquitectura, la metadata deja de ser un parche y se convierte en una capa de intención declarativa que cualquier herramienta del ecosistema puede consumir de forma fiable.