In Laravel's s3 disk configuration, what do AWS_ENDPOINT, AWS_USE_PATH_STYLE_ENDPOINT and AWS_URL change when the disk points at an S3-compatible service?
answer
- same driver, different host
- endpoint key read from AWS_ENDPOINT
- path-style: bucket in the path
- url key: base for public links
- R2 endpoint disables retain_visibility
basics
~20 sAWS_ENDPOINT feeds the s3 disk's endpoint key, sending API calls to another S3-compatible host. AWS_USE_PATH_STYLE_ENDPOINT puts the bucket in the URL path instead of the hostname. AWS_URL sets the base URL Laravel uses for public file links.
solid answer
~40 sThe skeleton's `s3` disk maps `endpoint` to `AWS_ENDPOINT`, `use_path_style_endpoint` to `AWS_USE_PATH_STYLE_ENDPOINT` (default `false`) and `url` to `AWS_URL`. Laravel hands `endpoint` and `use_path_style_endpoint` to the AWS SDK's `S3Client`, so the same `s3` driver can talk to MinIO, RustFS, DigitalOcean Spaces or Cloudflare R2: requests go to that host, and path-style addressing puts the bucket in the path (`host/bucket/key`) rather than a subdomain, which self-hosted servers often need. `url` does not change where the API is called; it only replaces the base of the links Laravel generates, for example a CDN domain. Laravel also special-cases an endpoint containing `r2.cloudflarestorage.com` by turning off `retain_visibility`, so copies do not try to carry the source object's ACL across.
code
ini · 8 linesFILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=local-dev-key
AWS_SECRET_ACCESS_KEY=local-dev-secret
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=portfolio-uploads
AWS_ENDPOINT=http://127.0.0.1:9000
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_URL=http://127.0.0.1:9000/portfolio-uploadsgo deeper
Remember that the s3 driver can talk to other providers and that AWS_ENDPOINT is the key that changes where requests go.
Explain which keys the AWS SDK consumes and which only Laravel's URL generation reads, and when path-style addressing is required.
Show you have run this in practice: local S3-compatible servers in development, a public link host that differs from the API host, and provider differences to test.
Weigh provider portability against features that differ between S3-compatible services, and keep the app on the operations every target supports.
## One driver, many providers Laravel's `s3` disk driver is built on Flysystem's AWS S3 adapter and the official **AWS SDK for PHP**. Many storage products speak the same **S3 API** (the HTTP protocol for buckets and objects), so the same driver works against them once it is told where to send requests. There is no separate "MinIO driver" or "R2 driver" in Laravel; you configure the `s3` driver differently. The Laravel 13 skeleton's disk looks like this: ```php 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 'throw' => false, 'report' => false, ], ``` ## What each key changes | Key (env variable) | Consumed by | Effect | |---|---|---| | `endpoint` (`AWS_ENDPOINT`) | `S3Client` | every API request goes to this host instead of AWS's regional endpoint | | `use_path_style_endpoint` (`AWS_USE_PATH_STYLE_ENDPOINT`) | `S3Client` | request URLs become `https://host/bucket/key` instead of `https://bucket.host/key` | | `region` (`AWS_DEFAULT_REGION`) | `S3Client` | the region string the SDK signs requests for | | `url` (`AWS_URL`) | Laravel's adapter | base URL for generated file links; no effect on API calls | | `bucket` (`AWS_BUCKET`) | both | the bucket every path is relative to | Laravel passes the whole disk array (minus `token`) to `new S3Client(...)` after adding `'version' => 'latest'`, so SDK options such as `endpoint` and `use_path_style_endpoint` work without Laravel knowing about them individually. ## Pointing the disk at an S3-compatible server For a design-portfolio site developed against a local S3-compatible server and deployed against a hosted one, the steps are: 1. Keep `'driver' => 's3'` and install `league/flysystem-aws-s3-v3`. 2. Set `AWS_ENDPOINT` to the provider's API host, for example `http://127.0.0.1:9000` for a local server. 3. Set `AWS_USE_PATH_STYLE_ENDPOINT=true` if the server cannot route bucket subdomains, which is common for self-hosted servers reached by IP or container name. 4. Give `AWS_DEFAULT_REGION` whatever value the provider documents; the SDK still signs requests with a region. 5. Set `AWS_URL` if public links should use a different host from the API, such as a CDN or the bucket's public domain. The Laravel docs' own example uses `env('AWS_ENDPOINT', 'https://rustfs:9000')`, a container hostname, the kind of address that usually needs path-style addressing. ## The R2 special case When Laravel builds the Flysystem instance, it checks whether `endpoint` contains `r2.cloudflarestorage.com` and, if so, sets `retain_visibility` to `false`. With `retain_visibility` on, Flysystem's S3 adapter reads the source object's visibility (its ACL) during a copy so the new object matches; Laravel switches that lookup off for R2 endpoints. It is a small but real sign that "S3-compatible" is not the same as "identical to S3". ## Other disk keys worth knowing - **`options`** — default upload parameters handed to the Flysystem S3 adapter for writes. - **`temporary_url`** — replaces the scheme, host and port of presigned URLs, useful when the API host is internal but browsers need a public one. - **`credentials`** — a provider of `ecs` or `instance` instead of static keys. - **`throw` / `report`** — whether failed operations throw or are reported instead of returning `false`. ## Checking the connection Misconfiguration on this disk tends to be quiet, because the skeleton ships `'throw' => false` and `'report' => false`. A wrong endpoint or a bucket-name mismatch makes `put()` return `false` and `get()` return `null`, and nothing appears in the log. While wiring up a new provider: - set `'throw' => true` (or at least `'report' => true`) so the underlying `UnableToWriteFile` or `UnableToReadFile`, with the SDK's message inside, reaches you; - run a single `put()` and `exists()` from `php artisan tinker` against the real environment; - confirm that a generated link opens in a browser, which checks `url` separately from `endpoint`. Once the disk works, decide deliberately whether production should throw or return `false`; many teams keep `throw` on for storage the app cannot run without. ## Common mistakes - Setting only `AWS_URL` and expecting uploads to reach the new provider; `url` never changes where writes go. - Leaving path-style off against a server reached as `http://minio:9000`, so the SDK tries `http://bucket.minio:9000`, which does not resolve. - Assuming every S3 feature exists on every compatible service; ACLs, presigning and multipart behaviour vary, so test the operations your app uses.
- Why does setting only AWS_URL not move uploads to a new provider?`url` is read by Laravel's adapter when it builds file links; the S3 client never sees it. API calls, including every write, still go to whatever `endpoint` resolves to, which is AWS when `AWS_ENDPOINT` is empty. Changing providers needs `endpoint` (and usually credentials, region and path style), not just `url`.
- How do you let the s3 disk use a role instead of static access keys?Leave `key` and `secret` empty or add a `credentials` entry. Laravel only builds static credentials when both are non-empty; a `credentials` value with provider `ecs` or `instance` makes it use the matching AWS SDK credential provider, memoized for the process. With neither, the SDK resolves credentials on its own.
saying these in an interview costs you the question
- Each S3-compatible provider needs its own Laravel filesystem driver
- AWS_URL decides which host uploads are sent to
- Path-style addressing stores objects under an extra folder named after the bucket
- The region setting can be dropped once a custom endpoint is set
- An S3-compatible service supports every S3 feature exactly like AWS