/
Блог Хекслета
/
Код
/

MinIO: объектное хранилище своими руками — Хекслет

MinIO: объектное хранилище своими руками — Хекслет

25 июня 2026 г.

7 минут
MinIO: объектное хранилище своими руками — Хекслет

MinIO: объектное хранилище своими руками — от бакета до S3 API

Знакомая сцена. Приложению нужно хранить файлы пользователей: аватары, документы, фото. Первое решение — класть их на диск сервера, рядом с кодом. Работает ровно до первого деплоя: контейнер пересоздался, и все загруженные файлы исчезли. Добавили второй сервер — файл лежит на одном, а запрос пришёл на другой, и картинка не открывается.

Второе решение — облако, обычно AWS S3. Надёжно, но файлы уезжают во внешний сервис, появляется привязка к одному поставщику, набегает счёт за хранение и трафик, а для локальной разработки приходится либо платить, либо городить заглушки. Данные клиентов на чужих серверах — отдельная головная боль там, где это запрещено.

MinIO закрывает обе проблемы. Это объектное хранилище, которое вы запускаете сами — на своём сервере, в Docker, за одну команду. Снаружи оно говорит на языке AWS S3, поэтому любой инструмент и любая библиотека, умеющие работать с S3, работают с MinIO без переделок. Получаете надёжное хранилище файлов на своей инфраструктуре, без облачного счёта и без утечки данных наружу.

Разберём по шагам: что такое объектное хранилище, почему важна совместимость с S3, как запустить MinIO в Docker, как устроены бакеты и права и как хранить в нём файлы приложения.

MinIO запускают в Docker на Linux-сервере — основы того и другого разбирают в курсах «Docker» и по администрированию Linux на Хекслете.

Что такое MinIO

MinIO — объектное хранилище с открытым кодом. Его задача — хранить файлы и отдавать их по запросу, надёжно и в больших объёмах. От облачных хранилищ его отличает то, что вы разворачиваете его сами: на своём сервере, в своей сети, под своим контролем.

Главная особенность — совместимость с Amazon S3. S3 за годы стал негласным стандартом объектного хранения, и его протокол понимают сотни инструментов. MinIO повторяет этот протокол точь-в-точь. На практике это значит, что код, написанный под AWS S3, переключается на MinIO сменой одного адреса — и работает.

Где это применяют: хранение пользовательских загрузок в приложении, резервные копии, статика и медиа сайтов, наборы данных для машинного обучения, замена облачного S3 на своём железе и локальное хранилище для разработки. Везде, где нужно хранить много файлов и обращаться к ним по сети.

Объектное хранилище: чем оно отличается

Привычный способ хранить файлы — файловая система: папки внутри папок, путь до файла как адрес. Объектное хранилище устроено иначе, и разница принципиальна.

Файловая система

Объектное хранилище

Структура

Дерево папок

Плоский список объектов в бакетах

Адрес файла

Путь: /home/user/avatar.png

Ключ объекта внутри бакета

Доступ

Через файловую систему сервера

По сети, через HTTP-запрос

Метаданные

Базовые: дата, размер

Произвольные: свои поля у объекта

Масштаб

Ограничен диском

Растёт добавлением серверов

Объект — это файл вместе с его метаданными и уникальным ключом. Бакет — контейнер для объектов, что-то вроде папки верхнего уровня. Внутри бакета структура плоская: настоящих вложенных папок нет, но ключ может выглядеть как путь — users/42/avatar.png. Это просто имя объекта, а не реальная иерархия, хотя выглядит привычно.

minio_01_objvsfs.png

Зачем так сложнее. Плоская модель и доступ по сети дают то, чего не даёт диск: хранилище растёт добавлением серверов, файлы дублируются для надёжности, а приложение обращается к ним по одному и тому же адресу, сколько бы серверов за ним ни стояло. Файл больше не привязан к конкретной машине.

S3 API: почему совместимость решает всё

Это главный козырь MinIO, и его легко недооценить. S3 API — набор правил, по которым приложения общаются с хранилищем: как загрузить объект, как скачать, как перечислить содержимое бакета. AWS придумала его для своего облака, но он стал стандартом де-факто.

minio_02_s3compat.png

MinIO говорит на этом же языке. Последствия приятные:

Что вы используете с AWS S3

С MinIO

Библиотека для языка (boto3, aws-sdk)

Работает — меняется только адрес

Консольная утилита (aws, s3cmd, rclone)

Работает с тем же адресом

Инструменты резервного копирования

Видят MinIO как обычный S3

Код приложения под S3

Переключается одной настройкой

Практический вывод важен для архитектуры. Можно вести разработку на локальном MinIO, а в проде использовать облачный S3 — код один и тот же. Или начать с облака и переехать на своё хранилище, когда счёт станет заметным. Совместимость снимает привязку к поставщику: вы не заперты ни в облаке, ни в MinIO.

Запуск в Docker за одну команду

MinIO разворачивается как обычный контейнер. Одна команда поднимает и хранилище, и веб-консоль управления:

docker run -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=admin \
  -e MINIO_ROOT_PASSWORD=password123 \
  -v ~/minio-data:/data \
  quay.io/minio/minio server /data --console-address ":9001"

Разберём, что здесь происходит. Два порта: 9000 — это сам S3 API, к которому обращается код, а 9001 — веб-консоль для людей. Логин и пароль администратора задаются переменными. Том -v привязывает папку на сервере к хранилищу внутри контейнера — без него данные пропадут при пересоздании контейнера, что повторило бы исходную проблему.

minio_03_docker.png

После запуска открываете http://localhost:9001, входите под заданными логином и паролем и попадаете в консоль: создаёте бакеты, смотрите объекты, заводите ключи доступа. Всё то же самое делается и из командной строки, и из кода — консоль просто удобна для людей.

Бакеты, объекты и ключи доступа

Работа с MinIO строится на трёх вещах: бакеты, объекты и ключи.

Бакет — контейнер верхнего уровня. Под разные задачи заводят разные бакеты: avatars для аватаров, documents для документов, backups для копий. Имя бакета уникально в пределах хранилища.

minio_04_buckets.png

Объект — файл внутри бакета со своим ключом. Ключ часто пишут как путь, чтобы группировать объекты по смыслу, хотя физически структура плоская:

бакет: uploads
  объект: avatars/user_42.png
  объект: avatars/user_77.png
  объект: docs/contract_2026.pdf

Ключи доступа — это пара «access key» и «secret key», аналог логина и пароля для программного доступа. Те же, что у AWS. Администраторский ключ задаётся при запуске, но в приложении используют не его, а отдельные ключи с ограниченными правами.

Права: что кому доступно

По умолчанию бакеты закрыты — без ключа к ним не обратиться. Доступ настраивают политиками. Бакет можно сделать публичным на чтение (тогда файлы открываются по прямой ссылке — удобно для статики сайта) или оставить приватным, и тогда к нему ходят только с ключом.

Режим бакета

Кто читает

Когда использовать

Приватный

Только с ключом доступа

Документы, личные файлы — по умолчанию

Публичный на чтение

Любой по прямой ссылке

Статика, картинки сайта, публичные медиа

Не делайте бакет публичным «чтобы заработало». Самый быстрый способ показать файл — открыть бакет всему интернету, и так часто и делают сгоряча. Но если в бакете ��ежат документы пользователей, публичный доступ означает утечку всего разом. Личные файлы держат приватными, а отдают через временные подписанные ссылки — о них ниже.

Работа из кода

В приложении с MinIO работают через библиотеку. Поскольку хранилище S3-совместимое, годятся и родная библиотека MinIO, и любой клиент AWS S3 — достаточно указать адрес. Пример на JavaScript:

import { Client } from 'minio';

const minio = new Client({
  endPoint: 'localhost',
  port: 9000,
  useSSL: false,
  accessKey: 'admin',
  secretKey: 'password123',
});

// создать бакет
await minio.makeBucket('uploads');

// загрузить файл
await minio.fPutObject('uploads', 'avatars/user_42.png', './avatar.png');

// скачать файл
await minio.fGetObject('uploads', 'avatars/user_42.png', './downloaded.png');

Те же действия — создать бакет, положить объект, забрать объект, перечислить содержимое — есть в библиотеках для Python, Go, Java и других языков. Имена методов отличаются, идея одна.

Временные ссылки без раздачи ключей

Возникает вопрос: как дать пользователю скачать приватный файл, не отдавая ему секретный ключ. Ключ в браузере — это доступ ко всему хранилищу, его раздавать нельзя.

minio_05_presigned.png

Ответ — presigned URL, временная подписанная ссылка. Приложение на своей стороне, с ключом, генерирует особую ссылку: она ведёт прямо к объекту в MinIO, действует ограниченное время и работает без ключа. Пользователь скачивает файл по ней напрямую, а ключ остаётся на сервере.

// ссылка на скачивание, живёт 5 минут
const url = await minio.presignedGetObject(
  'uploads',
  'avatars/user_42.png',
  5 * 60
);
// эту ссылку безопасно отдать в браузер

Тот же приём работает и на загрузку: приложение выдаёт подписанную ссылку, и браузер заливает файл прямо в MinIO, минуя сервер приложения. Это снимает нагрузку — большие файлы не проходят через ваш бэкенд, а идут напрямую в хранилище. Ключ при этом ни разу не покидает сервер.

Надёжность и что MinIO умеет ещё

Хранилище файлов бессмысленно, если файлы из него пропадают. MinIO защищает данные на уровне самого хранилища. В распределённом режиме его запускают на нескольких серверах, и каждый объект хранится с избыточностью: часть дисков может выйти из строя, а файлы всё равно остаются целыми и доступными. Это называется erasure coding — данные раскладываются по дискам так, что восстанавливаются даже при потере нескольких из них. На одном контейнере для разработки это не нужно, но в проде именно так MinIO переживает отказ железа.

