Anatomía del sistema, interfaz web y herramientas CLI

Por: Artiko
proxmoxproxmox-vepmxcfsclipveshapiinterfaz-webdiagnostico

Anatomía del sistema, interfaz web y herramientas CLI

Tienes el nodo instalado y entras por primera vez a https://<IP>:8006. La interfaz responde, el árbol de la izquierda muestra tu nodo, y a partir de ahí todo parece obvio hasta el día en que algo falla: la web deja de responder, mkdir sobre /etc/pve devuelve Read-only file system, las estadísticas de las VMs se quedan congeladas, o un script que funcionaba en la GUI se comporta distinto por CLI.

Ninguna de esas situaciones se resuelve mirando botones. Se resuelven sabiendo qué proceso hace qué, dónde escribe, de qué depende y con qué comando se interroga. Proxmox VE no es una caja negra: es un Debian 13 Trixie con una decena de demonios Perl y Rust muy bien delimitados, un filesystem FUSE replicado montado en /etc/pve, y una API REST que es la única puerta de entrada real. La interfaz web es un cliente de esa API. Los comandos qm, pct, pvesm y compañía son clientes de esa misma API. Entender eso cambia por completo cómo depuras.

En el capítulo 1 instalaste el nodo y dejaste los repositorios en orden. Este capítulo es el mapa del territorio: al terminarlo vas a poder señalar en un systemctl --failed qué se rompió, leer un fichero de /etc/pve sabiendo quién lo escribe, decidir si abres noVNC o SPICE, y descubrir cualquier endpoint de la API sin salir de la terminal.

La versión de referencia es Proxmox VE 9.2 sobre Debian 13 Trixie, con la documentación 9.2.4 compilada el 4 de agosto de 2026.

Mapa de servicios: quién hace qué en un nodo

Un nodo Proxmox VE no tiene un “servicio Proxmox”. Tiene un conjunto de unidades systemd con responsabilidades separadas y con un modelo de privilegios deliberado: lo que está expuesto a la red corre sin privilegios, y lo que necesita privilegios solo escucha en loopback.

flowchart TB
    BR["Navegador o cliente de API"]
    SPC["Cliente SPICE virt-viewer"]
    subgraph node["Nodo Proxmox VE"]
        PXY["pveproxy 8006 HTTPS - usuario www-data"]
        SPX["spiceproxy 3128 - usuario www-data"]
        PVD["pvedaemon 127.0.0.1:85 - usuario root"]
        subgraph locales["Demonios locales"]
            PST["pvestatd"]
            PSC["pvescheduler"]
            PFW["pve-firewall"]
            CRM["pve-ha-crm y pve-ha-lrm"]
            QEV["qmeventd"]
        end
        PCL["pve-cluster o pmxcfs - FUSE en /etc/pve"]
        COR["corosync 5405-5412 UDP"]
    end
    BR --> PXY
    SPC --> SPX
    PXY -->|"operaciones privilegiadas"| PVD
    PXY -->|"peticiones a otros nodos"| COR
    PVD --> PCL
    locales --> PCL
    PCL <--> COR

La tabla con la descripción oficial de cada demonio:

ServicioUnidad systemdUsuarioPuertoQué hace
pveproxypveproxy.servicewww-data8006/TCP HTTPSExpone toda la API. Corre con permisos muy limitados y reenvía a pvedaemon local lo que requiere privilegios. Las peticiones dirigidas a otros nodos se reenvían automáticamente a esos nodos
pvedaemonpvedaemon.serviceroot127.0.0.1:85Expone la misma API pero solo en loopback. Corre como root y tiene permiso para todas las operaciones privilegiadas
pvestatdpvestatd.servicerootConsulta a intervalos regulares el estado de VMs, storages y contenedores, y difunde el resultado a todos los nodos del cluster
pve-cluster (pmxcfs)pve-cluster.servicerootMonta el filesystem FUSE del cluster en /etc/pve
pveschedulerpvescheduler.servicerootLanza los jobs programados: replicación y vzdump. Para los de backup lee su configuración de /etc/pve/jobs.cfg
pve-firewallpve-firewall.servicerootCompila y aplica las reglas del firewall distribuido
pve-ha-crmpve-ha-crm.servicerootCluster Resource Manager. Solo el nodo que gana el manager lock actúa como master
pve-ha-lrmpve-ha-lrm.servicerootLocal Resource Manager: ejecuta en cada nodo lo que ordena el CRM
spiceproxyspiceproxy.servicewww-data3128/TCPProxy HTTP que reenvía las peticiones CONNECT del cliente SPICE a la VM correcta
qmeventdqmeventd.servicerootsocketEscucha el socket QMP de QEMU esperando eventos SHUTDOWN. Al desconectarse el cliente ejecuta /usr/sbin/qm cleanup para limpiar tap devices y vgpus huérfanos
corosynccorosync.serviceroot5405-5412/UDPMotor de mensajería y quorum del cluster. Es upstream, no un componente Proxmox

Hay además unidades auxiliares del paquete pve-manager que conviene reconocer en un log: pve-daily-update.service y su timer (refresco diario del índice de paquetes, de la base de datos de plantillas y renovación ACME, con retardo aleatorio); pve-guests.service, que en el arranque espera a que haya quorum y entonces levanta los guests con onboot; pvenetcommit.service, que aplica /etc/network/interfaces.new; pvebanner.service, que escribe la URL de la web UI en /etc/issue; y pve-firewall-commit.service y pve-sdn-commit.service.

Gotcha de diseño: pvedaemon no aparece en ninguna lista de puertos expuestos porque escucha solo en 127.0.0.1:85. Si intentas atacarlo desde fuera no hay nada que atacar; toda la superficie remota es pveproxy. Por eso subir workers o restringir IPs se hace sobre pveproxy, no sobre pvedaemon.

pmxcfs: el filesystem de cluster montado en /etc/pve

