SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-12

Podman vs Docker en VPS: diferencias técnicas clave

Podman funciona sin daemon y sin root. Aprende cómo esto afecta el uso de archivos compose, la gestión de volúmenes, los permisos de puertos y la configuración de systemd en servidores.

Diferencias reales entre Podman y Docker

Podman y Docker ejecutan las mismas imágenes OCI (Open Container Initiative) en un VPS, por lo que la elección no depende del software que pueda ejecutar. La diferencia radica en el modelo de procesos. Docker ejecuta un daemon con privilegios de root que controla todos los contenedores, y el comando docker es un cliente pequeño que solicita al daemon que realice el trabajo. Podman no utiliza un daemon: podman run inicia el contenedor como un proceso hijo de quien lo invoca, bajo su propio usuario sin privilegios.

Todo lo demás se deriva de este hecho. El inicio automático pasa a ser responsabilidad de systemd en lugar del daemon. La propiedad de los volúmenes se gestiona a través de un espacio de nombres de usuario (user namespace), por lo que el propietario que usted ve con ls -l en el host no es el mismo que ve el contenedor. Los puertos inferiores a 1024 rechazan la vinculación hasta que se modifica una configuración del kernel. La interfaz de línea de comandos (CLI) docker sigue funcionando mediante un envoltorio (wrapper), hasta el punto en que algún componente requiere el socket de Docker.

Sin daemon: qué se ejecuta realmente al iniciar un contenedor

En un host Docker, pstree -a muestra a dockerd como root, containerd a su lado y un containerd-shim-runc-v2 por cada contenedor en ejecución. Su aplicación es un proceso hijo de ese shim, y el shim es hijo del PID 1. Nada conecta el contenedor con la shell que lo inició. Si detiene el daemon, pierde el plano de control de todos los contenedores del equipo y, con el ajuste live-restore desactivado por defecto, systemctl restart docker también detiene sus contenedores.

Podman no tiene un proceso equivalente. Al iniciar un contenedor, obtiene un proceso conmon (monitor de contenedor) que mantiene el proceso principal del contenedor, propiedad del usuario que ejecutó el comando.

podman run -d --name web -p 8080:80 docker.io/library/caddy:2
ps -o user,pid,ppid,args -C conmon
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080

ps debería listar conmon ejecutándose como su usuario de inicio de sesión y no como root, y curl debería imprimir 200. Debido a que ningún servicio central es propietario del contenedor, sudo apt upgrade podman no detiene nada que ya esté en ejecución, y el bloqueo del monitor de un contenedor no puede arrastrar a los demás.

La ausencia de un daemon también tiene un coste. Nada inicia sus contenedores tras un reinicio. El --restart=always de Docker es una promesa que el daemon cumple al arrancar, y Podman la sustituye por systemd, que es el propósito de la sección sobre quadlet más abajo.

El socket es la otra mitad de la historia. /var/run/docker.sock es un punto de conexión de la API (interfaz de programación de aplicaciones) propiedad de root, y cualquier proceso con permisos de escritura en él puede iniciar un contenedor privilegiado que monte el sistema de archivos del host. Añadir un usuario al grupo docker le otorga privilegios de root por una vía más lenta, lo cual merece la pena leer junto a otorgar a cada cuenta de servicio solo el acceso que necesita. Podman no expone ningún socket a menos que usted lo solicite, y el socket que obtiene pertenece a un único usuario en /run/user/<uid>/podman/podman.sock.

Instalar Podman en Ubuntu 24.04 y confirmar que el modo rootless funciona

sudo apt update
sudo apt install -y podman uidmap
podman --version
podman info | grep -i rootless

El paquete uidmap proporciona newuidmap y newgidmap. Estos son los ayudantes setuid que permiten a un usuario normal reclamar un rango de IDs subordinados; sin ellos, los contenedores rootless no arrancan. podman info debería imprimir rootless: true.

Ubuntu 24.04 incluye Podman 4.9 y Debian 13 incluye Podman 5.x, verificado en agosto de 2026. La diferencia es importante, ya que los archivos quadlet requieren la versión 4.4 o superior y los archivos quadlet .pod requieren la 5.0. Ejecute podman --version antes de copiar un ejemplo de la documentación oficial.

Cada usuario rootless necesita un rango de IDs subordinados:

grep "$USER" /etc/subuid /etc/subgid

Un usuario creado mediante adduser en Ubuntu obtiene un rango automáticamente. Un usuario creado mediante useradd -M o mediante una herramienta de configuración a menudo no lo tiene, y el error lo indica explícitamente:

Error: cannot find UID/GID for user deploy: no subuid ranges found for user "deploy" in /etc/subuid - check rootless mode in man pages.

Asigne un rango y luego restablezca el almacenamiento de ese usuario para que se utilice el nuevo mapeo:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 deploy
podman system migrate

Otra sorpresa en la primera ejecución: Podman no asume Docker Hub por defecto. Un nombre de imagen corto se resuelve contra unqualified-search-registries en /etc/containers/registries.conf, y en un script sin terminal asociada, la descarga falla con short-name resolution enforced but cannot prompt without a TTY. Escriba el nombre completo siempre. Utilice docker.io/library/nginx:1.27 en lugar de nginx.

Lo que realmente aportan los contenedores rootless en un servidor alquilado

Un contenedor rootless se ejecuta dentro de un espacio de nombres de usuario, una funcionalidad del kernel que asigna al proceso su propio mapa privado de IDs de usuario. Dentro del espacio de nombres, el superusuario del contenedor es el UID (ID de usuario) 0. Fuera de él, en su VPS, ese mismo proceso es su usuario de inicio de sesión habitual. Ser root en el contenedor no significa ser root en el host.

Ese es el alcance real de la ventaja. Una imagen que insiste en ejecutarse como root, una aplicación web con un error de ejecución remota de código o un escape que dependa de ser UID 0 en el exterior: todos ellos terminan teniendo los permisos de su usuario sin privilegios en lugar de los de la máquina. Lo que rootless no hace es protegerle contra errores del kernel, ni protege sus propios archivos, ya que el proceso escapado se ejecuta como usted y puede leer todo lo que usted puede leer.

Docker también puede ejecutarse de forma rootless. dockerd-rootless-setuptool.sh install configura un daemon por usuario y funciona correctamente. La diferencia radica en hacia dónde apunta la configuración predeterminada. Con Podman, usted obtiene rootless sin tener que solicitarlo, por lo que su primer fallo será un contenedor que no puede enlazar el puerto 80, en lugar de un servicio que se ejecutó silenciosamente como root durante dos años.

¿Por qué mis archivos de volumen pertenecen al UID 100999?

Esto ocurre debido al mismo espacio de nombres de usuario. El UID 0 del contenedor se asigna a su UID en el host. El UID 1 del contenedor se asigna al primer ID en su rango subuid, y aumenta a partir de ahí. Con un rango que comienza en 100000, el UID 1000 del contenedor aparece en el host como 100999.

mkdir -p "$PWD/data"
podman run --rm --user 1000 -v "$PWD/data:/data" docker.io/library/alpine:3 sh -c 'id -u; touch /data/f'
ls -ln "$PWD/data"

El contenedor muestra 1000. El listado del host muestra como propietario 100999, porque 100000 más 1000 menos 1 es 100999. Nada está roto, y un simple chown no lo solucionará, porque su usuario sin privilegios no puede cambiar la propiedad de los archivos fuera del espacio de nombres.

Cuatro soluciones posibles:

  • podman unshare chown 1000:1000 "$PWD/data" ejecuta el chown dentro del mismo espacio de nombres de usuario, donde los números significan lo mismo que para el contenedor.
  • -v "$PWD/data:/data:U" solicita a Podman que corrija la propiedad del directorio de origen por usted. Úselo en un directorio nuevo, no en datos importantes.
  • --userns=keep-id asigna su UID del host al mismo UID dentro del contenedor, de modo que los archivos nuevos le pertenezcan a usted.
  • Un volumen con nombre como -v appdata:/data evita este problema, ya que Podman lo crea dentro de su propio almacenamiento con la propiedad ya configurada correctamente.

