Contenido

Tutorial de Docker rootless y Podman: ejecutar contenedores sin root

En Prácticas recomendadas de seguridad comprobamos que cualquiera que pueda usar Docker puede montar el directorio raíz del host en un contenedor y leer y escribir cualquier archivo: es equivalente a tener permisos de root en el host. Esto se debe a que Docker Engine (dockerd) se ejecuta como root, por lo que el root del contenedor es el root del host (aunque esté limitado por varios mecanismos de aislamiento).

En esta sección se presentan dos formas de prescindir de root: el modo Rootless de Docker y otra herramienta de contenedores, Podman.

Docker Rootless: Docker Engine se ejecuta como un usuario normal

El modo Rootless ejecuta dockerd y todos los contenedores como un usuario normal. Utiliza el espacio de nombres de usuario de Linux: dentro del contenedor parece root (uid 0), pero en el host corresponde a tu cuenta o a un intervalo de uid sin privilegios.

En esta sección hacemos una prueba con la imagen oficial docker:29.9-dind-rootless, sin afectar la configuración del host:

$ docker run -d --name rl --privileged docker:29.9-dind-rootless
93e609c8808360d7e63bdc214ab2ff0e76af520fb0fb9dec5b5852d57a4881fe

$ docker logs rl 2>&1 | tail -n 5
time="2026-10-13T05:22:59.775914576Z" level=info msg="Initializing buildkit"
time="2026-10-13T05:22:59.918099929Z" level=info msg="Completed buildkit initialization"
time="2026-10-13T05:22:59.929895038Z" level=info msg="Daemon has completed initialization"
time="2026-10-13T05:22:59.930093633Z" level=info msg="API listen on /run/user/1000/docker.sock"
time="2026-10-13T05:22:59.930231753Z" level=info msg="API listen on [::]:2376"

$ docker exec rl id
uid=1000(rootless) gid=1000(rootless) groups=1000(rootless)

Los registros muestran que Docker Engine escucha en /run/user/1000/docker.sock (una ruta exclusiva del usuario 1000), en lugar de la ruta habitual /var/run/docker.sock. El usuario que ejecuta dockerd es rootless (uid 1000), no root.

Quién es el root dentro del contenedor

Conecta el comando docker al socket de Rootless (DOCKER_HOST) y ejecuta un contenedor:

$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker info --format '{{json .SecurityOptions}}'
["name=seccomp,profile=builtin","name=rootless"]

$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker run -d --name sleeper alpine:3.23 sleep 300
18986d442e9b9282178d96e7187534b9ac19a83dd42e3b4671a5243b55bcf1ff

$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker exec sleeper id
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

$ docker exec rl ps -o user,pid,args | grep 'sleep 300' | grep -v grep
rootless   382 sleep 300
  • En las opciones de seguridad de docker info aparece name=rootless.
  • id dentro del contenedor muestra uid=0(root), así que para los programas del contenedor ese usuario es root.
  • Desde el host (el exterior), el propietario del mismo proceso sleep 300 es el usuario normal rootless.

Prueba: montar el directorio raíz del host no permite leer /etc/shadow

/etc/shadow almacena hashes de contraseñas y solo root puede leerlo. Monta / del host e intenta leer el archivo con Docker normal y con Docker Rootless:

$ docker run --rm -v /:/host alpine:3.23 sh -c "id -u; head -n 1 /host/etc/shadow"
0
root:*::0:::::

$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker run --rm -v /:/host alpine:3.23 sh -c 'id -u; head -n 1 /host/etc/shadow'
0
head: /host/etc/shadow: Permission denied
  • Docker normal: el root del contenedor leyó directamente /etc/shadow del host.
  • Docker Rootless: dentro del contenedor también es root (id -u devuelve 0), pero al intentar leer el archivo recibe Permission denied. En el host solo es un usuario normal, por lo que sus permisos no superan los de la cuenta que inició Docker.

Así que, aunque alguien comprometa el contenedor o use -v /:/host por error, el impacto se limita a esa cuenta de usuario.

Instalar Docker Rootless en Linux

Estos pasos se han recopilado de la documentación oficial de Docker. Este tutorial no lo ha instalado en el host (la instalación modifica la configuración del sistema). Consulta la documentación oficial antes de hacerlo.

# 1. Instala los paquetes necesarios (Ubuntu / Debian)
sudo apt-get install -y uidmap dbus-user-session

# 2. Si ya tienes instalado Docker normal, se recomienda desactivarlo primero
sudo systemctl disable --now docker.service docker.socket

# 3. Ejecuta el script de instalación como usuario normal (se instala con el paquete de Docker Engine)
dockerd-rootless-setuptool.sh install

# 4. Conecta el comando docker al socket rootless (añádelo a ~/.bashrc)
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock

# 5. Haz que se inicie al arrancar (seguirá ejecutándose después de cerrar sesión)
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)

Limitaciones de Rootless

LimitaciónDescripción y solución
Puertos por debajo de 1024Los usuarios normales no pueden enlazar los puertos 80 y 443 de forma predeterminada; -p 80:80 fallará. Usa otro puerto, como 8080, y reenvía el tráfico mediante un proxy inverso, o ajusta la configuración del sistema net.ipv4.ip_unprivileged_port_start
Límites de recursos--memory y --cpus requieren que el host use cgroup v2 y que systemd delegue permisos al usuario
Rendimiento de redEl tráfico se reenvía mediante componentes de red en espacio de usuario, por lo que el rendimiento es inferior al del modo normal
Ubicación de los datosLas imágenes y los Volume se guardan en el directorio personal del usuario (~/.local/share/docker) y no se comparten con los datos del modo normal
Algunas funcionesPor ejemplo, no se admiten la configuración de AppArmor ni algunos controladores de almacenamiento y red

El entorno de prueba de este tutorial tiene ajustado ip_unprivileged_port_start, así que -p 80:80 no dio error y no es posible reproducir aquí el primer punto de la tabla.

Podman: una herramienta de contenedores que no necesita un proceso residente

Podman es una herramienta de contenedores de código abierto liderada por Red Hat y la herramienta de contenedores predeterminada en distribuciones como Fedora y RHEL. Su mayor diferencia con Docker es la siguiente:

  • No tiene un proceso residente (daemonless): los comandos de Docker se ejecutan a través de dockerd; los de Podman crean contenedores directamente, y cada contenedor es un proceso hijo del proceso que ejecutaste.
  • Rootless de forma predeterminada: cuando un usuario normal ejecuta podman, está usando el modo rootless.
  • Los comandos son casi iguales a los de Docker: los comandos run, build, ps, images, exec, logs y sus opciones son iguales.

Prueba práctica: usar Podman como Docker

Ejecuta los comandos en la imagen quay.io/podman/stable (el cgroup v1 del host de prueba hace que Podman muestre una advertencia de obsolescencia, así que añadimos PODMAN_IGNORE_CGROUPSV1_WARNING=1 para ocultarla):

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman --version
podman version 5.8.7

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run --rm docker.io/library/alpine:3.23 echo hello from podman
hello from podman

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run --rm alpine:3.23 echo hi
hi

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman build -q -t express-app /tmp/express-app
npm notice
npm notice New major version of npm available! 11.19.0 -> 12.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
npm notice To update run: npm install -g npm@12.2.0
npm notice
npm warn using --force Recommended protections disabled.
a5a0b2b95ea756ad73b92ead62ece1770fdb26033c69749933fe03e3988dcb8d

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run -d --name app -p 3000:3000 express-app
74aeae727d68aacae32aee45d0705881fbea9b449ac1a5ee07c365b1490f42b3

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman exec app wget -qO- localhost:3000
{"message":"Hello from Express","node":"v24.21.0"}

$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman su podman -c 'podman run --rm docker.io/library/alpine:3.23 id -u; podman info --format {{.Host.Security.Rootless}}'
0
true
  • podman run se usa igual que Docker; puedes usar directamente opciones como --rm, -d y -p.
  • podman build usa directamente el Dockerfile de la aplicación Node.js; la imagen se construye sin cambios y el contenedor responde correctamente.
  • Conviene escribir el nombre completo de la imagen, por ejemplo, docker.io/library/alpine:3.23. Podman determina desde qué Registry descargar los nombres cortos (alpine:3.23) según la configuración del sistema, que puede variar entre distribuciones.
  • Por último, cambia al usuario normal podman para ejecutar los comandos: podman info muestra rootless como true. Dentro del contenedor sigue siendo uid 0, pero, al igual que en Rootless Docker, solo corresponde a un usuario normal del host.

Instalación

# Ubuntu / Debian
sudo apt-get install -y podman

