MinIO is an S3-compatible object store: the API surface AWS S3 exposes, on your own disk. Almost every backup tool, photo app and "cloud storage" replacement speaks S3, and this is how you give them all a target that costs nothing.
What it is
Buckets, objects, presigned URLs, versioning, lifecycle rules, and a web console. It replaces S3 for private infrastructure, and it makes backups to a second physical location straightforward because there's a real mc client that mirrors, syncs and encrypts.
Before you start
- Recent MinIO images gate the web console behind a licence for some features, and the console port can change. Check the release notes for the tag you pin rather than assuming
:latestis stable. - The root credentials are god credentials. Make them long and random, and never use them as your backup app's credentials — create a scoped service account instead.
- Versioning is off by default. If the point is "my own dropbox", turn it on before you start uploading.
1 — Create the directory
mkdir -p ~/services/minio/data
cd ~/services/minio
openssl rand -base64 32 # root password
2 — Write the compose file
services:
minio:
image: minio/minio:latest
container_name: minio
restart: always
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: CHANGE_ME_16_chars_min
MINIO_ROOT_PASSWORD: CHANGE_ME_48_random_bytes
volumes:
- ./data:/data
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 5s
retries: 3
createbuckets:
image: minio/mc:latest
depends_on:
- minio
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 CHANGE_ME_16_chars_min CHANGE_ME_48_random_bytes;
mc mb --ignore-existing local/backups;
mc mb --ignore-existing local/photos;
mc mb --ignore-existing local/documents;
mc version enable local/backups;
mc anonymous set none local/backups;
mc anonymous set download local/photos;
echo 'buckets ready';
"
restart: "no"
mc anonymous set download turns a bucket into a world-readable unauthenticated endpoint. The example above does it for photos to show the syntax — for anything private use none and hand out presigned URLs instead.3 — Start it
docker compose up -d
docker compose logs createbuckets # should end with 'buckets ready'
4 — First-run setup
- Log into the console at
http://yourhost:9001with the root credentials. - Under Identity → Users, create a service user with only the policies it needs for its bucket. Use that in Restic, Duplicati, rclone, Immich — not root.
- Confirm the port split: 9000 is the S3 API, 9001 is the console. Anything you point an S3 client at uses 9000.
- Decide on erasure or replication. With a single volume, MinIO runs in a single-drive configuration — fine for a home server, and honest about the fact that a disk failure is a data loss event.
5 — Use it from the command line
docker run --rm --network host minio/mc \
alias set local http://127.0.0.1:9000 CHANGE_ME CHANGE_ME
docker run --rm --network host minio/mc \
mirror --watch --overwrite local/photos ~/pictures