How to Make MinIO temporaryUrl() Work in Laravel
Laravel’s Storage::disk(‘s3’)->temporaryUrl() generates signed URLs for private files in MinIO. Sounds simple. But if you’re running MinIO inside Docker, the URLs it generates will point to the container’s internal hostname and break in your browser.
Here’s the minimal config that actually works.
Step 1: Set a Stable Container Name
Modify your MinIO service in docker-compose.yml to set a fixed container name:
minio:
image: "minio/minio:latest"
ports:
- "9000:9000"
- "8900:8900"
container_name: dev.minio.example.com
environment:
MINIO_ROOT_USER: "example"
MINIO_ROOT_PASSWORD: "password"
volumes:
- "example-minio:/data/minio"
networks:
- example-network
command: minio server /data/minio --console-address ":8900"
Why this works
The container name doubles as the hostname. Laravel uses it to reach MinIO inside Docker, and you’ll map it to 127.0.0.1 on your machine (next step) so your browser can reach it too. No VIRTUAL_HOST, no network aliases needed.
Step 2: Update /etc/hosts for Local Access
On your local machine, add the following entry to /etc/hosts (Linux/macOS) or C:\Windows\System32\drivers\etc\hosts (Windows):
127.0.0.1 dev.minio.example.com
Now http://dev.minio.example.com resolves to your local machine. The signed URLs will work in your browser.
Step 3: Generate MinIO Credentials
Don’t use the root credentials in your app. Generate a dedicated access key from the MinIO console:
- Open the MinIO Console: http://localhost:8900
- Log in with:
- Username:
example - Password:
password
- Username:
- Navigate to Identity > Users.
- Click “Create User” and generate:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- Assign read and write policies to the user.
Step 4: Configure Laravel
Update your .env with the credentials you just created:
AWS_ACCESS_KEY_ID=your_generated_access_key
AWS_SECRET_ACCESS_KEY=your_generated_secret_key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=test-storage
AWS_URL=http://dev.marketplace-api.example.com:9000/test-storage
AWS_ENDPOINT=http://dev.minio.example.com:9000
AWS_USE_PATH_STYLE_ENDPOINT=true
What each field does:
| Field | Description |
|---|---|
| AWS_URL | Your Laravel app’s URL followed by :minio-port/bucketname. |
| AWS_ENDPOINT | Points to the MinIO container name with port 9000. |
| AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY | Credentials generated in MinIO Console. |
After updating the .env file, clear Laravel’s configuration and cache:
php artisan config:clear
php artisan cache:clear
Step 5: Generate a Temporary URL in Laravel
That’s it. Generate a signed URL:
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
$filePath = 'documents/sample.pdf';
$expiration = Carbon::now()->addMinutes(60); // URL expires in 1 hour
$url = Storage::disk('s3')->temporaryUrl($filePath, $expiration);
return response()->json(['temporary_url' => $url]);
The whole trick
The core problem is hostname resolution. Docker containers use internal hostnames, but your browser needs to reach the same hostname from outside Docker. Setting the container name to a domain and mapping it in /etc/hosts solves both sides at once.
Five minutes of config. No nginx proxy, no network aliases, no custom middleware.