abdulwolfiz's avatar

Serving Static Assets in Dockerized Laravel Environments

Hey team, I wanted to bring up an interesting point about serving static assets in our Dockerized Laravel setup. Currently, we have the public folder inside our backend container, but Nginx on the host cannot directly access it. This has caused issues where assets like /logo.png work locally in Docker Desktop but fail in production unless accessed via /public/logo.png or through a Laravel route.

There are a few potential approaches to handle this:

Mount the public folder from container to host, so host Nginx can serve static assets directly.

Move Nginx into Docker, link it with the backend container, and serve assets internally — more self-contained and consistent across environments.

Continue using a Laravel route fallback, though this is less efficient and may bypass caching/static optimizations.

I’d love to hear everyone’s thoughts, experiences, and suggestions on the best approach. Especially interested in what works best in production environments while keeping deployment simple and consistent.

0 likes
5 replies
DigitalArtisan's avatar

You said quoate but Nginx on the host cannot directly access it. This has caused issues where assets like /logo.png work locally in Docker Desktop but fail in production unless accessed via /public/logo.png or through a Laravel route.

Sounds like a configuration problem.

What do you have containerized?

abdulwolfiz's avatar

You're right — it is essentially a configuration issue, so here’s the setup for clarity:

What's containerized:

Laravel backend (PHP-FPM) is running inside Docker

Nginx is not containerized — it's installed on the host machine

Nginx on the host is pointing to a directory like /home/plannxt-loginxt/backend/public, but the actual public folder with assets is inside the Docker container at /var/www/public

So Nginx on the host cannot directly see or serve files like logo.png, favicon.ico, or images inside /vehicles, /vehicletype, etc.

Result:

Inside Docker Desktop or local development → works because Nginx is inside Docker

On production → fails because the host Nginx has no access to container’s filesystem

That’s why /logo.png returns 404, unless accessed through /public/logo.png or a Laravel route — which means Laravel is serving it, not Nginx.

Current suspicion: The public directory is not mounted or synced between host and container, so the host’s Nginx is basically serving an empty or outdated public folder.

Happy to share my Nginx config or docker-compose setup if needed.

ian_h's avatar

We encountered this probably a decade ago at a company I used to work at and ever since, I've personally opted to "break conventions" for my own projects and for production, bundle nginx with my php-fpm container for this very reason. This then sits behind a host-installed nginx working as a reverse proxy.

abdulwolfiz's avatar

Yeah, that makes a lot of sense, and honestly we’re hitting the exact same pain point right now.

Since our Laravel app (PHP-FPM) is containerized but Nginx on production is running on the host, the host Nginx simply cannot see the container’s /var/www/public files. That’s why simple assets like /logo.png fail in production but work perfectly in local Docker Desktop.

Your approach — bundling Nginx + PHP-FPM inside the same container and letting the host Nginx act purely as a reverse proxy — actually solves the root problem:

No mismatch between host filesystem vs. container filesystem

Nginx serves exactly what PHP-FPM sees

No need for hacks like /public/... routing or custom Laravel routes to serve files

Predictable, consistent asset behaviour across environments

I’m seriously considering switching to this approach as well, because maintaining separate host-level Nginx + container-level PHP-FPM gets messy fast, especially with static assets and mount paths.

If you’re open to it, I’d love to see an example of how you structure that combined Nginx + PHP-FPM container.

ian_h's avatar

For me, it works really well as for 99% of projects, even the reverse proxy configs are the same less for the domain/log names/ssl certs etc. This mostly only deviates for me if I have a project that also uses reverb for websockets, where I need to define another section to handle those.

Bear with me.. the projects and config for these are all in private repos.. and the final docker container is built through github actions.. but I'll throw up a dummy repo that has these configs in them and share the link later today.. bit too much to start copy/pasting here.

The only "gotchya" I found with this originally was when working with Inertia-based projects and hitting the assets because the docker container only runs as HTTP, the certs all sit in the reverse proxy for HTTPS. In AppServiceProvider, I originally had just:

if ($this->app->environment('production')) {
    URL::forceScheme('https');
}

But for Inertia apps, I also had to add:

$this->app['request']->server->set('HTTPS', true);

I added this under the first line to scope it into the production environment as locally, everything is just HTTP.

I'll get a repo up later.

Please or to participate in this conversation.