En una imagen Docker, el proceso suele ejecutarse como el usuario configurado por la imagen base; muchas parten de root. Crear una cuenta propia y activar USER permite ejecutar la aplicación con menos privilegios. También hay que preparar los permisos de sus archivos y directorios: cambiar de usuario sin hacerlo puede dejar la aplicación sin acceso a lo que necesita.
En esta guía crearás una cuenta con UID y GID definidos, limitarás la escritura a un directorio de datos y comprobarás la identidad y los permisos al arrancar el contenedor.
👉 Y recuerda, si quieres aprender más de Linux, visita el curso de Linux gratis.
🐳 Si quieres aprender más de Docker, visita el curso de Docker gratis.
Qué cambia al ejecutar un contenedor como usuario no root
El usuario creado dentro de la imagen es la identidad predeterminada de los procesos del contenedor. No es una cuenta del host ni inicia sesión en el servidor. En una configuración habitual, root dentro de un contenedor tiene menos alcance que root en el host, pero conserva privilegios amplios dentro del contenedor y puede resultar peligroso si además se conceden capacidades, dispositivos o montajes sensibles.
Crear la cuenta no basta: si no se declara USER después, el proceso puede seguir ejecutándose como root. La instrucción fija el usuario y el grupo predeterminados para las instrucciones posteriores y para el proceso que inician CMD o ENTRYPOINT. Al ejecutar el contenedor, opciones como –user pueden reemplazar esa identidad.
Crear un usuario de aplicación en Alpine
Este Dockerfile utiliza las herramientas de Alpine para crear el grupo y la cuenta app con UID y GID 10001. Prepara /app/data como directorio escribible y establece app como usuario predeterminado. El comando sleep mantiene activo este contenedor de prueba si no se indica otra orden. Los comandos de comprobación de abajo sustituyen CMD; en una imagen real, usa el proceso principal de tu aplicación en forma exec.
FROM alpine:3.22
RUN addgroup -g 10001 -S app \
&& adduser -u 10001 -S -D -G app app \
&& mkdir -p /app/data \
&& chown app:app /app/data
WORKDIR /app
USER app:app
CMD ["sleep", "3600"]Qué configura cada parte
| Instrucción o valor | Qué hace | Por qué importa |
|---|---|---|
| UID y GID 10001 | Identifican numéricamente a la cuenta y al grupo app. | Permiten repetir la identidad entre imágenes y relacionarla con permisos de archivos. Comprueba que no colisione con otra cuenta de la imagen. |
| addgroup y adduser | Crean un grupo y una cuenta de sistema sin contraseña de inicio de sesión. | La aplicación recibe una identidad propia en lugar de heredar root. |
| /app/data y chown | Crean el directorio de escritura y se lo asignan a app. | El proceso solo necesita permiso de escritura en las rutas que realmente modifica. |
| WORKDIR /app | Define el directorio de trabajo de los comandos y del proceso. | No concede permisos por sí mismo; el acceso depende de los permisos del directorio. |
| USER app:app | Selecciona explícitamente la cuenta y el grupo predeterminados. | Las instrucciones RUN posteriores y el proceso de inicio usan esa identidad, salvo que otra configuración la reemplace. |
| CMD | Mantiene activo el contenedor de prueba cuando no se sustituye CMD al ejecutar docker run. | En producción, el proceso real debe permanecer en primer plano para que Docker observe su ciclo de vida. |
Construir la imagen y comprobar el acceso
Ejecuta estos comandos desde el directorio que contiene el Dockerfile. El primer comando de ejecución sustituye CMD por id para mostrar el usuario efectivo. El siguiente ejecuta una comprobación aparte y crea un archivo en el directorio autorizado.
docker build -t usuario-demo:1.0 .
docker run --rm usuario-demo:1.0 id
docker run --rm usuario-demo:1.0 sh -c 'id && touch /app/data/prueba && ls -ln /app/data/prueba'La salida de id debe mostrar UID 10001 y el grupo app. El segundo comando debe terminar sin error y listar el archivo recién creado. Si la escritura falla, revisa primero el propietario y los permisos de /app/data; no amplíes todo el sistema de archivos.
Asignar el propietario al copiar los archivos
Los archivos que COPY añade a una imagen son root:root por defecto. Si la aplicación solo necesita leerlos, esos permisos pueden bastar; si debe modificarlos, copia únicamente las rutas necesarias con el propietario de la aplicación. El grupo y el usuario con nombre deben existir en la imagen antes de usar sus nombres en –chown.
COPY --chown=app:app ./src/ /app/Coloca esta instrucción después de crear app. Evita asignar propiedad de aplicación a todo el sistema o dar permisos universales con chmod 777. Mantén como root los archivos que la aplicación solo deba leer y reserva la escritura para directorios concretos, como datos, caché o subidas.
Permisos al usar volúmenes y bind mounts
Los permisos de la imagen no solucionan automáticamente los permisos de una ruta montada desde el host. Un bind mount cubre el contenido que había en esa ruta del contenedor y presenta los archivos del host; la aplicación debe poder acceder a ellos con los permisos y la identidad numérica que correspondan. Prepara la carpeta del host para el UID/GID de la aplicación o monta el contenido como solo lectura si no tiene que cambiarlo.
Con un volumen Docker vacío, Docker copia por defecto el contenido que ya existe en el directorio de destino de la imagen. Por eso, preparar allí los datos y su propietario puede servir como inicialización. Un volumen ya existente conserva sus propios datos y permisos; inspecciónalos antes de atribuir un fallo al Dockerfile.
Errores frecuentes al crear la cuenta
- Crear el usuario y olvidar USER. La imagen puede seguir ejecutando la aplicación como root. Comprueba el usuario predeterminado en ejecución con id.
- Cambiar a USER antes de terminar las tareas administrativas. La instalación de paquetes, la creación de cuentas y algunos cambios de permisos necesitan privilegios; coloca USER después de esas tareas.
- Suponer que todos los archivos deben pertenecer a la aplicación. Da escritura solo a las rutas que la aplicación modifica; el código y la configuración pueden permanecer de solo lectura.
- Confundir COPY –chown con el dueño de un bind mount. La opción ajusta los archivos copiados a la imagen, no cambia el propietario de los archivos del host que se montan después.
- Usar –user sin revisar sus efectos. Puede reemplazar la cuenta del Dockerfile y, con un UID numérico sin entrada en /etc/passwd, ciertas aplicaciones pueden no encontrar nombre o directorio HOME.
Qué aporta y qué no aporta USER a la seguridad
Ejecutar como usuario no root reduce los privilegios iniciales del proceso y limita el impacto de algunos fallos. No convierte por sí solo el contenedor en una frontera de seguridad completa ni sustituye la actualización de imágenes, el control de capacidades, la protección de secretos o la revisión de los montajes.
Tampoco es lo mismo que el modo rootless de Docker: USER cambia la identidad del proceso dentro del contenedor; rootless ejecuta el daemon de Docker y los contenedores con una cuenta no privilegiada del host. Usa cada mecanismo según el riesgo y la arquitectura del sistema.
Lecciones relacionadas del curso Docker
- Instrucción USER Dockerfile
- Instrucción WORKDIR Dockerfile
- Instrucción RUN Dockerfile
- Qué hacer si hay problemas de permisos
- Buenas prácticas Dockerfile
Documentación oficial y recursos
- Referencia de Dockerfile: USER
- Referencia de Dockerfile: COPY –chown
- Volúmenes Docker
- Bind mounts en Docker
- Modo rootless de Docker
Antes de integrar este patrón en una imagen real, valida con el mismo UID/GID que usará el servicio: lectura de configuración, escritura solo en las rutas previstas y acceso a cada volumen. Así se detectan los problemas de permisos antes del despliegue sin recurrir a privilegios amplios.

