Proceso de conversión de VM a contenedor LXC en Proxmox VE

Escenario: VM Linux sin Docker, acceso a la VM mediante usuario con sudo (sin login directo como root por SSH). Conversión manual usando solo la CLI de PVE.

Requisitos previos

  • Acceso SSH o consola a la VM de origen con un usuario que tenga sudo.

  • Acceso a la terminal del host PVE como root.

  • Conocer la versión exacta del sistema operativo de la VM de origen.

  • Espacio suficiente en el almacenamiento del host PVE para el contenedor.

  • Backup o snapshot de la VM de origen antes de empezar.

Proceso

Identificar el sistema operativo de la VM

En la VM de origen:

cat /etc/os-release

Anotar ID (ej. debian) y VERSION_ID (ej. 13).

Descargar la plantilla LXC en el host PVE

En el host PVE:

pveam update
pveam available --section system | grep -i "debian"
pveam download local debian-13-standard_13.6-1_amd64.tar.zst
pveam list local

Ajustar el nombre de la plantilla según la versión real de la VM.

Crear el contenedor LXC

pct create 201 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst \
  --hostname CTMigracion \
  --ostype debian \
  --arch amd64 \
  --cores 2 \
  --memory 4096 \
  --swap 0 \
  --storage Storage \
  --rootfs volume=Storage:24 \
  --unprivileged 0 \
  --password CLAVEDELROOTDELCONTENEDOR \
  --unprivileged 0 \
  --features nesting=1,keyctl=1,fuse=1 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.8.201/22,gw=192.168.8.1 \
  --nameserver 192.168.8.2 \
  --start false

⚠️ En este caso se creó un contenedor privilegiado (--unprivileged 0). Esto simplifica la migración porque el root interno es el root real del host (mapeo de identidad), y no requiere ajustes de propietarios después del rsync.

Verificar la configuración:

pct config 201

Si el contenedor queda en estado lock: mounted por un pct mount previo:

pct unmount 201

Sincronizar los archivos desde la VM al contenedor

Montar el rootfs del contenedor en el host (si no está montado)

pct mount 201

La ruta del rootfs es /var/lib/lxc/201/rootfs/.

Ejecutar rsync desde la VM de origen

En la VM de origen (con sudo):

sudo rsync -aAXv \
  --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/boot/*","/lib/modules/*"} \
  / root@PVE_HOST_IP:/var/lib/lxc/201/rootfs/

Lo que hace el rsync es copiar todo lo que no está incluido en esta lista.

Exclusiones obligatorias:

Directorio

Motivo

/dev, /proc, /sys

Sistemas de archivos virtuales; el contenedor los genera propios

/boot, /lib/modules

El contenedor comparte el kernel del host; no los necesita

/tmp, /run

Datos efímeros

/mnt, /media

Puntos de montaje (especialmente NFS/CIFS) que bloquearían el rsync

/lost+found

Específico del sistema de archivos de la VM

Verificar propietarios

Para contenedor privilegiado (mapeo de identidad):

ls -alh /var/lib/lxc/201/rootfs/ | head

Limpiar enlaces simbólicos de kernel rotos

rm -f /var/lib/lxc/201/rootfs/vmlinuz \
      /var/lib/lxc/201/rootfs/vmlinuz.old \
      /var/lib/lxc/201/rootfs/initrd.img \
      /var/lib/lxc/201/rootfs/initrd.img.old

Desmontar el rootfs

pct unmount 201

Verificar el tipo de contenedor (uid_map)

Arrancar el contenedor y comprobar el mapeo:

pct start 201
pct exec 201 -- cat /proc/self/uid_map

Salida

Significado

0 0 4294967295

Contenedor privilegiado (mapeo de identidad)

Ajustar la red dentro del contenedor

pct enter 201
  1. Verificar el nombre de la interfaz:

    ip addr show
    
  2. Editar la configuración de red según el gestor de la distro:

    • Ubuntu/Debian con netplan: /etc/netplan/*.yaml

    • Debian con ifupdown: /etc/network/interfaces

  3. Ajustar IP, gateway y DNS si es necesario (los parámetros de pct create ya aplican la red de Proxmox, pero la configuración interna puede sobrescribirla).

  4. Reiniciar el servicio de red o el contenedor.

Deshabilitar IPv6 (opcional)

Dentro del contenedor:

cat > /etc/sysctl.d/99-disable-ipv6.conf <<EOF
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
EOF

sysctl --system

Verificar:

ip addr show eth0

Si la dirección IPv6 persiste tras el reinicio, revisar la configuración de systemd-networkd o ifupdown dentro del contenedor.

Verificar servicios

Dentro del contenedor:

systemctl status ssh
systemctl status <otros-servicios>
journalctl -xe

Problemas comunes y solución:

Síntoma

Causa probable

Solución

Servicio falla al iniciar

Ruta a hardware o disco inexistente

Editar la configuración del servicio

Error de permisos

Contenedor no privilegiado sin chown

Aplicar chown -R 100000:100000

Red no funciona

Nombre de interfaz distinto (ens18 vs eth0)

Ajustar configuración de red interna

cloud-init o qemu-guest-agent con errores

Servicios específicos de VM

Deshabilitarlos: systemctl disable --now

Validación final

pct status 201
pct exec 201 -- id
pct exec 201 -- ip addr show eth0
pct exec 201 -- systemctl status ssh

Criterios de éxito:

  • pct status 201 → status: running

  • id → uid=0(root) gid=0(root)

  • eth0 con la IP esperada y sin IPv6 (si se deshabilitó)

  • SSH y servicios críticos activos

Notas y advertencias

  • No copiar /boot ni /lib/modules: el contenedor no usa kernel propio.

  • Contenedor privilegiado vs no privilegiado:

    • Privilegiado: migración más simple (sin chown), pero menor aislamiento de seguridad.

    • No privilegiado: requiere chown -R 100000:100000, mayor seguridad.

  • Puntos de montaje de red: desmontar NFS/CIFS en la VM antes del rsync para evitar bloqueos.

  • lock: mounted: si el contenedor queda bloqueado, usar pct unmount 201 para liberarlo.

  • Servicios específicos de VM: cloud-init, qemu-guest-agent, systemd-networkd con configuración de hardware virtual, deben revisarse o deshabilitarse.

  • Backup: mantener la VM original intacta hasta validar completamente el contenedor.