El mecanismo: por qué Docker se niega a escribir
Docker comparte el kernel Linux del host. Cuando un contenedor monta un directorio del host (bind-mount), el sistema de archivos aplica las mismas reglas de permisos POSIX. Un archivo propiedad del UID 1000 en el host sigue siendo propiedad del UID 1000, independientemente de si se llama alice en el host o paperless en el contenedor. Si el proceso en el contenedor corre como UID 472 y el directorio pertenece a root (UID 0), las escrituras son denegadas — aunque hayas usado chmod 755.
Trampa clásica: creas el directorio con tu sesión SSH (UID 1000), luego lanzas un contenedor Grafana que corre como UID 472. Grafana intenta escribir su base SQLite en /var/lib/grafana montado desde /opt/grafana/data — que pertenece a tu usuario SSH. Resultado: GF_PATHS_DATA='/var/lib/grafana' is not writable.
Las aplicaciones más afectadas y sus UIDs
Los problemas de permisos Docker afectan principalmente a las aplicaciones que se ejecutan con un UID específico diferente al del host.
- Paperless-ngx — UID
1000(usuariopaperless): gestiona los directoriosconsume,export,mediaydata - Grafana — UID
472(usuariografana): escribe su base SQLite y plugins en/var/lib/grafana - Nextcloud (imagen Debian) — UID
33(usuariowww-data): directorio de datos, config y logs - Immich — UID
1000(usuarionode): librería de fotos, miniaturas y base de datos ML - Gitea — UID
1000(usuariogit): repositorios, claves SSH, logs y base SQLite por defecto
Diagnóstico: identificar el problema en menos de 2 minutos
Antes de corregir, confirma que es un problema de permisos e identifica el UID implicado. Tres comandos son suficientes.
Leer el mensaje de error exacto
Consulta los logs del contenedor afectado:
docker logs <nombre-contenedor> 2>&1 | grep -i 'permission\|denied\|cannot\|mkdir'Un mensaje
permission deniedocannot create directoryconfirma el diagnóstico.Identificar el UID del proceso dentro del contenedor
docker exec <nombre-contenedor> idSalida típica:
uid=472(grafana) gid=0(root). Apunta el UID — aquí472.Verificar el propietario del directorio en el host
ls -ln /opt/grafana/dataSalida
drwxr-xr-x 2 0 0 ...indica que el directorio pertenece a root (UID 0). El UID 472 solo tiene derechos «other» — lectura y ejecución, sin escritura.Usar stat para un diagnóstico completo
stat /opt/grafana/dataRevisa las líneas
Uid:yGid:. Si muestran(0/root)mientras tu contenedor corre como 472, el problema está confirmado.Inspeccionar la configuración del contenedor
docker inspect <nombre-contenedor> | grep -A5 'Mounts'Este comando lista todos los bind-mounts y volúmenes nombrados activos.
Corrección caso por caso: chown en el directorio del host
La corrección básica es un chown del directorio del host hacia el UID esperado por el contenedor. Aquí los comandos para las aplicaciones más comunes.
Grafana (UID 472)
mkdir -p /opt/grafana/data chown -R 472:472 /opt/grafana/dataEn tu
compose.yml:volumes: - /opt/grafana/data:/var/lib/grafanaNextcloud (UID 33, imagen Debian)
mkdir -p /opt/nextcloud/{data,config,apps} chown -R 33:33 /opt/nextcloud/data chown -R 33:33 /opt/nextcloud/configNota: la imagen Alpine usa UID 82. Verifica con
docker exec <contenedor> id www-datasi no estás seguro de la variante.Paperless-ngx (UID 1000)
mkdir -p /opt/paperless/{consume,export,media,data} chown -R 1000:1000 /opt/paperless/consume chown -R 1000:1000 /opt/paperless/export chown -R 1000:1000 /opt/paperless/media chown -R 1000:1000 /opt/paperless/dataImmich (UID 1000)
mkdir -p /opt/immich/{library,thumbnails,encoded-video,profile} chown -R 1000:1000 /opt/immichNota: las variables de entorno
PUID/PGIDNO funcionan con las imágenes oficiales de Immich.Verificar tras la corrección
ls -ln /opt/grafana/dataDebe mostrar
drwxr-xr-x 2 472 472 .... Luego reinicia el contenedor:docker compose restart grafana docker logs grafana --tail 20
Variantes: bind-mounts, volúmenes nombrados y user:
El chown en el directorio del host funciona para bind-mounts, pero Docker ofrece otros enfoques según el contexto.
Directiva user: en compose.yml. Algunas imágenes están diseñadas para aceptar un UID arbitrario vía user:. Esto evita el chown si tu directorio pertenece a tu usuario del host:
services:
app:
image: mi-imagen
user: "1000:1000"
volumes:
- /home/user/data:/app/dataEste enfoque solo funciona si la imagen no requiere archivos internos con UID específico. Paperless-ngx soporta este modo; Grafana no.
Volúmenes nombrados de Docker. Con un volumen Docker nombrado, Docker gestiona el directorio bajo /var/lib/docker/volumes/. En la primera escritura, el directorio se crea con los permisos del proceso del contenedor — sin problemas de permisos al arrancar, pero migrar datos existentes requiere un paso de copia explícito.
Casos NFS y montajes remotos
Los montajes NFS añaden una capa de complejidad: el servidor NFS aplica sus propias reglas de UID. Si el servidor exporta con root_squash (por defecto), el acceso root desde el cliente se reduce a nobody.
Soluciones:
1. Configurar la exportación NFS con all_squash,anonuid=472,anongid=472 para Grafana.
2. Usar no_root_squash solo si controlas completamente la red (riesgo de seguridad).
3. Para CIFS/SMB, pasar uid=33,gid=33 en las opciones de montaje para Nextcloud.
En /etc/fstab:
//servidor/compartido /opt/nextcloud/data cifs uid=33,gid=33,credentials=/etc/cifs-creds,iocharset=utf8 0 0Métodos de corrección: comparación
Desplace la tabla
| Método | Ventajas | Riesgos / Limitaciones |
|---|---|---|
| chown UID:GID en directorio del host | Simple, universal, compatible con todas las imágenes | Hay que conocer el UID exacto; hay que repetirlo si se recrea el directorio |
| user: UID:GID en compose.yml | Sin chown; portable entre hosts | La imagen debe soportar UIDs arbitrarios; puede romper archivos internos |
| Volúmenes nombrados Docker | Permisos gestionados automáticamente en el primer arranque | Menos transparente; migrar datos existentes es más complejo |
| --privileged o chmod 777 | Resuelve el problema de inmediato | PELIGRO: expone el host y todos sus procesos; nunca usar en producción |
Resolución de problemas: 4 errores clásicos con su mensaje exacto
Estos son los cuatro errores Docker más comunes relacionados con permisos, con el mensaje de error exacto y su solución.
mkdir: cannot create directory '/var/lib/grafana/plugins': Permission denied→ El directorio del host no pertenece al UID 472. Aplicachown -R 472:472 /opt/grafana/data.[Errno 13] Permission denied: '/usr/src/paperless/media'→ Paperless-ngx no puede escribir en su directorio media. Verifica que el bind-mount pertenece al UID 1000 en el host.Could not create lock file /var/lib/grafana/.~lock.grafana.db→ Grafana puede leer el directorio pero no escribir. Problema de permisos en archivos existentes:chown 472:472 /opt/grafana/data/*.dbo elimina el lock file.chown: changing ownership of '/data': Operation not permitted(al arrancar el contenedor) → La imagen intenta corregir permisos pero no puede porque no corre como root. Ejecuta elchownmanualmente en el host ANTES de arrancar el contenedor.
SELinux y AppArmor: los flags :z y :Z en bind-mounts. En sistemas con SELinux activo (CentOS, RHEL, Fedora), un bind-mount puede bloquearse aunque los permisos POSIX sean correctos. Docker proporciona dos sufijos: :z reetiqueta el contenido para compartirlo entre múltiples contenedores, y :Z para acceso privado (un solo contenedor). Ejemplo: - /opt/grafana/data:/var/lib/grafana:z. Sin este flag en un sistema SELinux, obtendrás Permission denied incluso tras un chown correcto. Para verificar si SELinux está bloqueando: ausearch -m AVC -ts recent | grep docker.
Conclusión
El Permission denied en los logs de Docker nunca es inevitable. La solución en tres comandos: docker exec <contenedor> id para encontrar el UID, ls -ln <directorio-host> para confirmar el propietario actual, y chown -R <UID>:<GID> <directorio-host> para corregirlo. La regla de oro: crea los directorios del host con el propietario correcto antes de arrancar el contenedor, no después. En un VPS dedicado, esta disciplina evita el 90% de los incidentes de datos perdidos silenciosamente al reiniciar.