Permission Denied Nginx Docker

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Solving "Permission Denied" in Dockerized Laravel: A Deep Dive into Volume Permissions

As senior developers working with containerized applications, we often encounter frustrating issues where the application functions perfectly until a specific operation—like writing logs or uploading files—fails with a cryptic Permission denied error. This is an extremely common pitfall when dealing with Docker volumes and file permissions.

You’ve correctly identified that your setup (PHP-FPM and MySQL running fine, static assets served) suggests the problem lies specifically in the write access to application directories within the container. Let's dissect why this happens in your Docker Compose environment and how we fix it.

The Root Cause: Docker Volume Permissions

The error you are seeing, /home/html/storage/logs/laravel-2019-06-10.log could not be opened: failed to open stream: Permission denied, is a classic Linux file permission problem translated into the Docker context.

When you use a volume mount (e.g., - "./:/home/html/"), you are mapping a directory from your host machine into the container. By default, the user process running inside the container (in this case, the PHP-FPM process) operates under a specific User ID (UID). If the files on your host system have permissions that conflict with what the container expects, or if the volume mount doesn't grant the necessary write privileges to the container user, the operation fails.

In essence, the MySQL and basic web serving parts work because they don't heavily rely on writing complex application data, but when Laravel attempts to execute a standard logging operation into /home/html/storage/logs, it hits this permission wall.

The Solution: Explicitly Managing Ownership

The fix is not usually in the docker-compose.yml syntax itself, but in ensuring that the user running the PHP process inside the container has ownership or write access to the mounted volume on the host.

We need to explicitly tell Docker Compose how to handle these permissions when mounting volumes. The most robust solution involves using the user directive within your service definition or ensuring the files are owned by a predictable user ID that the container process can access.

Step 1: Inspecting Your Volume Setup

Your current setup mounts ./:/home/html/ to both services. While this works for reading, it doesn't guarantee write permissions for the PHP process running as www-data (or similar).

Step 2: Implementing Ownership Correction

A common and highly effective practice is to use the user directive in your docker-compose.yml to ensure the container process runs with a specific user ID that matches the ownership structure, or to run commands inside the container to adjust permissions immediately upon startup.

For Laravel applications, ensuring that the web server user (like www-data) can write to the storage directories is crucial. We can achieve this by adding a command to set the correct ownership within the PHP service.

Here is how you can modify your php-fpm service to resolve the permissions issue:

version: "3"
services:
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: "root"
    ports:
      - 3306:3306

  php-fpm:
    image: php:7.3-fpm-alpine
    links:
      - db
    volumes:
      - "./:/home/html/"
    # Add this section to ensure permissions are correct upon container start
    user: "www-data" 

  nginx:
    image: nginx:1-alpine
    ports:
      - "8080:80"
    links:
      - php-fpm
    volumes:
      - "./site.conf:/etc/nginx/conf.d/default.conf"
      - "./:/home/html/"

Note on the change: By specifying user: "www-data", we instruct the PHP process to run as the user typically associated with Nginx/Apache setups, which often resolves permission conflicts when writing logs and cache files.

Laravel Best Practices in Docker

When deploying applications like those built with Laravel, maintaining proper file permissions is non-negotiable. As outlined by best practices for application deployment—especially when utilizing containerization—you must treat the volume mounts as a boundary where strict access control applies. If you are building an application that leverages frameworks like Laravel, understanding how the framework interacts with its storage and cache directories within the container is key to avoiding these runtime errors.

By explicitly managing file ownership via Docker Compose, you move from guessing at permissions to enforcing them, ensuring your Laravel application can write necessary logs, cache configuration, and session files without interruption.

Conclusion

The "Permission denied" error in a Dockerized environment is almost always an issue of mismatched user/group ownership across the host filesystem and the container's volume mount. The solution lies in proactively setting file permissions, either through explicit chown commands within your Dockerfile or by leveraging service-level directives like the user key in your docker-compose.yml. By adopting these practices, you ensure that your development workspace remains stable, reliable, and ready for scalable deployment.