Si ha lidiado con esto en Docker, es el mismo problema un nivel más arriba. Las variables PUID y PGID que exponen muchas imágenes definen el UID que utiliza el proceso dentro del contenedor y, bajo Podman sin root, ese UID se asigna una segunda vez. PUID=1000 dentro de un contenedor sin root sigue escribiendo archivos en el host con el propietario 100999. Elija los números teniendo en cuenta esa segunda asignación, o mueva los datos a un volumen con nombre y deje de preocuparse por ello.

Dos notas adicionales sobre los montajes. Las banderas :z y :Z que aparecen en ejemplos de Fedora y RHEL son opciones de reetiquetado de SELinux; en Ubuntu se utiliza AppArmor, por lo que no tienen efecto allí. Además, Podman sin root no puede montar un directorio del host que su usuario no pueda leer; esto es una medida de seguridad, no un fallo.

¿Por qué Podman sin privilegios (rootless) rechaza publicar el puerto 80?

Vincular un puerto inferior a 1024 requiere un privilegio del que su usuario no dispone. El error indica la solución:

Error: rootlessport cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied

Existen dos soluciones. Reducir el umbral para todo el host:

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system
sysctl net.ipv4.ip_unprivileged_port_start

El último comando debería devolver 80. Tenga claro qué hace este ajuste: cualquier usuario en la máquina puede ahora vincular los puertos 80 y 443, no solo el usuario que ejecuta los contenedores. En un VPS con un único administrador, es un compromiso aceptable. En un equipo que aloja cuentas de otros usuarios, no lo es. La otra opción es publicar en el puerto 8080 y colocar un reverse proxy delante, que es donde usted querría certificados emitidos y renovados por certbot en nginx de todos modos.

La publicación sin privilegios también cambia lo que ve su aplicación. Podman 4.x utiliza slirp4netns con el gestor de puertos rootlesskit por defecto, y las conexiones reenviadas llegan con una dirección de origen reescrita, por lo que el registro de acceso (access log) registra a cada visitante como 10.0.2.100. Podman 5.0 cambió el valor por defecto a pasta, que mantiene la dirección real del cliente. En la versión 4.x, --network slirp4netns:port_handler=slirp4netns restaura la dirección de origen real a costa de cierto rendimiento.

Aquí hay una ventaja inesperada. Un puerto publicado sin privilegios es un socket de escucha común propiedad de un proceso normal, por lo que las reglas de entrada de su firewall se aplican a él. Docker publica puertos escribiendo reglas NAT (Network Address Translation) además de sus propias aceptaciones de reenvío, que es exactamente la razón por la cual un puerto publicado por Docker ignora la regla de ufw que usted creía que lo estaba bloqueando. Podman con privilegios (rootful) utiliza una infraestructura similar y hereda la misma trampa. La versión sin privilegios no lo hace.

¿Siguen funcionando mis archivos Docker Compose en Podman?

En su mayoría, a través de dos vías distintas. La primera es podman-compose, una implementación independiente que lee el mismo archivo y controla la CLI de Podman:

sudo apt install -y podman-compose
podman-compose up -d
podman ps

La segunda es el Docker Compose real comunicándose con la API compatible con Docker de Podman a través de un socket por usuario:

systemctl --user enable --now podman.socket
export DOCKER_HOST="unix:///run/user/$(id -u)/podman/podman.sock"
docker compose up -d
podman ps

docker compose ps y podman ps deberían listar los mismos contenedores, ya que solo existe un conjunto de ellos. La resolución de nombres también funciona: el backend de red predeterminado de Podman, netavark, ejecuta aardvark-dns, por lo que los contenedores en una red definida por el usuario se encuentran entre sí por su nombre.