Esta es la pieza que más confunde al llegar desde otros hipervisores. /etc/pve no es un directorio del disco. Es un punto de montaje FUSE servido por pmxcfs, definido oficialmente así:

“The Proxmox Cluster file system (‘pmxcfs’) is a database-driven file system for storing configuration files, replicated in real time to all cluster nodes using corosync.”

Lo que se ve como ficheros de texto son en realidad filas de una base de datos SQLite en /var/lib/pve-cluster/config.db, con una copia completa en RAM. Esa copia en RAM impone el límite duro que hay que conocer: 128 MiB como máximo, suficiente para la configuración de varios miles de VMs, pero no para meter ahí ficheros grandes.

Las propiedades que te importan en el día a día:

Uno. Todo lo que escribas en /etc/pve en un nodo aparece en el resto del cluster en tiempo real, vía corosync. No hay que copiar nada a mano ni montar NFS para compartir configuración.

Dos. Hay comprobaciones de consistencia fuertes. Es pmxcfs quien impide que existan dos VMID iguales en el cluster.

Tres. Provee un mecanismo de bloqueo distribuido (los locks viven en /etc/pve/priv/lock/), que es lo que evita que dos nodos hagan la misma operación sobre el mismo guest.

Cuatro y la más importante: cuando el nodo pierde el quorum, pmxcfs monta /etc/pve en solo lectura. Los guests que ya estaban corriendo siguen corriendo; lo que se bloquea es toda escritura de configuración: no puedes crear, arrancar ni migrar guests, ni editar nada. Si alguna vez ves mkdir: cannot create directory ...: Read-only file system sobre /etc/pve, el problema no es el disco, es el quorum. El diagnóstico es siempre el mismo:

pvecm status        # mirar la linea "Quorate:"
systemctl status pve-cluster corosync
journalctl -b -u pve-cluster -u corosync

El detalle completo de quorum, votos y QDevice está en el capítulo 11; aquí basta con saber que el síntoma es un filesystem de solo lectura.

Limitaciones POSIX deliberadas

pmxcfs no pretende ser un filesystem general. Renuncia a cosas a propósito, y conviene saberlo antes de escribir un script que falle de forma rara:

  • No soporta enlaces simbólicos.
  • No se puede renombrar un directorio no vacío — así se garantiza la unicidad de los VMID.
  • Los permisos son por ruta y no se pueden cambiar. Todos los ficheros son de root con lectura para el grupo www-data; lo que cuelga de /etc/pve/priv/ es solo root.
  • O_EXCL no es atómico (como en implementaciones antiguas de NFS), y la creación con O_TRUNC tampoco lo es, por restricción de FUSE.

Traducción práctica: no uses /etc/pve como almacén de locks de tus propios scripts, y no des por hecho que un chmod va a funcionar ahí.

Inventario de /etc/pve y los enlaces virtuales del nodo local

Estos son los ficheros que vas a abrir una y otra vez. Los agrupo por dominio.

