TL;DR: MinIO’s
temporaryUrl()fails in Docker because the generated URL uses the internal container hostname. Fix it by settingAWS_URLto your external MinIO endpoint and configuring theurlkey in your S3 disk config. The signed URL parameters still work — only the base URL changes.
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.
Frequently Asked Questions
Why does MinIO temporaryUrl() fail in Docker?
The generated presigned URL uses the internal Docker hostname (like minio:9000) which isn’t accessible from the browser. You need to configure an externally reachable URL via AWS_URL in your .env.
What’s the difference between AWS_ENDPOINT and AWS_URL in Laravel?
AWS_ENDPOINT is used by the S3 client to connect to MinIO internally. AWS_URL is used to generate public-facing URLs. In Docker, the endpoint might be http://minio:9000 while the URL should be http://localhost:9000.
Does this fix work with production MinIO deployments?
Yes. In production, set AWS_URL to your public MinIO domain (e.g., https://storage.example.com). The presigned URL parameters are generated by the S3 client regardless of the base URL.