Existen limitaciones reales. Cualquier elemento que monte /var/run/docker.sock debe apuntar al socket de Podman o ser eliminado. network_mode: host se comporta de forma distinta bajo un espacio de nombres de usuario. depends_on con condition: service_healthy cuenta con un soporte irregular entre las versiones de podman-compose. restart: always no sobrevive a un reinicio por sí solo, lo cual se soluciona en la siguiente sección. Compose sigue siendo una buena forma de describir una pila de varios contenedores en un solo archivo, y bajo Podman actúa como una capa de traducción. Para una pila que pretenda mantener durante años, conviértala a quadlets y mantenga una sola abstracción en lugar de dos.

Pods: el concepto para el que Docker no tiene respuesta

Un pod es un grupo de contenedores que comparten el mismo espacio de nombres de red. Podman inicia un pequeño contenedor infra para mantener ese espacio de nombres abierto, y los miembros se comunican entre sí a través de 127.0.0.1 sin necesidad de redes definidas por el usuario ni descubrimiento de servicios.

podman pod create --name app -p 8080:80
podman run -d --pod app --name app-cache docker.io/library/redis:7
podman run -d --pod app --name app-web docker.io/library/nginx:1.27
podman pod ps
podman ps --pod

podman pod ps debería mostrar el pod Running con tres contenedores, contando el contenedor de infraestructura. El contenedor web ahora accede a Redis en 127.0.0.1:6379 en lugar de en app-cache:6379. Del espacio de nombres compartido se derivan dos reglas: los puertos se publican en el pod y nunca en un miembro, y dos miembros no pueden escuchar en el mismo puerto.

Este es el modelo de Kubernetes, y Podman se apoya en él. podman kube generate app > app.yaml genera un manifiesto de Kubernetes a partir de lo que se está ejecutando (en paquetes más antiguos se escribe podman generate kube), y podman kube play app.yaml lo recrea en otro host. Quadlet tiene un tipo de unidad .kube que ejecuta dicho archivo como un servicio de systemd. Es una forma genuinamente distinta de agrupar servicios y es la razón más sólida para elegir Podman si Kubernetes forma parte de su futuro.

Inicio automático sin daemon: unidades Quadlet

Quadlet es un generador de systemd. Convierte un archivo breve que describe un contenedor en un servicio real de systemd durante el arranque. Los archivos se ubican en ~/.config/containers/systemd/ para un usuario sin privilegios (rootless), o en /etc/containers/systemd/ para el usuario root.

~/.config/containers/systemd/caddy.container:

[Unit]
Description=Caddy web server
After=network-online.target

[Container]
Image=docker.io/library/caddy:2
ContainerName=caddy
PublishPort=8080:80
Volume=caddy-data.volume:/data
Environment=TZ=UTC
AutoUpdate=registry

[Service]
Restart=always
MemoryMax=512M

[Install]
WantedBy=default.target

~/.config/containers/systemd/caddy-data.volume puede estar casi vacío, ya que el encabezado de la sección es lo que crea el volumen:

[Volume]
systemctl --user daemon-reload
systemctl --user start caddy
systemctl --user status caddy
journalctl --user -u caddy -n 50

El nombre del servicio proviene del nombre del archivo: caddy.container se convierte en caddy.service. No ejecute systemctl --user enable caddy. Las unidades generadas no se pueden habilitar y systemd responde Failed to enable unit: Unit /run/user/1000/systemd/generator/caddy.service is transient or generated.. La sección [Install] es la que inicia el contenedor en el arranque, y daemon-reload es lo que regenera la unidad después de editar el archivo.

Ahora, el ajuste que suele causar problemas a casi todos:

sudo loginctl enable-linger deploy
loginctl show-user deploy --property=Linger

Espere Linger=yes. Sin el modo linger, systemd cierra toda la sesión del usuario cuando termina su última conexión SSH, por lo que cada contenedor rootless se detiene y ninguno vuelve a iniciar en el arranque. Los contenedores que desaparecen al cerrar sesión siempre se deben a esto.

