Tutorial

Permisos de volúmenes Docker: corrección en 3 comandos

Despliegue10 min de lectura10 pasos

La aplicación arranca, el contenedor corre, pero los logs muestran `Permission denied` y nada se guarda. Este problema afecta a prácticamente todas las aplicaciones self-hosted en el momento en que montas un directorio del host en Docker. La causa es siempre la misma: el UID del usuario que corre dentro del contenedor no coincide con el propietario del directorio en el host. Esta guía explica el mecanismo, da los comandos de diagnóstico y propone la corrección adecuada para cada caso — sin ejecutar nunca los contenedores como root.

Contenido· El mecanismo: por qué Docker se niega a escribir1/9
  1. 01El mecanismo: por qué Docker se niega a escribir
  2. 02Las aplicaciones más afectadas y sus UIDs
  3. 03Diagnóstico: identificar el problema en menos de 2 minutos
  4. 04Corrección caso por caso: chown en el directorio del host
  5. 05Variantes: bind-mounts, volúmenes nombrados y user:
  6. 06Casos NFS y montajes remotos
  7. 07Métodos de corrección: comparación
  8. 08Resolución de problemas: 4 errores clásicos con su mensaje exacto
  9. 09Conclusión

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 (usuario paperless): gestiona los directorios consume, export, media y data
  • Grafana — UID 472 (usuario grafana): escribe su base SQLite y plugins en /var/lib/grafana
  • Nextcloud (imagen Debian) — UID 33 (usuario www-data): directorio de datos, config y logs
  • Immich — UID 1000 (usuario node): librería de fotos, miniaturas y base de datos ML
  • Gitea — UID 1000 (usuario git): 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.

  1. 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 denied o cannot create directory confirma el diagnóstico.

  2. Identificar el UID del proceso dentro del contenedor

    docker exec <nombre-contenedor> id

    Salida típica: uid=472(grafana) gid=0(root). Apunta el UID — aquí 472.

  3. Verificar el propietario del directorio en el host

    ls -ln /opt/grafana/data

    Salida 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.

  4. Usar stat para un diagnóstico completo

    stat /opt/grafana/data

    Revisa las líneas Uid: y Gid:. Si muestran (0/root) mientras tu contenedor corre como 472, el problema está confirmado.

  5. 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.

  1. Grafana (UID 472)

    mkdir -p /opt/grafana/data
    chown -R 472:472 /opt/grafana/data

    En tu compose.yml:

    volumes:
      - /opt/grafana/data:/var/lib/grafana
  2. Nextcloud (UID 33, imagen Debian)

    mkdir -p /opt/nextcloud/{data,config,apps}
    chown -R 33:33 /opt/nextcloud/data
    chown -R 33:33 /opt/nextcloud/config

    Nota: la imagen Alpine usa UID 82. Verifica con docker exec <contenedor> id www-data si no estás seguro de la variante.

  3. 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/data
  4. Immich (UID 1000)

    mkdir -p /opt/immich/{library,thumbnails,encoded-video,profile}
    chown -R 1000:1000 /opt/immich

    Nota: las variables de entorno PUID/PGID NO funcionan con las imágenes oficiales de Immich.

  5. Verificar tras la corrección

    ls -ln /opt/grafana/data

    Debe 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/data

Este 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 0

Métodos de corrección: comparación

Desplace la tabla

MétodoVentajasRiesgos / Limitaciones
chown UID:GID en directorio del hostSimple, universal, compatible con todas las imágenesHay que conocer el UID exacto; hay que repetirlo si se recrea el directorio
user: UID:GID en compose.ymlSin chown; portable entre hostsLa imagen debe soportar UIDs arbitrarios; puede romper archivos internos
Volúmenes nombrados DockerPermisos gestionados automáticamente en el primer arranqueMenos transparente; migrar datos existentes es más complejo
--privileged o chmod 777Resuelve el problema de inmediatoPELIGRO: 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. Aplica chown -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/*.db o 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 el chown manualmente 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.

Un VPS listo para el self-hosting

Despliega tus aplicaciones Docker en un VPS ServOrbit con acceso root, IPv4 dedicada y snapshots automáticos. Desde 99 DH/mes.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva