Flysystem S3 driver · Storage::disk()

Laravel S3 Object Storage in Frankfurt

The Flysystem S3 driver simply points at the German endpoint — Storage::disk() stays unchanged, you only swap config and ENV. Endpoint https://de-fra.i3storage.com, region de-fra, SigV4, presigned temporaryUrl. GDPR/DPA, 1 TB egress incl. per TB, from €2.49/TB.

php — config/filesystems.php
'intercolo' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => 'de-fra',
    'bucket' => env('AWS_BUCKET'),
    'endpoint' => 'https://de-fra.i3storage.com',
    'use_path_style_endpoint' => false,
    'visibility' => 'private',
],
Flysystem
S3 driver
temporaryUrl
presigned
GDPR / DPA
Frankfurt DC

The S3 filesystem driver simply points at Frankfurt

Laravel talks to object storage through the Flysystem package and its s3 driver. For intercolo you only swap endpoint and region in config/filesystems.php — your application code using Storage::disk() stays unchanged line for line. Uploads, user assets, generated PDFs or CSV exports then land in a GDPR-compliant German object storage in the Frankfurt data center instead of a US hyperscaler. Endpoint https://de-fra.i3storage.com, region de-fra, SigV4 auth, strong read-after-write and 99.999999% durability. From €2.49 per TB/month, 1 TB egress included per stored TB.

Set up in four steps

1

Install the Flysystem S3 adapter

The s3 driver needs exactly one Composer package: composer require league/flysystem-aws-s3-v3 "^3.0". That is the only dependency — Composer pulls in the AWS SDK automatically as a sub-dependency. Compatible with Laravel 9, 10 and 11.

2

Add a disk in config/filesystems.php

Add a new disk under disks (see the terminal above): driver s3, region de-fra, endpoint https://de-fra.i3storage.com, use_path_style_endpoint false and visibility private. Pull key, secret and bucket from the .env via env(). Storage::disk('intercolo') is then available everywhere in the app.

3

Fill the .env

Enter the credentials: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from your intercolo API key pair, AWS_BUCKET with your bucket name, AWS_DEFAULT_REGION=de-fra, AWS_ENDPOINT=https://de-fra.i3storage.com and AWS_USE_PATH_STYLE_ENDPOINT=false. No passwords, no bucket policies — access runs exclusively through this SigV4 key pair.

4

Use it in code

From now on you address storage through the familiar facade: Storage::disk('intercolo')->put(...), ->get(...) and ->temporaryUrl(...). Existing code that already uses Storage::disk() keeps working unchanged after the pure config switch.

Usage: put, get and presigned temporaryUrl

php
use Illuminate\Support\Facades\Storage;

// Store the upload privately — no public ACL required
Storage::disk('intercolo')->put('invoices/2026/inv-1042.pdf', $pdf);

// Read the content back or process it as a stream
$content = Storage::disk('intercolo')->get('invoices/2026/inv-1042.pdf');
$stream  = Storage::disk('intercolo')->readStream('videos/rough-cut.mov');

// Check existence and size
if (Storage::disk('intercolo')->exists('invoices/2026/inv-1042.pdf')) {
    $bytes = Storage::disk('intercolo')->size('invoices/2026/inv-1042.pdf');
}

// Presigned download link, valid for 15 minutes — instead of a public URL
$url = Storage::disk('intercolo')->temporaryUrl(
    'invoices/2026/inv-1042.pdf',
    now()->addMinutes(15),
);

Storage::disk('intercolo') returns a perfectly ordinary Flysystem adapter — put, get, exists, size, readStream and temporaryUrl behave identically to any other disk. temporaryUrl signs the URL with SigV4 against the endpoint set on the disk.

No public buckets via ACL — built that way on purpose

intercolo does not support public buckets via ACL. So set visibility to private on the disk, not public — a call to Storage::disk('intercolo')->url() for permanently public links deliberately goes nowhere. That is intent, not a gap: instead of making files globally readable through bucket ACLs (and risking accidental data leaks), you deliver every download via temporaryUrl() as a presigned URL — signed with SigV4, valid for a limited time and authorised per file. Public, non-sensitive assets such as CSS, JS or logos still go the classic way into public/ or behind a CDN PoP in the DACH region and Europe; confidential uploads stay private and are signed on demand only. That way there is no global read access you forget to close again.

Change two lines, touch no code

If your app already uses the s3 driver against AWS, the switch is trivial: you set endpoint to https://de-fra.i3storage.com and region to de-fra on the disk — that is it in code. The AWS SDK, Flysystem, every Storage::disk() and Storage::put() call and your queue workers stay unchanged, because intercolo speaks the same S3 API (SigV4 signing, multipart upload, presigned URLs). In the .env you replace AWS_DEFAULT_REGION with de-fra and add AWS_ENDPOINT plus AWS_USE_PATH_STYLE_ENDPOINT=false. You mirror existing objects once via rclone or aws-cli sync to Frankfurt — over 500 million files have already moved this way.

