Una máquina virtual y un contenedor resuelven problemas relacionados, pero aíslan en niveles diferentes.
Una máquina virtual emula o virtualiza hardware suficiente para ejecutar un sistema operativo invitado completo. Un contenedor aísla procesos dentro de un sistema operativo y comparte el kernel con el host.
La diferencia no significa que uno sustituya siempre al otro. En producción es común ejecutar contenedores dentro de máquinas virtuales: la VM delimita hosts o tenants y los contenedores empaquetan y organizan aplicaciones.
Piensa en una VM como una casa completa dentro de un terreno virtual: tiene su propia instalación eléctrica, tuberías y reglas internas. Un contenedor se parece más a una habitación aislada dentro de un edificio: tiene límites y acceso controlado, pero comparte infraestructura fundamental.
La analogía tiene límites, pero ayuda a recordar:
la VM posee su propio kernel;
el contenedor comparte el kernel del host;
una vulnerabilidad del guest no cruza automáticamente el hypervisor;
un escape de container intenta atravesar una frontera basada en el kernel compartido.
Un hypervisor expone CPU, memoria, almacenamiento y dispositivos virtuales. El guest inicia su bootloader y kernel, detecta ese hardware virtual y arranca servicios del sistema.
Texto
crear VM
→ asignar vCPU, RAM y disco
→ arrancar firmware/bootloader
→ iniciar guest kernel
→ iniciar system services
→ ejecutar aplicación
La VM conserva estado en discos virtuales. Reiniciar la aplicación no implica recrear el guest. Esto facilita tratarla como una máquina tradicional, pero también favorece configuración manual y drift si no se automatiza.
El runtime crea un proceso en el kernel del host y configura namespaces, cgroups, mounts, capabilities y red.
Texto
crear container
→ preparar filesystem desde image
→ crear namespaces
→ aplicar cgroups y seguridad
→ configurar red y mounts
→ iniciar proceso principal
No existe un boot completo de sistema operativo. Por eso el arranque suele ser más rápido y el overhead menor.
Una imagen Linux necesita interfaces de un kernel Linux. En un host Linux, Docker Engine puede usar directamente ese kernel. En macOS y Windows, Docker Desktop ejecuta contenedores Linux dentro de una VM Linux.
Texto
macOS / Windows
→ Docker Desktop
→ Linux VM
→ Linux containers
Por eso un contenedor Linux no ejecuta directamente sus syscalls sobre el kernel de macOS. La VM suministra el kernel esperado.
Los contenedores Windows utilizan otro modelo y requieren compatibilidad entre host, base image y versión del sistema. No debes asumir que una Dockerfile Linux será portable a Windows.
Los contenedores suelen usar menos memoria de base y arrancar más rápido porque no repiten un guest OS. Sin embargo, “contenedor = rendimiento nativo perfecto” es una simplificación.
El coste puede aparecer en:
filesystem por capas;
networking virtual;
bind mounts en Docker Desktop;
emulación entre arquitecturas;
límites de cgroups;
cifrado o drivers de storage;
VM intermedia en escritorio.
Las VMs también pueden tener rendimiento cercano al nativo con virtualización moderna. La decisión no debe basarse únicamente en milisegundos de arranque.
La VM suele ofrecer una frontera más fuerte porque el atacante debe atravesar el guest y luego el hypervisor para alcanzar el host. En un contenedor, un proceso comprometido ya interactúa con el kernel compartido mediante syscalls permitidas.
Esto no significa que los contenedores sean inseguros. La seguridad depende de defensa en profundidad:
Para ejecutar código hostil o multi-tenant no confiable pueden usarse VMs, microVMs o sandboxes adicionales. Un simple container con --privileged no ofrece una frontera adecuada.
Supón una plataforma con API, worker y base de datos.
Una opción es dedicar una VM a cada componente:
Texto
VM API → guest OS + API
VM worker → guest OS + worker
VM DB → guest OS + PostgreSQL
Otra es usar una VM como nodo y varios contenedores:
Texto
VM Linux
├── API container
├── worker container
└── proxy container
Database administrada externa
La segunda opción aumenta densidad y simplifica despliegues. La VM sigue aportando aislamiento frente al proveedor físico y una unidad para parches, networking y capacidad.
Parte de los recursos pertenecen a la VM Linux, cache del filesystem y containers. Debes medir dentro de la VM y del proceso, no comparar solo un número del monitor del host.
En una VM, root controla el guest. En un container, root puede estar restringido, pero mounts, capabilities o socket pueden darle impacto sobre el host. “Root” debe evaluarse según la frontera concreta.