Debido a que el contenedor es el proceso principal de una unidad de servicio común, se aplican directamente los controles de systemd. MemoryMax= y CPUQuota= en la sección [Service] se comportan exactamente igual que para cualquier otro servicio que limite con systemd. Esto requiere cgroup v2 (control group version 2), que Ubuntu utiliza de forma predeterminada desde la versión 22.04. Confírmelo con podman info | grep -i cgroup.

Las actualizaciones tienen un mecanismo de coincidencia. AutoUpdate=registry más systemctl --user enable --now podman-auto-update.timer comprueba en el registro si existe una imagen más reciente con la misma etiqueta, reinicia la unidad y revierte a la imagen anterior si el nuevo contenedor no logra iniciarse. Ejecute podman auto-update --dry-run primero para ver qué cambios se aplicarían. El comando anterior podman generate systemd todavía existe pero está obsoleto, por lo que debe escribir quadlets para cualquier implementación nueva.

Dónde funciona el alias de docker y dónde no

sudo apt install -y podman-docker
sudo touch /etc/containers/nodocker
docker ps

podman-docker instala un envoltorio /usr/bin/docker que invoca a Podman. Sin el archivo nodocker, cada llamada imprime primero Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.. El envoltorio cubre los comandos que utiliza a diario: run, ps, logs, exec, build, pull, push, inspect, cp, volume, network.

Lo que no se transfiere es una lista más corta y más crítica. El modo Swarm no tiene equivalente, por lo que un stack de Swarm no tiene dónde ejecutarse. Las herramientas que se comunican con el socket de Docker requieren que se exporte el socket de Podman, y algunas aún detectan la diferencia; el proveedor de Docker de Traefik funciona cuando se apunta a /run/user/<uid>/podman/podman.sock, mientras que Watchtower no tiene cabida, ya que podman auto-update realiza esa función. El almacenamiento es independiente, por lo que Podman no puede ver las imágenes que ya descargó con Docker, y podman images en un host de Docker ocupado comienza vacío.

Migración de una pila en ejecución, paso a paso

  1. Cree o elija el usuario sin privilegios que será propietario de los contenedores y confirme que tiene un rango en /etc/subuid.
  2. Vuelva a descargar cualquier imagen proveniente de un registro, utilizando nombres completos. Podman tiene su propio almacén de imágenes y no leerá el de Docker.
  3. Mueva las imágenes creadas localmente mediante docker save app:1.4 | podman load.
  4. Detenga el contenedor de Docker, copie el contenido de cada volumen fuera de /var/lib/docker/volumes/<name>/_data y luego corrija la propiedad con podman unshare chown -R 1000:1000 <path>.
  5. Resuelva la cuestión de los puertos: publíquelos por encima de 1024 detrás de un proxy inverso o configure net.ipv4.ip_unprivileged_port_start.
  6. Escriba un archivo quadlet por cada contenedor, ejecute systemctl --user daemon-reload e inicie cada servicio.
  7. Ejecute sudo loginctl enable-linger <user>, reinicie el VPS, vuelva a iniciar sesión y compruebe que podman ps enumera todos los servicios de nuevo.

Ambos motores no comparten nada: el almacenamiento de imágenes y las redes son independientes. Por lo tanto, puede ejecutar ambos mientras realiza la migración; lo único por lo que pueden entrar en conflicto es por el número de puerto del host. Mueva un servicio, monitorícelo durante un día y luego mueva el siguiente.

Podman frente a Docker: ¿cuál elegir para su VPS?

Manténgase en Docker si su infraestructura depende de archivos compose que otras personas también mantienen, o si utiliza herramientas que interactúan con el socket de Docker. La compatibilidad con los estándares de la industria es una ventaja real, y Docker posee una mayor cuota de mercado en este aspecto. Un equipo cuyos integrantes utilizan Docker en sus equipos locales obtiene una ventaja operativa directa al ejecutar el mismo motor en producción.

