Skip to content

How to Make MinIO `temporaryUrl()` Work in Laravel

MinIO's temporaryUrl() doesn't work out of the box with Docker. Here's the minimal config that fixes it.

· 4 min read ·
Laravel MinIO S3 Docker

TL;DR: MinIO’s temporaryUrl() fails in Docker because the generated URL uses the internal container hostname. Fix it by setting AWS_URL to your external MinIO endpoint and configuring the url key 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:

  1. Open the MinIO Console: http://localhost:8900
  2. Log in with:
    • Username: example
    • Password: password
  3. Navigate to Identity > Users.
  4. Click “Create User” and generate:
    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
  5. 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:

FieldDescription
AWS_URLYour Laravel app’s URL followed by :minio-port/bucketname.
AWS_ENDPOINTPoints to the MinIO container name with port 9000.
AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEYCredentials 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.