0% прочитано

Як підключити MinIO до Laravel через стандартний S3 disk

MinIO не потребує окремої файлової реалізації в Laravel. Якщо сховище підтримує S3-сумісний API, застосунок може залишитися на стандартному disk s3, а локальне середовище лише задає іншу адресу, бакет і облікові дані.

8 жовтня 2026 р. 8 хв читанняLaradock

Після того як я додала MinIO до Laradock, наступним кроком стало підключення сховища до Laravel.

На цьому місці легко створити окремий MinioStorageService, додати спеціальні методи для запису і читання файлів та поступово прив’язати застосунок до конкретного сховища.

Але MinIO підтримує S3-сумісний API, а Laravel уже вміє працювати з S3 через Filesystem.

Тому я не додавала новий спосіб роботи з файлами. Для Laravel MinIO залишився звичайним s3 disk з іншою адресою сховища.

MinIO тут не є новим типом файлової системи для Laravel. Це S3-сумісне сховище, до якого стандартний S3 disk звертається через іншу адресу.

Я не хочу, щоб прикладний код знав про MinIO

Код прив’язаний до конкретного сховища
$minioStorage->put(    $path,    $contents,); $minioStorage->delete(    $path,);
Робота через стандартний disk Laravel
Storage::disk('s3')->put(    $path,    $contents,); Storage::disk('s3')->delete(    $path,);

У другому варіанті прикладний код знає лише про файловий disk.

Йому не потрібно знати, чи за цим disk зараз стоїть MinIO, AWS S3 або інше S3-сумісне сховище.

Локальне середовище може використовувати MinIO, а інше середовище - іншу інфраструктуру без переписування кожного місця, де застосунок працює з файлами.

Основою залишається стандартний disk s3

config/filesystems.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'),     'endpoint' =>

Окремого driver=minio тут немає.

Драйвер залишається s3.

Відрізняється лише інфраструктурна конфігурація: адреса S3-сумісного API, облікові дані, регіон, бакет і спосіб побудови адрес.

Локальні відмінності залишаються в конфігурації середовища

Приклад локальної конфігурації
AWS_ACCESS_KEY_ID=laradockAWS_SECRET_ACCESS_KEY=laradockAWS_DEFAULT_REGION=us-east-1 AWS_BUCKET=local-files AWS_ENDPOINT=http://minio:9000 AWS_USE_PATH_STYLE_ENDPOINT=true

У прикладному коді мені не потрібна умова на кшталт if local then MinIO.

Саме середовище повідомляє Laravel, де знаходиться файлове сховище.

У моєму локальному Docker-середовищі адреса дорівнює http://minio:9000.

Причину, чому localhost усередині контейнера не веде до MinIO, я розібрала окремо: контейнер застосунку знаходить сусідній сервіс через мережу Docker та ім’я сервісу.

Адреса змінює місце призначення, а не контракт застосунку

Один файловий контракт для різної інфраструктури
Application    |    vStorage::disk('s3')    |    vLaravel Filesystem    |    vS3 driver    |    vconfigured endpoint    |    +--> MinIO locally    |

AWS_ENDPOINT відповідає лише на питання, де знаходиться S3-сумісний API.

Він не змінює спосіб, яким прикладний код просить записати, прочитати або видалити файл.

Саме тому адресу MinIO я залишаю в конфігурації, а не передаю в сервіси предметної області.

Для локального MinIO використовую бакет у шляху

Бакет передається в шляху
http://minio:9000/local-files/example.txt                   |                   +--> bucket

Для локальної адреси minio:9000 зручно використовувати AWS_USE_PATH_STYLE_ENDPOINT=true.

Тоді назва бакета передається в шляху запиту.

Для локальної Docker-мережі це простіше, ніж вимагати окреме DNS-ім’я для кожного бакета.

Laravel не створює бакет лише тому, що його назва є в .env

AWS_BUCKET=local-files лише повідомляє Laravel, який бакет використовувати.

Сам бакет має існувати у сховищі.

Тому після налаштування MinIO я окремо перевіряю, що потрібний бакет створений, і лише після цього тестую запис із Laravel.

Першу перевірку роблю через стандартний Storage

Повний базовий цикл
Storage::disk('s3')->put(    'checks/minio.txt',    'MinIO works',); Storage::disk('s3')->exists(    'checks/minio.txt',); Storage::disk('s3')->get(    'checks/minio.txt',); Storage::disk('s3')->delete(    'checks/minio.txt',);

Для першої перевірки мені не потрібен окремий контролер або спеціальний MinIO-клієнт.

Я хочу довести базовий цикл через той самий API, яким користуватиметься застосунок: записати файл, перевірити його існування, прочитати і видалити.

Якщо ці операції проходять через Storage::disk('s3'), базове підключення Laravel до MinIO вже працює.