Cambie a Podman si su VPS ejecuta un conjunto limitado de servicios que usted controla por completo, o si prefiere que cada aplicación se ejecute bajo su propio usuario sin privilegios, sin necesidad de tener el grupo docker en el sistema. La alineación con la distribución también es un factor relevante: RHEL y sus derivados incluyen Podman como el motor soportado, por lo que en esos sistemas Podman es la opción con menos imprevistos. Si ya supervisa el resto de sus servicios mediante unidades de systemd, los quadlets le resultarán una pieza lógica que encaja en su flujo de trabajo, en lugar de una herramienta nueva que deba aprender.

Existe una opción intermedia que merece ser mencionada. Podman en modo rootful se comporta de manera muy similar a Docker, mantiene el comando docker mediante un wrapper y elimina la necesidad de un daemon en ejecución constante. Sin embargo, al usar este modo se prescinde de la ejecución sin privilegios (rootless), que es precisamente el componente que mejora su postura de seguridad, por lo que debe considerarse solo como un paso intermedio.

Si aún está configurando su primer host de contenedores, la guía de instalación y endurecimiento de Docker en un VPS nuevo es el camino más directo, y ninguno de los conocimientos adquiridos será en vano. Las imágenes y los volúmenes son los mismos objetos en ambos motores, por lo que una migración posterior solo afectará a la forma en que se supervisan sus servicios y a muy poco más.

FAQ

¿Es Podman un reemplazo directo de Docker?

Para los comandos que escribe, es muy similar. Instalar podman-docker le proporciona un alias /usr/bin/docker, y run, ps, build, logs y exec se comportan de la misma manera. No es un reemplazo para el daemon. Swarm no tiene equivalente, las herramientas que se conectan a /var/run/docker.sock deben apuntar al socket de Podman por usuario, y las imágenes descargadas por Docker permanecen invisibles para Podman porque ambos mantienen almacenamientos separados.

¿Por qué mis contenedores de Podman rootless se detienen al cerrar la sesión SSH?

Porque systemd detiene la sesión del usuario, y con ella cada servicio de usuario, cuando se cierra su último inicio de sesión. Ejecute sudo loginctl enable-linger <user> y luego verifique que loginctl show-user <user> --property=Linger devuelva Linger=yes. La opción linger mantiene la instancia de systemd de ese usuario en ejecución sin una sesión activa, lo cual también permite que los contenedores se inicien nuevamente tras un reinicio.

¿Por qué los archivos en mi volumen tienen como propietario el UID 100999?

Podman rootless asigna el UID 0 del contenedor a su usuario en el host y luego asigna el UID 1 del contenedor en adelante a su rango de subuid. Con un rango que comienza en 100000, el UID 1000 del contenedor se convierte en 100999 en el host. Corríjalo desde dentro del espacio de nombres con podman unshare chown 1000:1000 /path/to/data, móntelo con la bandera :U en la primera ejecución o utilice --userns=keep-id para que los UID del contenedor coincidan con los suyos.

¿Puedo seguir usando docker-compose.yml con Podman?

Sí, de dos formas. podman-compose lee el archivo y gestiona la CLI de Podman directamente. O bien, habilite el socket de compatibilidad con systemctl --user enable --now podman.socket, configure DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock y ejecute docker compose real contra él. Espere fricción en network_mode: host, en servicios que montan el socket de Docker y en restart: always, que requiere una unidad quadlet y linger para sobrevivir a un reinicio.

¿El modo rootless realmente hace que los contenedores sean más seguros?

Elimina un riesgo específico: un proceso que escapa de un contenedor rootless mantiene los permisos de su usuario sin privilegios en lugar de los de root. Esto es valioso y es la razón por la cual el grupo docker, equivalente a root, no tiene contraparte en Podman rootless. No detiene las vulnerabilidades del kernel ni protege los archivos que su propio usuario puede leer, así que mantenga el resto del endurecimiento de seguridad que aplicaría en cualquier servidor.