Problema
En cualquier homelab con varios contenedores LXC es fácil perder la pista de cuál cumple cada función. Cuando el número de CTID supera la decena, la vista rápida del panel web ya no basta; los nombres, recursos asignados y propósitos quedan dispersos en distintas pantallas. La falta de un registro centralizado genera confusión al planificar actualizaciones, migraciones o simplemente al revisar el estado del entorno. Además, la documentación manual tiende a quedar desactualizada tan pronto como se crea, elimina o modifica un contenedor.
Causa
Los principales factores que provocan este desorden son:
- Dependencia exclusiva de la UI – La descripción del contenedor se guarda en la base de datos de Proxmox, pero no se expone automáticamente en listados de línea de comandos.
- Ausencia de proceso de generación automática – Sin un script que consulte
pctde forma periódica, cualquier cambio manual no se refleja en los documentos estáticos. - Falta de estandarización – Cada administrador usa su propio formato (CSV, notas en Wiki, etc.), lo que dificulta la consolidación.
- Limitaciones de herramientas de terceros – Herramientas como
pve-managero la API REST pueden requerir configuración adicional y credenciales, lo que no siempre está disponible en hosts minimalistas.
Solución
Implementar un script autónomo que:
- Consulte la lista de contenedores con
pct listy, para cada CTID, recupere atributos relevantes (hostname,status,cores,memory,rootfs,netX,onboot,description). - Normalice valores ausentes (por ejemplo, sin descripción o sin interfaz de red) sustituyéndolos por “–”.
- Genere una tabla Markdown con encabezados claros y orden predecible (por ejemplo, ordenado por CTID o por orden de autostart).
- Se ejecute con privilegios de root en el propio nodo, evitando dependencias externas y manteniendo la consistencia con la fuente de datos oficial.
Enfoque reutilizable
- Uso de la biblioteca estándar – No se necesita instalar paquetes adicionales;
subprocess,jsonypathlibson suficientes. - Salida idempotente – Cada ejecución sobrescribe el mismo archivo (
homelab.md), garantizando que la documentación siempre refleje el estado actual. - Extensibilidad – El script puede ampliarse para incluir VMs (
qm) o para exportar a otros formatos (HTML, CSV) sin romper la lógica principal. - Integración con cron – Programar la ejecución cada noche o al iniciar el host mantiene la información siempre fresca.
Cuándo aplicar esta solución
- Entornos con >5 contenedores LXC – Cuando la gestión manual empieza a ser impráctica.
- Homelabs que usan la descripción del contenedor como fuente de verdad – Ideal si ya estableciste la práctica de definir el propósito con
pct set <id> --description "…". - Equipos que prefieren archivos estáticos – Si tu flujo de trabajo incluye repositorios Git para documentación, un Markdown versionado encaja perfectamente.
- No aplicable cuando se necesita un inventario en tiempo real para herramientas de monitoreo externas; en ese caso la API de Proxmox sería más adecuada.
Código
#!/usr/bin/env python3
import subprocess, json, pathlib, datetime
def run_cmd(cmd):
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
return result.stdout.strip()
def get_containers():
raw = run_cmd("pct list --output json")
return json.loads(raw)
def get_detail(ctid):
info = json.loads(run_cmd(f"pct config {ctid} --output json"))
status = run_cmd(f"pct status {ctid} | awk '{{print $2}}'")
# CPU/RAM
cores = info.get("cores", "–")
mem = info.get("memory", "–")
# Storage
rootfs = info.get("rootfs", "–")
# Network (first interface)
net = info.get("net0", "–")
# Autostart
onboot = info.get("onboot", "no")
order = info.get("startup", "")
autostart = f"yes (order {order})" if onboot == "yes" else "no"
# Description
purpose = info.get("description", "–")
return {
"CTID": ctid,
"Hostname": info.get("hostname", "–"),
"Status": status,
"CPU/RAM": f"{cores} Cores / {mem} MB",
"Storage": rootfs,
"Network": net,
"Autostart": autostart,
"Purpose": purpose
}
def generate_md(containers):
header = ["CTID","Hostname","Status","CPU/RAM","Storage","Network","Autostart","Purpose"]
lines = ["| " + " | ".join(header) + " |",
"|---|"*len(header) + "|"]
for c in sorted(containers, key=lambda x: int(x["CTID"])):
row = [c[h] for h in header]
lines.append("| " + " | ".join(row) + " |")
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
content = f"# Homelab – LXC Containers\nGenerated {timestamp}\n\n" + "\n".join(lines) + "\n"
return content
def main():
md_path = pathlib.Path("/root/homelab.md")
containers = [get_detail(ct["ctid"]) for ct in get_containers()]
md_path.write_text(generate_md(containers))
print(f"Inventario escrito en {md_path}")
if __name__ == "__main__":
main()
Verificación
- Ejecuta el script manualmente con
sudo ./inventario_lxc.py. - Abre
homelab.mdy comprueba que la tabla contiene una fila por cada CTID, con “–” en los campos vacíos. - Modifica la descripción de un contenedor (
pct set 105 --description "Nuevo propósito"), vuelve a ejecutar el script y verifica que la columna Purpose se actualiza sin necesidad de editar el archivo. - Programa una tarea cron (
0 2 * * * /usr/local/bin/inventario_lxc.py) y revisa que el archivo se renueva cada madrugada.
Notas adicionales
- Permisos – El script necesita privilegios de root porque
pctsolo está disponible para el usuario administrador. - Primer interfaz – Actualmente solo se muestra
net0. Si usas múltiples NICs, extiendeget_detailpara iterar sobrenet*. - Compatibilidad con QEMU – Añadir soporte para
qmes tan sencillo como replicar la lógica deget_detailusandoqm config. Mantén la tabla separada o agrega columnas específicas para VMs según tu política de documentación. - Control de cambios – Versiona
homelab.mden un repositorio Git; cada commit muestra la evolución del inventario y facilita auditorías. - Escalado – En despliegues con cientos de contenedores, considera paginar la tabla o generar varios archivos por nodo para evitar archivos Markdown demasiado pesados.