Після запису перевіряю файл і в MinIO

Після put() я відкриваю потрібний бакет у веб-консолі MinIO і перевіряю шлях checks/minio.txt.

Це підтверджує, що Laravel справді записав об’єкт у потрібне S3-сумісне сховище, а не випадково використав інший disk.

Після перевірки тестовий файл видаляється через той самий Storage.

Після зміни .env перевіряю кеш конфігурації

Очистити локальний кеш конфігурації
php artisan config:clear

Правильне значення в .env ще не гарантує, що саме його зараз використовує Laravel.

Якщо конфігурація була закешована раніше, застосунок може продовжувати працювати зі старою адресою або старим бакетом.

Тому при локальному налаштуванні я перевіряю і цей шар.

Дивлюся, яку конфігурацію Laravel реально отримав

Перевірка готової конфігурації
config(    'filesystems.disks.s3.endpoint',); config(    'filesystems.disks.s3.bucket',); config(    'filesystems.disks.s3.use_path_style_endpoint',);

Коли з’єднання не працює, я не обмежуюся переглядом .env.

Спочатку дивлюся, яке значення реально потрапило в filesystems.disks.s3.

Це швидко відділяє помилку конфігурації від проблеми мережі або самого S3-запиту.

Адресу MinIO не записую в прикладний сервіс

Інфраструктурна адреса потрапила в прикладний код
$client->put(    'http://minio:9000/local-files/'.$path,    $contents,);

minio:9000 є правильною адресою для конкретного локального середовища.

Але це не робить її частиною логіки застосунку.

Якщо записати цю адресу прямо у файловий сервіс, перехід на інше S3-сумісне сховище вже вимагатиме змін коду.

Через Laravel Filesystem ця деталь залишається в конфігурації.

Окремий файловий сервіс може бути потрібен, але не через MinIO

Сервіс описує правила застосунку
final readonly class PrivateAttachmentStorage{    public function store(        string $path,        string $contents,    ): void {        Storage::disk('s3')->put(            $path,            $contents,        );    }}

Окремий клас для роботи з файлами може бути цілком доречним.

Наприклад, якщо він визначає шлях приватних вкладень, правила заміни файла, очищення старого об’єкта або інші вимоги застосунку.

Але причина його існування тоді в поведінці системи, а не в тому, що локально використовується MinIO.

Усередині такий клас усе одно може працювати через Storage::disk('s3').

Найважливіше - однаковий прикладний код у різних середовищах

Змінюється інфраструктура, а не спосіб роботи з файлами
Локально Storage::disk('s3')        |        vMinIO  Інше середовище Storage::disk('s3')        |        vS3-сумісне сховище  

Саме заради цього я підключаю MinIO через стандартний S3 disk.

Локально отримую бакети, об’єкти, HTTP API та іншу поведінку S3-сумісного сховища.

При цьому код збереження файла не отримує окремої гілки лише для MinIO.

Адреса для Laravel і посилання для браузера - різні задачі

http://minio:9000 доступний контейнеру Laravel, але браузер користувача не знає внутрішнього Docker-імені minio.

Тому серверне підключення до S3 і формування посилань для браузера я не змішую в одну проблему.

На цьому етапі достатньо довести, що Laravel правильно читає і записує файли.

Тимчасові посилання на приватні файли мають власні правила і заслуговують окремого матеріалу.

Що я перевіряю після підключення MinIO до Laravel

Мій короткий список:

  • Чи використовується стандартний driver s3?
  • Чи адреса сховища береться з конфігурації?
  • Чи контейнер Laravel бачить http://minio:9000?
  • Чи вказані правильні облікові дані?
  • Чи існує потрібний бакет?
  • Чи ввімкнений режим адресації через шлях?
  • Чи Laravel бачить нові значення після зміни .env?
  • Чи працюють put(), exists(), get() і delete()?
  • Чи не потрапили адреса MinIO або спеціальна MinIO-логіка в прикладний код?

MinIO змінює інфраструктуру, а не файловий контракт Laravel

Після такого підключення в прикладному коді не з’являється новий спосіб роботи з файлами.

Laravel як використовував Storage::disk('s3'), так і продовжує його використовувати.

MinIO лише надає локальне S3-сумісне сховище з власною адресою, бакетом і обліковими даними.

У результаті я можу перевіряти S3-поведінку локально, не створюючи окрему файлову реалізацію лише для MinIO.

Якщо інфраструктура вже підтримує потрібний контракт, я краще підключу її до стандартної абстракції Laravel, ніж створю ще одну абстракцію через назву конкретного сервісу.

Мені не потрібен окремий спосіб зберігати файли в MinIO. Мені потрібен стандартний S3 disk, який у локальному середовищі використовує MinIO.