За пределами простого хранения есть набор возможностей, которые иначе пришлось бы собирать вручную.

Возможность

Зачем

Версионирование

Хранить прошлые версии объекта — откатиться или восстановить удалённое

Правила жизненного цикла

Автоматически удалять или архивировать старые объекты по сроку

Репликация

Зеркалировать бакет на другой сервер или в другой регион

Уведомления о событиях

Сообщать приложению о загрузке или удалении объекта

Шифрование

Хранить объекты в зашифрованном виде

Отдельно стоит упомянуть mc — консольный клиент MinIO. Он работает с хранилищем из командной строки: копирует файлы, настраивает политики, зеркалирует бакеты. Для администрирования и скриптов он удобнее, чем консоль в браузере.

Практика: хранилище файлов для приложения

Соберём хранение пользовательских загрузок с нуля.

Шаг 1. Запустить MinIO. Одна команда docker run из раздела выше — поднимутся хранилище и консоль.

Шаг 2. Создать бакет и ключ. В консоли на localhost:9001 создаёте бакет uploads и отдельный ключ доступа для приложения — не администраторский.

Шаг 3. Загружать файлы из кода. Приложение принимает файл от пользователя и кладёт в бакет под осмысленным ключом:

await minio.fPutObject(
  'uploads',
  `avatars/${userId}.png`,   // ключ привязан к пользователю
  uploadedFilePath
);

Шаг 4. Отдавать файлы по временной ссылке. Когда нужно показать файл, приложение генерирует presigned URL и отдаёт его в браузер:

const url = await minio.presignedGetObject('uploads', `avatars/${userId}.png`, 300);
res.json({ avatarUrl: url });

Готово. Файлы лежат в надёжном хранилище, переживают пересоздание контейнеров и не привязаны к конкретному серверу. Секретный ключ остался на бэкенде, а пользователь работает с файлами по временным ссылкам. Всё это — на вашей инфраструктуре, без облачного счёта, и тот же код позже переедет на облачный S3 без переписывания.

Антипаттерны

Хранить загрузки на диске сервера. С этого начиналась статья: файлы исчезают при деплое и не видны со второго сервера. Пользовательские файлы кладут в объектное хранилище, а не рядом с кодом приложения.

Использовать администраторский ключ в приложении. Корневой ключ даёт полный доступ ко всему хранилищу. Если он утечёт из приложения — потеряно всё. Для приложения заводят отдельный ключ с правами только на нужный бакет.

Делать приватный бакет публичным ради удобства. Публичный бакет с личными файлами — это утечка по прямой ссылке, без всякого взлома. Приватные данные отдают через временные подписанные ссылки, а не открытием бакета.

Класть файлы в базу данных как blob. База раздувается, копии замедляются, а отдача файлов нагружает СУБД. В базе хранят ключ объекта и метаданные, а сам файл — в объектном хранилище.

Запускать без привязки тома. Контейнер без -v хранит данные внутри себя — и теряет их при пересоздании. Данные хранилища всегда выносят на постоянный том на сервере.


FAQ

MinIO бесплатный?

Базовая версия с открытым кодом бесплатна и для разработки, и для коммерческих проектов. Платить нужно за сервер, на котором она работает, и за время на настройку и сопровождение. У компании есть платные тарифы с поддержкой и дополнительными возможностями для крупных установок.

Чем MinIO отличается от AWS S3?

S3 — облачный сервис Amazon: платите за хранение и трафик, данные на серверах Amazon. MinIO вы ставите сами на своём железе, платите только за инфраструктуру, данные остаются у вас. При этом протокол у них общий, поэтому код переносится между ними без переписывания. Облако проще на старте, MinIO выгоднее на масштабе и обязателен там, где данные нельзя отдавать наружу.

Нужно ли знать AWS, чтобы начать?

Нет. Достаточно понимать идею объектного хранилища — бакеты, объекты, ключи — и уметь запускать контейнеры в Docker. Знание AWS пригодится разве что для переноса кода между MinIO и облачным S3, но это та же самая библиотека.

Подходит ли MinIO для продакшена, а не только для разработки?

Да, MinIO рассчитан на боевую нагрузку. Для надёжности его разворачивают на нескольких серверах, где данные дублируются и переживают отказ дисков. Для разработки же хватает одного контейнера. Многие команды используют локальный MinIO в разработке и кластер MinIO в проде.

Что хранить в MinIO, а что в базе данных?

В MinIO — сами файлы: картинки, документы, видео, копии. В базе — структурированные данные и ссылка на объект: его бакет и ключ, плюс метаданные вроде имени и размера. Так база остаётся лёгкой, а файлы лежат там, где их удобно отдавать.

Можно ли использовать MinIO с любым языком?

Да. Раз хранилище совместимо с S3, подходят и официальные библиотеки MinIO, и клиенты AWS S3 для большинства языков: JavaScript, Python, Go, Java, C#. Принцип везде один: указать адрес и ключи, дальше работать с бакетами и объектами.

Никита Вихров

2 дня назад

0

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845