Change two lines, touch no code

Encrypt confidential files client-side — before put()

intercolo does not encrypt at rest; what is secured is transport over TLS. For confidential uploads you therefore encrypt the content client-side before it goes into put() — for example with sodium_crypto_secretstream (libsodium ships with current PHP versions) and your own key from your Laravel config or secrets store, or you mirror whole directories into the bucket via rclone crypt or Restic. Then object storage holds only ciphertext, the key never leaves your app infrastructure, and no provider — us included — can read the files. For public, non-sensitive assets you skip this step; you make the call per file or per bucket.

Encrypt confidential files client-side — before put()

What the driver can do — and what is deliberately missing

Fully supported

put, get, exists, size, delete, copy, move, readStream and writeStream for large files, multipart uploads, directory listing and presigned temporaryUrl() — all through the familiar Storage facade and comfortably from queue jobs.

No public ACL

Public buckets via ACLs do not exist. Instead of visibility public you use private plus temporaryUrl() for signed, expiring links; permanently public assets you deliver via public/ or a CDN PoP.

No versioning, no Object Lock

Object versioning and Object Lock/WORM do not exist. You model history and retention in your app logic through your own paths and timestamps, or additionally protect the bucket with a backup tool.

No at-rest encryption

Only TLS transport is secured. Confidential data you encrypt client-side before upload (libsodium, rclone crypt, Restic). No AES-256 at-rest promise — in return full key ownership stays with you.

intercolo i3 vs. AWS S3 vs. Cloudflare R2 as a Laravel disk

AWS S3 (native) Cloudflare R2 intercolo i3
Location / data residency EU region selectable, US corp (CLOUD Act) EU/global, US corp Frankfurt DC, 100% DE, GDPR/DPA
Config change native s3 driver override endpoint endpoint https://de-fra.i3storage.com, region de-fra
Egress model per GB, can get expensive no egress, but US operator 1 TB incl./TB, then +€3.99/TB
Public files public ACL / CloudFront r2.dev / public bucket private + presigned temporaryUrl()
Starting storage price list price US$/TB list price US$/TB €2.49/TB
GDPR / DPA (Art. 28) DPA, but US access possible DPA, US corp DPA, German operator, Schrems II safe

Agency setup: one bucket and one key pair per project

If you run several client projects, create a dedicated bucket and a dedicated API key pair (access/secret) per project and store it in the respective .env as a separate disk. Access runs exclusively through these SigV4 keys — no password login, no bucket policies. If a project ends or a key is compromised, you rotate exactly that key pair without touching other clients. Time-consuming uploads such as video encoding results or large exports you push into the bucket from a queued job with ShouldQueue, so the web request stays fast. Every project is thus cleanly separated, billable per bucket and cancellable individually.

お客様のデータを移行します

当社の経験が証明しています。数ペタバイトのデータと5億以上のファイルを含む移行を成功裏に完了しています。

500M+
files migrated
Petabytes
of data migrated
since 2006
in business

まだ質問がありますか?

No. You add a new disk in config/filesystems.php and fill the .env — every existing Storage::disk(), put() or get() call keeps working unchanged, because intercolo speaks the same S3 API as AWS.
Via temporaryUrl(): it generates a presigned URL that is signed with SigV4 and valid for a limited time. For permanently public, non-sensitive assets (CSS, JS, logos) you keep using public/ or a CDN PoP; confidential files stay private.
Transport is secured over TLS; there is no storage-side at-rest encryption. For confidentiality you encrypt the content client-side before put() — for example with libsodium, rclone crypt or Restic. The key then stays exclusively with you.
You only change endpoint to https://de-fra.i3storage.com and region to de-fra on the disk; SDK, Flysystem and your code stay untouched. You mirror existing objects once via rclone or aws-cli sync to Frankfurt.
Exactly one: composer require league/flysystem-aws-s3-v3 "^3.0". It brings the AWS SDK as a sub-dependency and is compatible with Laravel 9, 10 and 11.
With use_path_style_endpoint false Laravel addresses virtual-hosted: https://<bucket>.de-fra.i3storage.com. Path-style https://de-fra.i3storage.com/<bucket> also works if you set the value to true. Both variants are supported.
Yes. Create a dedicated bucket and a dedicated SigV4 key pair per project and define a separate disk in config/filesystems.php each. That keeps clients and environments cleanly isolated and individually rotatable.
1ヶ月間無料トライアル

当社のS3互換ストレージを1ヶ月間無料でお試しください。