Could we help you? Please click the banners. We are young and desperately need the money
If you’ve ever tried accessing uploaded files in a Laravel app running in Docker using Sail, only to be met with a dreaded 404 or missing file icon, you're not alone. Laravel’s "php artisan storage:link" command creates a symbolic link between "storage/app/public" and "public/storage" — a crucial step to serve uploaded files publicly. However, this doesn't "just work" when you're using Docker and Sail unless it's properly configured.
In this guide, we'll walk through how to mount a public volume in a Laravel 12.x application using Docker and Sail, and how to ensure your file uploads are accessible.
When your application handles file uploads — profile pictures, reports, PDFs, etc. — Laravel stores them in "storage/app/public". These files aren't web-accessible by default. The "php artisan storage:link" command creates a symbolic link in "public/storage" pointing to the real directory.
This is essential for your app to serve those files through HTTP.
$ php artisan storage:link
The [public/storage] directory has been linked.
Simple, right? But in Dockerized Laravel apps using Sail, things aren’t quite that straightforward.
Let’s fix that by running a proper sequence inside your Sail Docker container.
If you haven’t already, boot up your Laravel environment using:
$ ./vendor/bin/sail up -d
We’ll enter the container using Docker’s "exec" command. If your container is called "laravel.test" (the default), run:
$ docker exec -it laravel.test bash
Inside the container, you’ll usually start as the "root" user. Laravel’s Sail environment expects you to be the "sail" user when running artisan commands. Switch using:
# su sail
Now you're in the right context. Run the artisan command:
$ php artisan storage:link
If successful, you'll see:
The [public/storage] directory has been linked.
The symbolic link that Laravel creates is only valid inside the Docker container. If you're serving your app using Sail’s nginx config, this works fine because nginx reads paths within the container’s file system.
However, if you're accessing files or debugging from your host machine, make sure that your host has access to the volume. That’s where Docker volumes come in (but not our focus today).
public/
├── index.php
├── ...
└── storage → ../storage/app/public # symbolic link created
By default, everything in "storage/app/public" becomes web-accessible via "/storage". Ensure you don’t accidentally place private files there.
Creating a symbolic link with "php artisan storage:link" is an essential step in any Laravel application that handles uploads. But inside Docker and Laravel Sail, you need to be aware of container boundaries and user permissions.
By using "docker exec", switching to the "sail" user, and running the command inside the container, you ensure your files are properly linked and publicly accessible.
Happy coding, and may your uploads never go missing again!