Docker vs Podman en 2026: La guía completa de migración que nadie pidió (pero todos necesitan)
Algo raro está pasando en el mundo de los contenedores. Docker — la herramienta que literalmente inventó la contenedorización — está perdiendo terreno sin hacer ruido. Y no ante una startup deslumbrante, sino ante un proyecto open-source que la mayoría de los devs todavía no probó: Podman.
Los números son claros. Las estrellas de Podman en GitHub superaron las 30,000 a principios de 2026. Red Hat, SUSE y Canonical lo meten por defecto en sus distros. Kubernetes sacó a Docker como runtime en la versión 1.24, y desde entonces muchos equipos empezaron a preguntarse "¿nuestra pila de contenedores está bien así?". Para rematar, los cambios de licencia de Docker Desktop convirtieron "la herramienta gratis que todos usan" en "algo que sale 10M."
Pero la mayoría de los artículos de comparación erran el foco: esto no es sobre si Podman es "mejor" que Docker. Es sobre si tu workflow, tu equipo, tus requisitos de seguridad y tu target de deploy hacen que uno te sirva materialmente más que el otro.
En esta guía entramos a fondo en las diferencias de arquitectura, recorremos escenarios reales de migración, medimos lo que realmente importa y armamos un framework de decisión concreto. Sin vueltas ni "depende" sin contexto. Vamos.
Arquitectura: La división fundamental
Antes que nada, hay que entender la diferencia de diseño más grande entre Docker y Podman. Todo lo demás sale de acá.
Docker: siempre hay un daemon corriendo
Docker funciona con una arquitectura cliente-servidor donde un daemon persistente (dockerd) está siempre en background:
┌─────────────┐ ┌──────────────┐ ┌───────────────┐
│ docker CLI │────▶│ dockerd │────▶│ containerd │
│ (cliente) │ │ (daemon) │ │ (runtime) │
└─────────────┘ └──────────────┘ └───────────────┘
│
Corre como root
Siempre escuchando
Administra todos los contenedores
Cuando corrés docker run nginx, el CLI le manda un request al daemon, que baja la imagen, crea el contenedor y maneja todo su ciclo de vida. Corre como root por defecto y administra todos los contenedores del sistema desde un solo proceso.
Las ventajas son reales:
- Gestión centralizada: un proceso tiene visibilidad de todo el estado
- Background transparente: los contenedores siguen vivos aunque cierres la terminal
- Todo el ecosistema depende de él: Docker Compose, Swarm y miles de herramientas esperan el socket del daemon
Los costos también:
- Superficie de ataque grande: el daemon corre como root. Si lo comprometen, tienen root en el host
- Punto único de fallo: si
dockerdse cae, todos los contenedores caen - Recursos en idle: el daemon consume memoria y CPU incluso cuando no hay nada corriendo
Podman: sin daemon
Podman lo resuelve de otra forma. No hay daemon.
┌─────────────┐ ┌───────────────┐
│ podman CLI │────▶│ conmon │
│ (directo) │ │ (monitor por │
└─────────────┘ │ contenedor) │
└───────────────┘
│
Corre como usuario
Modelo fork-exec
Cada contenedor independiente
Cuando corrés podman run nginx, Podman forkea directamente el proceso del contenedor usando conmon (un monitor liviano). Sin daemon persistente. Cada contenedor corre como proceso independiente bajo tu cuenta de usuario.
¿Qué ganás?
- No necesitás root: los contenedores corren con tu UID por defecto
- Sin punto único de fallo: si un contenedor se cae, los demás ni se enteran
- Cero overhead en idle: cuando no usás contenedores, no corre nada
- Se integra con systemd: podés manejar contenedores como servicios systemd normales
¿Qué perdés?
- Sin estado centralizado: la gestión es por sesión (la base de datos de Podman maneja la persistencia)
- Background requiere setup extra: para que un contenedor sobreviva al logout necesitás
podman generate systemdo flags--restart - Herramientas que asumen Docker: las que esperan
/var/run/docker.socknecesitan adaptación
Seguridad: Por qué esto importa de verdad
"Contenedores rootless" suena a buzzword hasta que entendés qué previene concretamente.
El problema del root en Docker
El daemon de Docker corre como root por defecto. Cuando montás un volumen con -v /host/path:/container/path, el proceso del contenedor puede leer y escribir esos archivos como root en el host. Docker tiene mitigaciones (user namespaces, seccomp, AppArmor), pero son opt-in y la mayoría de los equipos no las activa.
Para dimensionarlo:
# Escape de contenedor con daemon root docker run -v /:/host --privileged alpine chroot /host # → acceso root completo al filesystem del host
El modo rootless de Docker (desde 20.10) lo resuelve, pero hay que configurarlo explícitamente:
# Habilitar modo rootless dockerd-rootless-setuptool.sh install # Verificar docker info | grep "Root Dir" # Debería mostrar algo bajo ~/.local/share/docker
En la práctica, la mayoría de las instalaciones siguen corriendo el daemon como root. Es el default y la mayoría de los tutoriales ni mencionan rootless.
Podman: rootless de fábrica
Podman corre rootless sin tocar nada. Sale así de caja:
# Funciona directo, sin root, sin daemon, sin configurar nada podman run -d nginx # Verificar que corre con tu usuario podman top -l user # Muestra tu UID, no root
Por debajo, Podman usa Linux user namespaces para mapear UIDs del contenedor a UIDs no privilegiados del host:
# Dentro del contenedor, nginx cree que es root (UID 0) # Pero en el host, realmente corre como tu usuario (ej: UID 1000) podman unshare cat /proc/self/uid_map # Output: # 0 1000 1 # 1 100000 65536
Si alguien logra escapar del contenedor, lo único que obtiene son los permisos de tu usuario no privilegiado. Root está fuera de alcance.
Seguridad: la comparación
| Feature | Docker (default) | Docker (rootless) | Podman |
|---|---|---|---|
| Daemon corre como | root | usuario | Sin daemon |
| UID del contenedor en host | root | mapeado | mapeado |
| Socket privilegiado | /var/run/docker.sock (root) | $XDG_RUNTIME_DIR/docker.sock | Ninguno |
| Capabilities default | Amplias | Reducidas | Mínimas |
| SELinux/AppArmor | Opcional | Opcional | Habilitado por defecto |
| Perfil Seccomp | Perfil default | Perfil default | Default más estricto |
| Si comprometen el daemon | Acceso root completo | Acceso de usuario | N/A |
Para equipos con requisitos de compliance (SOC 2, PCI-DSS, HIPAA), la postura de seguridad de Podman simplifica mucho la auditoría.
Compatibilidad CLI: La realidad del "alias docker=podman"
Una de las decisiones más inteligentes de Podman fue hacer su CLI prácticamente 100% compatible con Docker:
# Agregá esto a tu .bashrc/.zshrc alias docker=podman # Y todo esto funciona como siempre: docker pull nginx docker run -d -p 8080:80 nginx docker ps docker build -t myapp . docker push myregistry/myapp
Lo que funciona directo
docker run/build/pull/pushdocker ps/logs/execdocker images/rmidocker network create/ls/rmdocker volume create/ls/rm
Lo que no
Docker Compose — Podman ofrece podman-compose y soporta Docker Compose v2 vía su socket de compatibilidad:
# Opción 1: podman-compose (Python, más simple) pip install podman-compose podman-compose up -d # Opción 2: socket compatible → Docker Compose v2 systemctl --user enable --now podman.socket export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock docker compose up -d
Docker Swarm — Podman no lo soporta. Si usás Swarm (cada vez menos común), es un blocker total.
Docker-in-Docker (DinD) — Común en CI/CD, pero en Podman es diferente:
# Docker: necesitás privileged docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock docker # Podman: rootless, sin privileged podman run --security-opt label=disable \ -v $XDG_RUNTIME_DIR/podman/podman.sock:/var/run/docker.sock \ docker
Features de Docker Desktop — GUI, Kubernetes integrado, Extensions Marketplace y Dev Environments no tienen equivalente directo en Podman. Podman Desktop cubre parte, pero no es lo mismo.
Kubernetes: La ventaja real de Podman
Acá Podman tiene algo que Docker no puede replicar fácilmente por diseño.
Soporte nativo de Pods
Podman maneja pods — grupos de contenedores que comparten network namespace, igual que en Kubernetes — como ciudadanos de primera clase:
# Crear un pod podman pod create --name webapp -p 8080:80 # Meter contenedores podman run -d --pod webapp --name frontend nginx podman run -d --pod webapp --name api node:20-slim # Ambos comparten localhost # frontend accede al API en localhost:3000
Esto es exactamente como Kubernetes organiza contenedores. Docker no tiene este concepto — cada contenedor vive en su propio network namespace.
Generar YAML de Kubernetes
Podés exportar un pod corriendo directamente a YAML compatible con Kubernetes:
# Generar YAML de un pod en ejecución podman generate kube webapp > webapp.yaml cat webapp.yaml
# Generado por Podman apiVersion: v1 kind: Pod metadata: name: webapp spec: containers: - name: frontend image: docker.io/library/nginx:latest ports: - containerPort: 80 hostPort: 8080 - name: api image: docker.io/library/node:20-slim
Y funciona en la dirección inversa:
# Correr YAML Kubernetes localmente podman play kube webapp.yaml # Bajarlo podman play kube webapp.yaml --down
Este workflow de podman play kube tiene usos muy concretos:
- Desarrollo local que replica producción Kubernetes exactamente
- Probar manifests sin levantar un cluster entero
- Migración gradual de Docker Compose a Kubernetes
Docker y Kubernetes: la relación es indirecta
El approach de Docker hacia Kubernetes pasa por herramientas intermedias:
# Docker Desktop incluye un K8s single-node # Pero es un cluster K8s completo, no un concepto liviano de pod # Para convertir Docker Compose a K8s necesitás herramientas externas kompose convert -f docker-compose.yaml # → el output generalmente necesita bastante edición manual
Docker Compose y Kubernetes son abstracciones fundamentalmente distintas. Compose define servicios, Kubernetes define workloads. La traducción siempre pierde información. El concepto de pod de Podman cierra esa brecha de forma nativa.
Docker Compose vs Podman Compose: Lo que pasa en la práctica
La mayoría de los devs no corren contenedores sueltos — orquestan múltiples contenedores con archivos Compose. Acá la migración se pone interesante.
Docker Compose (v2)
Docker Compose v2 es maduro y probado en producción:
# docker-compose.yaml services: web: build: ./frontend ports: - "3000:3000" depends_on: - api - db environment: - API_URL=http://api:4000 api: build: ./backend ports: - "4000:4000" depends_on: db: condition: service_healthy environment: - DATABASE_URL=postgresql://postgres:secret@db:5432/myapp db: image: postgres:16 volumes: - pgdata:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=secret healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 volumes: pgdata:
docker compose up -d docker compose logs -f docker compose down
Las opciones en Podman
Opción 1: podman-compose (Python, de la comunidad):
pip install podman-compose podman-compose up -d
Simple y liviano. Pero no cubre todas las features de Docker Compose v2 (healthcheck.condition, algunos modos de red, profiles).
Opción 2: Docker Compose v2 vía socket Podman (para setups complejos):
# Levantar el socket Docker-compatible de Podman systemctl --user start podman.socket # Apuntar el CLI a Podman export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock # Docker Compose funciona normalmente docker compose up -d
Al pasar por la API Docker-compatible de Podman, tenés compatibilidad completa. Es el camino más suave para archivos Compose complejos.
Opción 3: Quadlet (nativo de Podman, integrado con systemd):
# ~/.config/containers/systemd/webapp.container [Container] Image=docker.io/library/nginx:latest PublishPort=8080:80 Volume=webdata:/usr/share/nginx/html [Service] Restart=always [Install] WantedBy=default.target
systemctl --user daemon-reload systemctl --user start webapp.service
Quadlet define contenedores como unit files de systemd. Más orientado a producción que Compose, pero implica reescribir la orquestación.
CI/CD: Integración en pipelines
CI/CD es la parte más compleja de la migración, porque casi toda la infra CI se construyó pensando en Docker.
GitHub Actions
Docker (default):
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image run: docker build -t myapp:${{ github.sha }} . - name: Push to registry run: | echo ${{ secrets.REGISTRY_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin docker push ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
Podman (reemplazo directo):
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image run: podman build -t myapp:${{ github.sha }} . - name: Push to registry run: | podman login ghcr.io -u ${{ github.actor }} -p ${{ secrets.REGISTRY_TOKEN }} podman push ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
Podman viene preinstalado en los runners ubuntu-latest de GitHub, así que literalmente cambiás el nombre del comando y listo.
GitLab CI
GitLab CI tradicionalmente usa DinD (Docker-in-Docker), que necesita modo privilegiado:
Docker (necesita privileged):
build: image: docker:latest services: - docker:dind variables: DOCKER_TLS_CERTDIR: "/certs" script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
Podman (sin privileged):
build: image: quay.io/podman/stable script: - podman build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - podman push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
No necesitar --privileged elimina una superficie de ataque importante en CI. Es un cambio silencioso pero significativo.
Buildah: builds especializados
El ecosistema de Podman incluye Buildah, una herramienta dedicada a construir imágenes con capacidades que docker build no tiene:
# Build from scratch: sin imagen base, superficie de ataque mínima container=$(buildah from scratch) buildah copy $container ./static-binary /app buildah config --entrypoint '["/app"]' $container buildah commit $container myapp:minimal # → imagen sin paquetes, sin shell, solo tu binario
# Control a nivel de capa individual buildah run $container -- pip install -r requirements.txt buildah run $container -- pip cache purge buildah commit $container myapp:optimized
Buildah es especialmente útil en entornos con requisitos de seguridad estrictos, porque nunca levanta un daemon y puede construir imágenes sin runtime de contenedor.
Benchmarks: Los números que importan
Vamos a lo que realmente hace diferencia en el día a día:
Startup
Inicio de contenedor (nginx, Alpine, SSD):
Docker: ~0.8-1.2s (daemon ya corriendo)
Podman: ~0.5-0.9s (fork-exec, sin daemon)
Primer contenedor después del boot:
Docker: ~2-4s (hay que esperar al daemon)
Podman: ~0.5-0.9s (no hay daemon que esperar)
Podman gana en cold-start. En servidores long-running donde el daemon ya está activo, la diferencia es despreciable.
Build
Build de imagen (Node.js multi-stage, cache frío):
Docker BuildKit: ~45-60s
Podman (Buildah): ~48-65s
Cache caliente:
Docker BuildKit: ~3-5s
Podman (Buildah): ~3-5s
Efectivamente lo mismo. BuildKit tiene leves ventajas en gestión de cache para builds multi-stage complejos, pero rara vez supera el 10%.
Memoria
Consumo idle:
Docker daemon: ~50-100MB (siempre corriendo)
Podman: ~0MB (nada corre en idle)
Por contenedor:
Docker: ~5-10MB (conmon + shim)
Podman: ~3-8MB (solo conmon)
El costo cero en idle pesa en máquinas de desarrollo y runners CI. En un servidor de producción con 50+ contenedores, el overhead por contenedor es casi idéntico.
Pull de imágenes
nginx:latest (comprimido ~70MB):
Docker: ~4-6s
Podman: ~4-6s
Imagen ML grande (~5GB):
Docker: ~45-90s
Podman: ~45-90s
Sin diferencia relevante. Mismo protocolo OCI, misma velocidad.
Docker Desktop vs Podman Desktop
Para devs en macOS y Windows, la experiencia de escritorio importa:
Docker Desktop
- Costo: gratis para uso personal, educación y empresas <250 empleados con <24/mes/usuario para el resto (Business tier)
- Kubernetes: cluster single-node integrado
- Extensions: marketplace con 100+ extensiones
- Dev Environments: entornos remotos estilo Codespaces
- VM: gestión automática de VM Linux en macOS/Windows
- Recursos: GUI para límites de CPU/memoria
- Volúmenes: GUI para inspección y gestión
Podman Desktop
- Costo: gratis, open-source (Apache 2.0)
- Kubernetes: integración Kind/Minikube (no integrado)
- Extensions: sistema de plugins en crecimiento
- VM: gestión automática vía
podman machine - Recursos: controles básicos de CPU/memoria
- Pods: UI de primera clase para crear y gestionar pods
Para devs individuales y equipos chicos, Podman Desktop compite bien. Para equipos que dependen de las Extensions, Dev Environments o features enterprise de Docker Desktop (SSO, gestión de acceso a imágenes, Hardened Desktop), Docker sigue adelante.
Playbook de migración: De Docker a Podman
Si decidiste migrar, este es el orden que mejor funciona:
Fase 1: Desarrollo local (Semana 1-2)
# 1. Instalar Podman # macOS: brew install podman podman machine init podman machine start # Linux (Ubuntu/Debian): sudo apt install podman # 2. Alias (no rompe nada, Docker sigue funcionando) echo 'alias docker=podman' >> ~/.zshrc source ~/.zshrc # 3. Probar workflows existentes docker pull your-registry/your-app:latest docker run -d -p 3000:3000 your-registry/your-app:latest # 4. Probar compatibilidad Compose systemctl --user start podman.socket export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock docker compose up -d
Fase 2: Pipeline CI/CD (Semana 3-4)
# Job paralelo al build Docker existente para validar sin romper nada build-podman: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build with Podman run: | podman build -t myapp:${{ github.sha }} . podman push ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
Fase 3: Producción (Semana 5+)
# Generar servicios systemd desde contenedores podman generate systemd --new --files --name webapp # Instalar y activar cp container-webapp.service ~/.config/systemd/user/ systemctl --user enable --now container-webapp.service # O usá Quadlet (ver la sección de Quadlet arriba)
Trampas comunes en la migración
- Permisos de volúmenes: el modo rootless mapea UIDs diferente. Puede que necesites
podman unshare chownpara volúmenes montados - Binding de puertos: rootless no puede bindear puertos bajo 1024 sin
sysctl net.ipv4.ip_unprivileged_port_start=0 - Modos de red:
--network=hostfunciona distinto en rootless - Mounts del socket: herramientas que montan
/var/run/docker.socknecesitan apuntar al socket de Podman
Framework de decisión: Docker vs Podman en 2026
Sin ambigüedad, concreto:
Quedate con Docker si:
- Tu equipo tiene menos de 250 personas (facturación <$10M) y Docker Desktop es gratis. El ecosistema pesa
- Dependés de Docker Swarm. Podman no tiene alternativa
- Tus pipelines usan features Docker-específicas (cache mounts avanzados de BuildKit, Compose completo en testing, DinD) y cambiarlas tiene alto costo
- Usás Extensions de Docker Desktop en tu día a día (escaneo de vulnerabilidades, explorador de logs, herramientas de DB)
- Tu equipo trabaja en macOS/Windows y necesita la experiencia pulida de Docker Desktop
Pasate a Podman si:
- El compliance de seguridad es prioridad. Rootless por defecto, sin daemon privilegiado, capabilities mínimas — la postura es significativamente más fuerte
- Estás pagando Docker Desktop con un equipo grande. 28,800/año. Podman Desktop es gratis
- Tu target es Kubernetes y querés que el desarrollo local refleje producción. El concepto de pod y
podman play kubeson herramientas genuinamente útiles - Corrés en servidores Linux y querés gestión nativa con systemd (Quadlet)
- Necesitás builds rootless en CI. Poder buildear sin modo privilegiado es una mejora real de seguridad
- La eficiencia de recursos importa (runners compartidos, laptops de desarrollo). Cero overhead de daemon idle suma
El enfoque híbrido (lo más común)
La realidad: la mayoría de los equipos no migra de un día para otro.
- Podman en servidores Linux — rootless, systemd, sin licensing
- Docker Desktop en máquinas de desarrollo — UX pulida, extensions, onboarding fácil
- Podman en CI/CD — sin contenedores privilegiados, preinstalado en GitHub runners
El estándar OCI garantiza imágenes 100% intercambiables, así que este mix funciona sin fricción.
Mirando hacia adelante: 2026 y después
El ecosistema de contenedores converge en estándares y diverge en implementación.
La estandarización OCI está lista. Imágenes, runtimes y specs de distribución son maduros. Docker y Podman buildean y corren las mismas imágenes. La época de "funciona en Docker, rompe en Podman" quedó atrás.
Contenedores WebAssembly (Wasm) están apareciendo como tecnología complementaria. Docker (vía runwasi) y Podman (vía crun-wasm) pueden correr workloads Wasm junto a contenedores Linux tradicionales. Arrancan en milisegundos, usan una fracción de la memoria y ofrecen sandboxing más fuerte. No van a reemplazar contenedores Linux, pero van a tomar una porción creciente de workloads livianos.
Rootless como default se está volviendo norma en la industria. Docker viene mejorando su modo rootless release a release. La filosofía de Podman — rootless y sin daemon por defecto — está ganando el argumento arquitectónico, uses la herramienta que uses.
Dev Containers y entornos de desarrollo basados en contenedores siguen madurando. Docker (vía la spec Dev Containers) y Podman (vía Podman Desktop) soportan este patrón. Si el futuro del desarrollo es contenedores de punta a punta, la elección de runtime se vuelve aún más importante.
Conclusión
La respuesta honesta: depende de qué estés optimizando, pero es menos ambigua que hace dos años.
Si seguridad, costo y alineación con Kubernetes son tus prioridades, Podman es la opción más fuerte en 2026. Rootless por defecto, cero overhead de daemon, soporte nativo de pods — son ventajas técnicas genuinas que Docker viene persiguiendo.
Si lo que más pesa es la madurez del ecosistema, la experiencia de escritorio en macOS/Windows y la inercia del tooling, Docker sigue siendo el default pragmático. El ecosistema es más grande, el escritorio más pulido, y hay más tutoriales, respuestas de Stack Overflow y templates de CI que asumen Docker.
La buena noticia: como ambas herramientas siguen OCI, no hay lock-in. Podés empezar con Docker, pasar a Podman para CI, usar Podman en producción y mantener Docker Desktop para desarrollo local. Las imágenes, los registries y los Dockerfiles (sí, Podman buildea Dockerfiles) son los mismos.
El mundo de los contenedores se dividió no por incompatibilidad sino por filosofía: daemon centralizado vs daemonless, root por defecto vs rootless por defecto, producto comercial vs proyecto comunitario. En 2026, las dos filosofías producen resultados de nivel producción. Tu elección debería reflejar tus restricciones y prioridades, no cuál herramienta tiene más hinchada en Twitter.
Elegí uno, levantá contenedores, y enfocate en lo que realmente es difícil: el código adentro de ellos.
Explora herramientas relacionadas
Prueba estas herramientas gratuitas de Pockit