# Fedora / RHEL
sudo dnf install -y podman

En Windows y macOS puedes instalar Podman Desktop, que ejecuta Linux en una máquina virtual igual que Docker Desktop.

Cómo elegir entre Docker, Rootless Docker y Podman

Docker (normal)Rootless DockerPodman
Proceso residentedockerd (root)dockerd (usuario normal)Ninguno
Permisos reales de root dentro del contenedorroot del host (con aislamiento)Usuario normalUsuario normal (en modo rootless)
Compatibilidad con los comandos de este tutorialTotalCasi totalEn su mayoría
Docker ComposeIntegradoIntegradopodman compose invoca una implementación externa
Herramientas del ecosistema (Portainer, Traefik, etc.)Compatibilidad completaCompatibilidad en la mayoría de los casosRequiere configurar un socket adicional
Adecuado paraDesarrollo general, Docker Desktop y la mayoría de los servidoresServidores compartidos por varios usuarios y requisitos de seguridad altosEntornos RHEL / Fedora y quienes no quieren un proceso residente

Para la mayoría de principiantes y proyectos generales, basta con usar Docker como se explica en este tutorial. Si varias personas inician sesión en el servidor o los requisitos de seguridad son altos, considera Rootless Docker o Podman.

Resumen

  • El dockerd de Docker en modo normal se ejecuta como root; tener acceso a Docker equivale a tener acceso root al host.
  • El modo Rootless usa un user namespace para que root dentro del contenedor corresponda a un usuario normal del host. En la prueba, no fue posible leer /etc/shadow del host.
  • Las principales limitaciones de Rootless son los puertos por debajo de 1024, la necesidad de cgroup v2 para los límites de recursos y el rendimiento de red.
  • Podman no tiene un proceso residente, es rootless de forma predeterminada y sus comandos son casi iguales a los de Docker; puedes construir directamente un Dockerfile.

Ejercicios

Ejercicio 1: Ejecuta docker run --rm alpine:3.23 id en Docker normal y luego ejecuta docker run --rm -u 1000:1000 alpine:3.23 id. ¿En qué se diferencia esto del modo Rootless?

Respuesta

-u 1000:1000 solo hace que el proceso dentro del contenedor se ejecute con uid 1000. dockerd sigue siendo root, y quien tenga acceso a Docker puede iniciar otro contenedor como root sin añadir -u. En el modo Rootless, todo Docker Engine se ejecuta como un usuario normal y ningún contenedor puede obtener permisos root en el host. Ambos modos pueden usarse a la vez.

Ejercicio 2: En Rootless Docker, falla docker run -d -p 80:80 nginx:1.30-alpine. ¿Qué dos opciones tienes para resolverlo?

Respuesta
  1. Usa un puerto superior a 1024, por ejemplo, -p 8080:80, y luego redirige el puerto 80 al 8080 mediante un proxy inverso en el host o una regla del firewall.
  2. Ajusta la configuración del sistema para permitir que los usuarios normales enlacen el puerto 80: sudo sysctl net.ipv4.ip_unprivileged_port_start=80 (escribe el valor en /etc/sysctl.conf para conservarlo después de reiniciar).

Preguntas frecuentes

¿El modo Rootless es más lento?

Los programas dentro de los contenedores se ejecutan a una velocidad similar. La red (que usa reenvío en el espacio de usuario) y algunas operaciones del sistema de archivos son algo más lentas que en el modo normal. En la mayoría de sitios web y tareas de desarrollo apenas se nota.

¿Docker Desktop necesita Rootless?

No. En Windows y macOS, Docker Desktop ejecuta Docker Engine dentro de una máquina virtual, aislándolo de tu equipo. Rootless se usa principalmente en servidores y escritorios Linux.

¿Puedo sustituir directamente el comando docker por podman?

En la mayoría de los casos, sí. Las opciones de podman run, podman build y podman ps son casi iguales, y mucha gente configura directamente alias docker=podman. Las diferencias son que no hay un daemon permanente, rootless está habilitado de forma predeterminada y las herramientas relacionadas con Compose y el socket de Docker requieren configuración adicional.

¿Podman puede usar compose.yaml?

Sí. podman compose llama a la implementación de Compose instalada en el sistema (docker-compose o podman-compose) para procesar compose.yaml. La mayoría de las configuraciones funcionan.

Actualizado 13 de octubre de 2026