RutaContenido
storage.cfgDefinición de todos los storages del cluster
user.cfgUsuarios, grupos y ACL
domains.cfgDominios de autenticación (realms)
datacenter.cfgAjustes cluster-wide: teclado, proxy, consola por defecto, estilo de tags, texto de consentimiento en base64
jobs.cfgJobs programados de backup que ejecuta pvescheduler
vzdump.cronProgramación de backups cluster-wide
corosync.confConfiguración del cluster (la copia maestra)
status.cfgServidores de métricas externos
ceph.confConfiguración de Ceph
firewall/cluster.fw, firewall/<NAME>.fw, firewall/<VMID>.fwReglas de firewall cluster-wide, por nodo y por guest
ha/resources.cfg y ha/rules.cfgRecursos gestionados por HA y reglas de afinidad
ha/manager_status y ha/crm_commandsEstado de los servicios HA en JSON y operaciones en curso
sdn/*Configuración de Software Defined Networking
virtual-guest/cpu-models.confModelos de CPU personalizados
nodes/<NAME>/configConfig del nodo: wakeonlan, acme, startall-onboot-delay, ballooning-target
nodes/<NAME>/qemu-server/<VMID>.confConfiguración de cada VM KVM
nodes/<NAME>/lxc/<VMID>.confConfiguración de cada contenedor LXC
nodes/<NAME>/pve-ssl.pem y .keyCertificado y clave del nodo, firmados por la CA del cluster
nodes/<NAME>/pveproxy-ssl.pem y .keyCertificado alternativo opcional del proxy
pve-root-ca.pem, pve-www.key, authkey.pubCA pública del cluster, clave de tokens CSRF y clave pública del sistema de tickets

Y el subárbol privado, legible solo por root:

RutaContenido
priv/shadow.cfgPasswords del realm PVE
priv/tfa.cfgConfiguración de doble factor, en Base64
priv/token.cfgSecretos de los API tokens
priv/authkey.key y priv/pve-root-ca.keyClave privada del sistema de tickets y de la CA del cluster
priv/authorized_keys y priv/known_hostsClaves SSH y verificación de los miembros del cluster
priv/lock/*Locks distribuidos cluster-wide
priv/ceph*Claves y capabilities de Ceph
priv/storage/<STORAGE-ID>.pwPasswords de storage en texto plano

Advertencia: priv/storage/<ID>.pw guarda las contraseñas de los backends remotos (CIFS, PBS, iSCSI con CHAP) en texto plano. Cualquier copia de /etc/pve o de config.db que saques del nodo lleva esas credenciales dentro. Trátalas como un secreto de primera categoría, sobre todo cuando prepares el material del capítulo 10.

Los enlaces virtuales al nodo local

pmxcfs no soporta symlinks, pero sí expone tres atajos “virtuales” que apuntan siempre al nodo donde estás:

EnlaceApunta a
/etc/pve/localnodes/<hostname-local>
/etc/pve/qemu-servernodes/<hostname-local>/qemu-server/
/etc/pve/lxcnodes/<hostname-local>/lxc/

Por eso qm config 100 funciona leyendo /etc/pve/qemu-server/100.conf mientras el fichero real vive en /etc/pve/nodes/pve1/qemu-server/100.conf. Y por eso, cuando un guest “desaparece” de un nodo, casi siempre es que su .conf está bajo el directorio de otro nodo.

Ficheros de estado para depuración

Son ficheros JSON generados al vuelo, no configuración: /etc/pve/.version lleva la versión de cada fichero para detectar modificaciones, /etc/pve/.members la información de los miembros del cluster, /etc/pve/.vmlist el inventario de todas las VMs y contenedores, /etc/pve/.clusterlog los últimos 50 eventos del cluster y /etc/pve/.rrd las últimas métricas.

Y el interruptor de logging verboso de pmxcfs:

echo "1" > /etc/pve/.debug          # activar
journalctl -f -u pve-cluster
echo "0" > /etc/pve/.debug          # desactivar

Recuperación con pmxcfs: config.db, modo local y mover guests

Tres escenarios de rescate que hay que tener memorizados.

a) El hardware del nodo murió y quieres su configuración

La base de datos es un fichero. Se puede trasplantar:

  1. Copia /var/lib/pve-cluster/config.db desde el host averiado al nuevo.
  2. Para el servicio: systemctl stop pve-cluster.
  3. Reemplaza el fichero config.db con permisos 0600.
  4. Ajusta el hostname y /etc/hosts para que coincidan con los del host perdido.
  5. Reinicia y comprueba.

Para el caso contrario —quitar la configuración de cluster de un nodo— la documentación recomienda reinstalar el nodo, porque es la única forma de garantizar que se destruyen todos los secretos y claves SSH del cluster.

b) Sin quorum y necesitas escribir en /etc/pve

Este es el salvavidas. El binario pmxcfs acepta cuatro flags —-d/--debug para mensajes de depuración, -f/--foreground para no demonizar, -h/--help y, el que importa aquí, -l/--local, que ignora corosync.conf y fuerza el quorum:

systemctl stop pve-cluster
pmxcfs -l                 # /etc/pve montado local y escribible, sin cluster
# ... arreglar corosync.conf o lo que toque ...
killall pmxcfs
systemctl start pve-cluster

Recuerda que hay dos copias de corosync.conf en cada nodo: /etc/pve/corosync.conf, que es la maestra y vive en pmxcfs, y /etc/corosync/corosync.conf, que es la copia local que corosync lee realmente al arrancar y que PVE genera a partir de la primera. El procedimiento oficial de edición segura pasa por una copia de trabajo y por subir el config_version:

cp /etc/pve/corosync.conf /etc/pve/corosync.conf.new
# editar el .new y subir config_version
cp /etc/pve/corosync.conf /etc/pve/corosync.conf.bak
mv /etc/pve/corosync.conf.new /etc/pve/corosync.conf

c) Recuperar los guests de un nodo caído

Solo funciona si los guests usan exclusivamente storage compartido (o están gestionados por HA). Como la configuración de un guest es un fichero dentro de un directorio por nodo, “mover” un guest de nodo es literalmente mover el fichero:

mv /etc/pve/nodes/node1/qemu-server/100.conf /etc/pve/nodes/node2/qemu-server/
mv /etc/pve/nodes/node1/lxc/200.conf         /etc/pve/nodes/node2/lxc/
qm start 100
pct start 200

Advertencia literal de la documentación: “Ensure that the source node is completely powered off before performing this operation”, porque de lo contrario se producen violaciones de lock. Si el nodo origen vuelve a arrancar con el guest todavía “suyo” y el storage es compartido, tienes doble arranque sobre el mismo disco y corrupción. Los guests con recursos exclusivamente locales no se recuperan así.

Configurar pveproxy: ALLOW_FROM, LISTEN_IP, cifrados y workers

Un solo fichero controla pveproxy y spiceproxy: /etc/default/pveproxy.

Control de acceso por IP, con sintaxis estilo apache2:

ALLOW_FROM="10.0.0.1-10.0.0.5,192.168.0.0/22"
DENY_FROM="all"
POLICY="allow"

all es alias de 0/0 y ::/0, y la sintaxis de direcciones es cualquiera que entienda Net::IP. La política por defecto es allow. La resolución cuando hay coincidencias es esta:

CoincidenciaPOLICY=denyPOLICY=allow
Solo Allowallowallow
Solo Denydenydeny
Ningunadenyallow
Allow y Deny a la vezdenyallow

IP de escucha. LISTEN_IP admite IPv4 (192.0.2.1), IPv6 (2001:db8:85a3::1) y link-local siempre que se indique la interfaz (fe80::c463:8cff:feb9:6a4e%vmbr0).

Advertencia oficial: “It is not recommended to set LISTEN_IP on clustered systems.” Los nodos del cluster necesitan acceso a pveproxy para comunicarse entre sí; si lo atas a una sola IP, te arriesgas a que dejen de verse.

Cifrados TLS. Estos son los valores por defecto reales, no un ejemplo inventado:

CIPHERS="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
CIPHERSUITES="TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
HONOR_CIPHER_ORDER=0

CIPHERS aplica a TLS 1.2 y anteriores; CIPHERSUITES a TLS 1.3. SSLv2 y SSLv3 están deshabilitados incondicionalmente. Para desactivar versiones concretas: DISABLE_TLS_1_2=1 o DISABLE_TLS_1_3=1.

Otros ajustes útiles del mismo fichero:

DHPARAMS="/path/to/dhparams.pem"      # PEM propio; por defecto se usan los built-in skip2048
TLS_KEY_FILE="/secrets/pveproxy.key"  # mover la clave privada fuera de /etc/pve
COMPRESSION=0                          # desactivar gzip HTTP para mitigar BREACH
PROXY_REAL_IP_HEADER="X-Forwarded-For"
PROXY_REAL_IP_ALLOW_FROM="192.168.0.2"
MAX_WORKERS=5

Sobre los certificados: pveproxy usa /etc/pve/local/pveproxy-ssl.pem y /etc/pve/local/pveproxy-ssl.key si existen, y si no cae a /etc/pve/local/pve-ssl.pem y /etc/pve/local/pve-ssl.key. La clave privada no puede llevar passphrase. Ojo: la integración ACME incluida no respeta TLS_KEY_FILE.

Sobre los workers: el valor por defecto es 3, con rango válido de 1 a 127. El mismo ajuste existe en /etc/default/pvedaemon. Si tienes automatización intensiva contra la API (Terraform, Ansible, exporters) y ves timeouts, es el primer número que hay que subir.

Los cambios se aplican con systemctl restart pveproxy.service spiceproxy.service. Gotcha caro: un restart de pveproxy corta las consolas y shells abiertas, porque mata los workers de larga duración. Usa reload siempre que el cambio lo permita, y programa el restart en ventana de mantenimiento.

Puertos usados por Proxmox VE

Lista oficial del Admin Guide, sección 13.12. Es la que hay que llevar al firewall perimetral:

ServicioPuertoProtocolo
Interfaz web8006TCP, HTTP/1.1 sobre TLS
Consola web VNC5900-5999TCP, WebSocket
SPICE proxy3128TCP
sshd (acciones de cluster)22TCP
rpcbind111UDP
sendmail25TCP, saliente
Tráfico de cluster corosync5405-5412UDP
Live migration (memoria de VM y discos locales)60000-60050TCP

Dos notas que ahorran horas. La primera: pvedaemon no está en la lista porque escucha solo en 127.0.0.1:85. La segunda: el wiki pve.proxmox.com/wiki/Ports está desactualizado desde mayo de 2022 y todavía muestra corosync como 5404, 5405 UDP; desde PVE 6 el rango correcto es 5405-5412 UDP. Si copias reglas de firewall de un tutorial viejo, el cluster parpadeará.

Rutas y directorios que hay que memorizar

RutaQué es
/etc/pve/pmxcfs: todo lo replicado en el cluster
/var/lib/pve-cluster/config.dbBase de datos SQLite de pmxcfs
/var/lib/vz/Storage local por defecto, con template/iso/, template/cache/ y dump/
/var/log/pve/tasks/Logs de tareas: el Task History de la GUI
/var/log/pveproxy/access.logAccesos HTTP a la GUI y a la API
/etc/default/pveproxyConfig de pveproxy y spiceproxy
/etc/default/pvedaemonConfig de pvedaemon, incluido MAX_WORKERS
/etc/network/interfacesRed del host
/etc/network/interfaces.newCambios de red pendientes, los aplica pvenetcommit al reiniciar
/etc/corosync/corosync.confCopia local que lee corosync
/etc/kernel/cmdline y /etc/default/grubLínea de comandos del kernel, según se use proxmox-boot-tool o GRUB

El storage que ya tienes tras instalar

Sin haber configurado nada, /etc/pve/storage.cfg trae esto:

dir: local
        path /var/lib/vz
        content iso,vztmpl,backup

# default image store on LVM based installation
lvmthin: local-lvm
        thinpool data
        vgname pve
        content rootdir,images

# default image store on ZFS based installation
zfspool: local-zfs
        pool rpool/data
        sparse
        content images,rootdir

Es decir: si instalaste con ext4 o xfs tienes local + local-lvm; si instalaste con ZFS tienes local + local-zfs. Y los storages de tipo fichero siempre usan el mismo layout de subdirectorios: images/<VMID>/ para discos de VM, template/iso/ para ISOs, template/cache/ para plantillas de contenedor, dump/ para backups, snippets/ para snippets y hook scripts, e import/ para OVAs y discos importables.

Una ruta real generada por el sistema tiene esta pinta: /var/lib/vz/images/100/vm-100-disk10.raw. El modelo completo de plugins de storage y el resto de backends están en el capítulo 7.

La interfaz web: cuatro regiones y nada más

La GUI está escrita en ExtJS 7.x y traducida a más de veinte idiomas. Lo relevante es que, gracias a pmxcfs, “you can connect to any node to manage the entire cluster… There is no need for a dedicated manager node.” No hay vCenter que instalar ni appliance de gestión que mantener.

flowchart TB
    H["HEADER - logo version buscador y botones Documentation / Create VM / Create CT / User Menu"]
    RT["RESOURCE TREE a la izquierda - Server Folder Pool y Tag View"]
    CP["CONTENT PANEL al centro - configuracion y estado del objeto seleccionado"]
    L["LOG PANEL abajo - tareas recientes de TODOS los nodos"]
    H --> RT
    H --> CP
    RT --> CP
    CP --> L

Header. A la izquierda, el logo, la versión actual del nodo y una barra de búsqueda que suele ser más rápida que navegar el árbol. A la derecha, cuatro botones: Documentation (abre la doc de referencia), Create VM, Create CT y el User Menu. Dentro del menú de usuario están My Settings, los atajos self-service a TFA y Password, el idioma, el tema de color y el logout.

My Settings contiene tres cosas útiles: Dashboard Storages (qué storages cuentan para el total del resumen del datacenter; si no marcas ninguno se suman todos), el botón para resetear todos los layouts de la GUI a su valor por defecto, y los ajustes de xterm.js (Font-Family, Font-Size, Letter Spacing, Line Height).

Content panel del Datacenter. Es donde vive todo lo cluster-wide: Search, Summary, Cluster (crear o unirse), Storage, Backup (los jobs son cluster-wide: da igual en qué nodo estén los guests), Replication, Permissions (usuarios, grupos, API tokens, LDAP, MS-AD y 2FA), HA, ACME, Firewall, Metric Server (InfluxDB y Graphite), Notifications, Support y Guest Resources/Hardware, esta última nueva en 9.2 para gestionar modelos de CPU personalizados. En Options están los ajustes por defecto del datacenter, incluidos Tag Style Override, User Tag Access, Registered Tags y Consent Text.

Content panel del Nodo. En la cabecera: Reboot, Shutdown, Shell (despliega noVNC, SPICE y xterm.js), Bulk Actions (Bulk Start, Bulk Shutdown, Bulk Migrate) y Help. En 9.2, todas las acciones bulk exponen el número de workers paralelos, no solo la migración; dejarlo vacío usa el nuevo valor por defecto auto. Las secciones son Summary, Notes (en Markdown), Shell, System (red, DNS, hora, syslog y panel de servicios), Updates con su subpanel Repositories, Firewall, Disks, Ceph (solo si está instalado), Replication, Task History y Subscription.

Content panel de un guest. Casi idéntico para VMs y contenedores: Summary con Notes, Console, Hardware en VMs frente a Resources, Network y DNS en contenedores, Options, Task History, Monitor (solo VMs: la interfaz interactiva QMP/HMP con el proceso KVM), Backup, Replication, Snapshots, Firewall y Permissions.

Log panel. Cada acción larga —crear una VM, hacer un backup— se ejecuta en segundo plano como una task, con su propio fichero de log. Doble click sobre la entrada abre el log y permite abortar la tarea en ejecución. El panel muestra tareas de todos los nodos en tiempo real; las terminadas se retiran para no ensuciar, pero siguen accesibles en Nodo → Task History. Las acciones muy cortas que solo difunden mensajes al cluster aparecen en el panel Cluster log. Novedad de 9.2: hay un botón Download para bajar el log de una tarea sin abrir el visor completo.

El árbol de recursos no es un solo árbol: es el mismo inventario agrupado de cuatro maneras.

VistaAgrupa por
Server View (por defecto)Todos los tipos de objeto, agrupados por nodo
Folder ViewTodos los tipos de objeto, agrupados por tipo
Pool ViewVMs y contenedores, agrupados por pool
Tag ViewVMs y contenedores, agrupados por tag

Los tipos de objeto que aparecen son Datacenter, Node, Guest (VMs, contenedores y plantillas), Storage y Pool.

Tags. Se definen por guest y hoy solo tienen valor informativo: no cambian comportamiento, solo agrupan y colorean. Se ven en el árbol y en la línea de estado del guest, y por CLI se ponen separados por punto y coma:

qm set 100 --tags 'produccion;web;chile'

El color se deriva del texto de forma determinista, salvo que lo sobrescribas en Datacenter → Options → Tag Style Override, que también tiene equivalente CLI —pvesh set /cluster/options --tag-style color-map=example:000000:FFFFFF pinta el tag example con fondo #000000 y texto #FFFFFF—.

Quién puede poner tags se controla en Datacenter → Options → User Tag Access, con cuatro valores: free (sin restricción, el valor por defecto), list (solo tags de una lista predefinida), existing (como list más los tags ya existentes) y none (no se permiten tags). Por defecto, quien tenga VM.Config.Options sobre /vms/ID puede poner cualquier tag; un usuario con Sys.Modify sobre / siempre puede poner o quitar cualquiera. Existe además una lista de Registered Tags reservada a usuarios con Sys.Modify sobre /.

Consent banner. Si necesitas mostrar un aviso legal antes del login, se configura en Datacenter → Options → Consent Text. Si está vacío no se muestra nada. El texto se guarda como cadena base64 dentro de /etc/pve/datacenter.cfg, lo cual es útil saberlo cuando gestiones ese fichero por automatización.

Las tres consolas: noVNC, xterm.js y SPICE

No son tres nombres para lo mismo. Son tres protocolos con capacidades distintas.

TipoQué esCuándo usarlo
noVNCCliente VNC sobre HTML5/WebSocket embebido en el navegador. Es la consola gráfica por defecto y el reemplazo del viejo applet JavaVer el arranque y el firmware de una VM, instalar un SO, cualquier cosa gráfica
xterm.jsEmulador de terminal puramente de texto en JavaScript, servido vía termproxy. Copiar y pegar nativos del navegador, scrollback y tipografía configurable desde My SettingsShell del nodo, consola serie de una VM, pct console
SPICEProtocolo remoto completo: vídeo, teclado, ratón, audio, redirección USB y carpetas compartidas. Requiere cliente externo virt-viewer: la GUI descarga un fichero .vv. Pasa por spiceproxy en el puerto 3128/TCPEscritorios virtuales, multi-monitor, audio, pasar un pendrive al guest
flowchart TD
    A["Que necesitas hacer"] --> B{"Sistema grafico o instalacion de SO"}
    B -->|"Si"| C{"Necesitas audio multimonitor o redireccion USB"}
    B -->|"No"| D{"Shell del nodo o consola serie"}
    C -->|"Si"| E["SPICE con virt-viewer por el puerto 3128"]
    C -->|"No"| F["noVNC en el navegador"]
    D -->|"Si"| G["xterm.js"]
    D -->|"No"| F

El rango 5900-5999/TCP sobre WebSocket es el que usa la consola web VNC. Para que xterm.js sirva como consola serie de una VM hay que añadirle un puerto serie y usarlo como display; y con contenedores tienes además acceso directo desde la terminal del nodo, sin pasar por el navegador:

qm set <vmid> --serial0 socket --vga serial0
pct console 101       # consola del contenedor
pct enter 101         # shell dentro del contenedor

El patrón común del CLI: todo es un cliente de la misma API

Aquí está la idea que hace que el resto del curso encaje: todas las herramientas de línea de comandos son clientes de la misma API REST que usa la web UI. No hay funciones exclusivas de la GUI ni comandos que hagan magia por debajo. Lo que se puede hacer en la interfaz se puede hacer por CLI y por API, y al revés.

flowchart LR
    API["API REST /api2/json servida por pvedaemon y pveproxy"]
    GUI["Web UI en ExtJS"]
    PSH["pvesh - acceso directo a cualquier endpoint"]
    subgraph tools["Herramientas CLI por dominio"]
        QM["qm - VMs KVM"]
        PCT["pct - contenedores LXC"]
        PVEAM["pveam - plantillas de contenedor"]
        PVESM["pvesm - storage"]
        PVECM["pvecm - cluster y quorum"]
        PVEUM["pveum - usuarios roles ACL y tokens"]
        HAM["ha-manager - alta disponibilidad"]
        PVESR["pvesr - replicacion"]
        PVECEPH["pveceph - Ceph"]
        PVENODE["pvenode - nodo ACME y tareas"]
        VZD["vzdump y qmrestore - backup"]
    end
    GUI --> API
    PSH --> API
    tools --> API

Este es el índice oficial de man pages, que vale como catálogo cerrado de lo que existe:

Interfaz de línea de comandos: ha-manager(1), pct(1), pveam(1), pveceph(1), pvecm(1), pvenode(1), pveperf(1), pvesh(1), pvesm(1), pvesr(1), pvesubscription(1), pveum(1), qm(1), qmrestore(1), vzdump(1).

Demonios de servicio: pmxcfs(8), pve-firewall(8), pve-ha-crm(8), pve-ha-lrm(8), pvedaemon(8), pveproxy(8), pvescheduler(8), pvestatd(8), qmeventd(8), spiceproxy(8).

Ficheros de configuración: cpu-models.conf(5), datacenter.cfg(5), pct.conf(5), qm.conf(5).

Gotcha documental: pvereport no tiene man page publicadapve-docs/pvereport.1.html devuelve 404. Solo se menciona dentro del capítulo del firewall. No es que el comando no exista; es que su contrato no está documentado formalmente.

qm, pct, pveam y pvesm: los comandos del día a día

qm — máquinas virtuales KVM

# ciclo de vida
qm list                          # VMs de este nodo
qm create <vmid> [OPTIONS]
qm start|shutdown|stop|reboot|reset|suspend|resume|status|wait <vmid>
qm destroy <vmid>
qm unlock <vmid>                 # quitar un lock atascado

# configuracion y diagnostico
qm config <vmid>
qm set <vmid> [OPTIONS]
qm pending <vmid>                # cambios pendientes de reinicio
qm showcmd <vmid> --pretty       # la linea de comandos QEMU real
qm monitor <vmid>                # monitor QEMU interactivo

# discos, snapshots y clonado
qm disk import <vmid> <source> <storage>
qm disk move <vmid> <disk> [<storage>]
qm disk resize <vmid> <disk> <size>
qm disk rescan
qm snapshot|listsnapshot|rollback|delsnapshot <vmid> [<snapname>]
qm template <vmid>
qm clone <vmid> <newid>

qm shutdown es un apagado ACPI ordenado y qm stop un corte duro. La sintaxis moderna de discos es qm disk <verbo>; los antiguos qm importdisk, qm move_disk y qm resize siguen existiendo como alias deprecados.

El detalle de cada opción de hardware está en el capítulo 3, y las plantillas y cloud-init en el capítulo 5.

pct — contenedores LXC

pct list
pct create <vmid> <ostemplate> [OPTIONS]
pct start|shutdown|stop|reboot|suspend|resume <vmid>
pct status <vmid>
pct config <vmid>
pct set <vmid> [OPTIONS]
pct console|enter <vmid>
pct exec <vmid> [<extra-args>]
pct push <vmid> <file> <destination>
pct pull <vmid> <path> <destination>
pct df|fsck|fstrim|mount|unmount <vmid>
pct resize <vmid> <disk> <size>
pct snapshot|listsnapshot|rollback|delsnapshot <vmid> [<snapname>]
pct template <vmid>
pct clone <vmid> <newid>
pct restore <vmid> <ostemplate>
pct migrate <vmid> <target>
pct cpusets                       # ver el reparto de CPUs entre contenedores

Todo el modelo de contenedores, con unprivileged, features y mount points, está en el capítulo 6.

pveam — plantillas de contenedor

pveam update                                   # refrescar la base de datos de plantillas
pveam available --section system
pveam download local debian-13-standard_13.0-1_amd64.tar.zst
pveam list local
pveam remove <template_path>

Las plantillas caen en /var/lib/vz/template/cache/ dentro del storage local, y la base de datos se refresca a diario por pve-daily-update.

pvesm — storage

pvesm status                                 # estado de todos los storages
pvesm list <storage> [--vmid <VMID>] [--content <tipo>]
pvesm add <type> <storage> [OPTIONS]
pvesm set <storage> [OPTIONS]
pvesm remove <storage>
pvesm alloc <storage> <vmid> <filename> <size>
pvesm free <volume>
pvesm path <volume>                          # ruta real de un volid
pvesm extractconfig <volume>                 # sacar la config guardada en un backup
pvesm prune-backups <storage>
pvesm scan nfs|cifs|iscsi|lvm|lvmthin|zfs|pbs <args>

# ejemplos documentados de alta de backends
pvesm add dir   <STORAGE_ID> --path <PATH>
pvesm add nfs   <STORAGE_ID> --path <PATH> --server <SERVER> --export <EXPORT>
pvesm add lvm   <STORAGE_ID> --vgname <VGNAME>
pvesm add iscsi <STORAGE_ID> --portal <HOST[:PORT]> --target <TARGET>
pvesm set <STORAGE_ID> --content iso

pvesm path es el comando que más resuelve discusiones: te dice exactamente en qué fichero o dispositivo de bloque vive un volumen.

pvesh como explorador autodocumentado de la API

pvesh es el atajo directo a la API:

“The Proxmox VE management tool (pvesh) allows to directly invoke API function, without using the REST/HTTPS server. Only root is allowed to do that.

Los verbos mapean uno a uno con HTTP:

pvesh get    <api_path> [FORMAT_OPTIONS]   # GET
pvesh set    <api_path> [FORMAT_OPTIONS]   # PUT
pvesh create <api_path> [FORMAT_OPTIONS]   # POST
pvesh delete <api_path> [FORMAT_OPTIONS]   # DELETE
pvesh ls     <api_path> [FORMAT_OPTIONS]   # listar hijos
pvesh usage  <api_path> [--command <create|delete|get|set>] [--returns] [--verbose]

Opciones de formato:

OpciónDefaultEfecto
--output-format <json|json-pretty|text|yaml>textFormato de salida
--human-readable <boolean>1Renderizado humano
--noborder <boolean>0Sin bordes, solo en text
--noheader <boolean>0Sin cabeceras de columna, solo en text
--quietSuprimir la salida

Con text, pvesh humaniza los valores: los epoch Unix se convierten a ISO 8601, las duraciones a 1d 5h, los bytes a B/KiB/MiB/GiB/TiB/PiB y las fracciones a porcentaje (1.0 pasa a 100%). Para scripting, usa siempre --output-format json.

El patrón de descubrimiento es lo que convierte a pvesh en documentación viva: pvesh ls / para bajar por el árbol y pvesh usage <ruta> -v para leer los parámetros exactos.

pvesh ls /
pvesh ls /nodes
pvesh ls /nodes/pve1/qemu
pvesh usage /cluster/options -v
pvesh get /nodes
pvesh get /nodes/pve1/status
pvesh get /cluster/resources --type vm --output-format json-pretty
pvesh get /cluster/tasks
pvesh set /cluster/options -console html5

El complemento web es el API Viewer oficial en https://pve.proxmox.com/pve-docs/api-viewer/, que muestra el mismo esquema JSON Schema con el que se generan tanto la GUI como las man pages. Sobre esa base se construye toda la automatización del capítulo 14.

pvenode, pvereport, pveperf y proxmox-boot-tool

pvenode

Gestiona lo que es propio del nodo: certificados, ACME, configuración local, tareas y acciones masivas.

pvenode cert set certificate.crt certificate.key -force
pvenode acme account register default [email protected]
pvenode config set --acme domains=example.invalid
pvenode acme cert order
pvenode acme cert renew

pvenode config set -wakeonlan XX:XX:XX:XX:XX:XX
pvenode wakeonlan <node>

pvenode task list --errors --vmid 100
pvenode task log UPID:pve1:00010D94:001CA6EA:6124E1B9:vzdump:100:root@pam:
pvenode task status <upid>

pvenode startall --vms 100,101,102 --force
pvenode stopall
pvenode migrateall pve2 --vms 100,101,102 --with-local-disks
pvenode config set --startall-onboot-delay 10
pvenode config set --ballooning-target 90

Los tres últimos son los que más se usan en mantenimiento: startall/stopall respetan el onboot y el orden de arranque, y migrateall vacía un nodo antes de reiniciarlo.

Ese --ballooning-target es el objetivo del auto-ballooning que ejecuta pvestatd, y se explica en detalle en el capítulo 3.

pvereport

Un solo comando que vuelca a stdout el informe completo del sistema. Equivalente en la GUI: Nodo → Subscription → System Report.

pvereport
pvereport > /root/reporte-$(hostname)-$(date +%F).txt

Lo que recolecta cubre prácticamente todo lo que preguntarías en un ticket: pveversion -v, /etc/hosts, pvesubscription get y los ficheros de /etc/apt/; storage.cfg, pvesm status, df --human -T y proxmox-boot-tool status; qm list, pct list y todos los .conf de guests; ip -details address, /etc/network/interfaces y la configuración de SDN; los .fw del firewall más iptables-save; pvecm nodes, pvecm status, corosync.conf, ha-manager status y datacenter.cfg; y el detalle de bloques y volúmenes con lsblk, pvs, lvs, vgs, zpool status, ceph status y multipath -ll.

Dos recomendaciones operativas. La primera: ejecuta pvereport antes de cualquier upgrade mayor y guarda la salida; es la línea base contra la que comparar si algo cambia. La segunda, en negrita porque cuesta caro: el informe contiene información sensible —IPs, nombres de usuario en storage.cfg, topología de red completa—. Revísalo antes de pegarlo en un foro.

pveperf

Benchmark rápido del nodo, no una prueba de aceptación de I/O. La propia documentación lo describe como “just a very quick and general benchmark”.

pveperf [PATH]     # PATH por defecto: /

Umbrales de referencia de la man page:

MétricaReferencia oficial
REGEX/SECOND> 300000
BUFFERED READSdiscos modernos >= 40 MB/s
AVERAGE SEEK TIMESCSI rápidos < 8 ms; IDE/SATA entre 15 y 20 ms
FSYNCS/SECOND> 200

El valor que más información da es FSYNCS/SECOND: si está por los suelos, tienes un disco sin caché protegida o un SSD de consumo, y todo lo demás en el nodo irá mal.

proxmox-boot-tool

En instalaciones con / sobre ZFS o BTRFS, el arranque no lo gestiona GRUB directamente sino esta herramienta, que mantiene sincronizadas las particiones EFI:

proxmox-boot-tool status
proxmox-boot-tool format /dev/sda2
proxmox-boot-tool init /dev/sda2            # systemd-boot
proxmox-boot-tool init /dev/sda2 grub       # forzar GRUB
proxmox-boot-tool refresh
proxmox-boot-tool kernel list
proxmox-boot-tool kernel pin <version>
proxmox-boot-tool kernel unpin

Gotcha al reemplazar un disco: si proxmox-boot-tool status indica que los discos actuales usan GRUB, hay que pasar grub como modo a init, o el disco nuevo quedará inicializado con systemd-boot y el arranque será inconsistente.

Diagnóstico: journalctl, task history y comandos de cabecera

En Debian 13 todo va a journald por defecto: /var/log/syslog solo existe si instalaste rsyslog. Cada unidad de la tabla de servicios tiene su propio hilo de log, y ese es el primer sitio donde mirar: corosync para membresía, links y retransmits; pve-cluster para quorum y montaje de /etc/pve; pveproxy para peticiones HTTP; pvestatd para la recolección de métricas; pvescheduler para jobs; pve-ha-crm y pve-ha-lrm para decisiones de HA, lock y watchdog.

journalctl -b -u corosync -u pve-cluster
journalctl -f -u pveproxy
journalctl -b -p err                       # solo errores de este arranque
journalctl -k                              # kernel: watchdog, ZFS, vfio
journalctl --since "2026-08-09 03:00" --until "2026-08-09 05:00"
journalctl -u pveproxy --grep "authentication failure"
journalctl --disk-usage
journalctl --vacuum-time=14d

Las tareas de Proxmox no van a journald. Tienen su propio almacén, que es lo que alimenta el Task History de la GUI:

ls /var/log/pve/tasks/
cat /var/log/pve/tasks/index
pvesh get /nodes/localhost/tasks --limit 50
pvesh get /cluster/tasks

Y el bloque de comandos de cabecera, el que ejecutas cuando llegas a un nodo que no conoces y algo va mal:

# versiones y servicios
pveversion -v
systemctl status pve-cluster pveproxy pvedaemon pvestatd pvescheduler pve-firewall corosync
systemctl --failed

# cluster
pvecm status; pvecm nodes; corosync-cfgtool -s; ha-manager status

# storage
pvesm status; df -h; zpool status; lvs; vgs; pvs

# red
ip -br a; ip -br link; bridge link

# firewall
pve-firewall status
pve-firewall localnet
pve-firewall simulate --from outside --to host --dport 8006

pve-firewall simulate acepta --from y --to con los valores host, outside, vm<N>, ct<N> o <grupo>/<iface>; el default es outside para el origen y host para el destino. Es la forma de responder “¿por qué no entra el puerto 8006?” sin tocar reglas, y se trabaja a fondo en el capítulo 10.

Errores comunes y diagnóstico

SíntomaCausa probableDiagnóstico y solución
/etc/pve en solo lectura, Read-only file systemPérdida de quorumpvecm status y mirar Quorate:. Recuperar nodos; en emergencia pvecm expected 1 en el nodo superviviente
pvecm status no responde o pmxcfs no montapve-cluster caído o corosync.conf inválidosystemctl status pve-cluster corosync; journalctl -u pve-cluster -u corosync. Último recurso: parar el servicio y arrancar pmxcfs -l
La web no responde en :8006pveproxy caídosystemctl status pveproxy; journalctl -u pveproxy -n 100
Timeouts con automatización intensivaSolo 3 workers por defectoSubir MAX_WORKERS en /etc/default/pveproxy y /etc/default/pvedaemon, rango 1-127
Se pierden las consolas abiertasSe hizo restart de pveproxy en vez de reloadProgramar el restart en ventana de mantenimiento
Solo conectan clientes IPv6, o ningunonet.ipv6.bindv6only=1Quitar el sysctl o poner LISTEN_IP="0.0.0.0"
Los nodos dejan de verse tras tocar la config del proxySe puso LISTEN_IP en un sistema en clusterQuitar LISTEN_IP: no está recomendado en clusters
El navegador rechaza el certificadoCertificado autofirmado por la CA del clusterImportar /etc/pve/pve-root-ca.pem o configurar ACME con pvenode acme
Los logs muestran la IP del proxy inversoFalta el header de IP realPROXY_REAL_IP_HEADER="X-Forwarded-For" más PROXY_REAL_IP_ALLOW_FROM
Estadísticas de VMs y storages congeladaspvestatd caídosystemctl restart pvestatd
Los backups programados no se ejecutanpvescheduler caído o jobs.cfg erróneosystemctl status pvescheduler; revisar /etc/pve/jobs.cfg
Interfaces tap huérfanas tras apagar una VMqmeventd caídosystemctl status qmeventd; ejecuta /usr/sbin/qm cleanup al detectar SHUTDOWN
Los cambios de red no se aplicanSiguen en /etc/network/interfaces.newBotón Apply Configuration en la GUI, o reiniciar para que los aplique pvenetcommit
Un guest “desaparece” tras cambiar de nodoSu .conf vive bajo /etc/pve/nodes/<NODO>/Mover el fichero al nodo correcto, solo con el nodo origen totalmente apagado
Un nodo eliminado sigue apareciendoFalta borrar su directoriorm -rf /etc/pve/nodes/<NODENAME> después de pvecm delnode
pmxcfs se queda sin espacioLímite de 128 MiB de la copia en RAMBuscar ficheros grandes en /etc/pve: snippets, notas en Markdown, dumps pegados por error
VMID duplicadosUn mv manual mal hecho saltándose las comprobacionesRevisar /etc/pve/.vmlist
Un nodo aparece gris en la GUI pero responde por SSHCertificados desincronizados o pvestatd caídopvecm updatecerts --force && systemctl restart pvedaemon pveproxy pvestatd

Lo que queda operativo

Ya puedes nombrar el proceso responsable de cada síntoma —web caída es pveproxy, estadísticas congeladas es pvestatd, backups que no arrancan es pvescheduler, /etc/pve de solo lectura es pve-cluster sin quorum—, leer y editar cualquier fichero de /etc/pve sabiendo que se replica solo, rescatar un nodo sin quorum con pmxcfs -l, mover la configuración de un guest a otro nodo con el origen apagado, endurecer pveproxy con ALLOW_FROM y workers, elegir la consola correcta a la primera, descubrir cualquier endpoint con pvesh ls y pvesh usage -v, y generar un pvereport antes de tocar nada importante.

Con el mapa del sistema en la cabeza, lo siguiente es empezar a poner carga encima. En el capítulo 3 desarmamos una máquina virtual KVM pieza a pieza: machine type, firmware, modelo de CPU, memoria con ballooning, buses de disco y modos de caché, red, passthrough PCIe y el formato completo de /etc/pve/qemu-server/<VMID>.conf — incluidas las divergencias entre los valores por defecto del backend y los que pone la GUI, que son las que rompen los scripts.