TechEarl

How to Run MinIO in Docker (Local S3-Compatible Storage)

Run a single MinIO container as a local S3-compatible object store: persistent volume on /data, console on 9001, S3 API on 9000, and how to point the AWS SDK at it for free dev work.

Ishan KarunaratneIshan Karunaratne⏱️ 5 min readUpdated
Share thisCopied

If you build against S3, you have two unappealing options for local dev: spend money calling AWS, or run something that emulates S3 on your machine. MinIO is the second option done well — a single Go binary that speaks the S3 API and has its own web console. Point the AWS SDK at it instead of s3.amazonaws.com and most of your code doesn't know the difference.

How do I run MinIO in Docker?

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

S3 API on http://localhost:9000, console on http://localhost:9001. Log in to the console with minio-admin / minio-password, create a bucket, get access/secret keys.

Try it with your own values

Configure ports, credentials, and the data volume.

Why a volume on /data

MinIO stores object data, bucket metadata, and config under /data. Without a volume mount there, every docker rm wipes your buckets. The volume keeps the data alive:

bash
docker stop :container_name && docker rm :container_name

docker run -d --name :container_name \
  -p :s3_port:9000 -p :console_port:9001 \
  -e MINIO_ROOT_USER=:user \
  -e MINIO_ROOT_PASSWORD=:password \
  -v minio-data:/data \
  quay.io/minio/minio \
  server /data --console-address ":9001"
# Buckets and objects are still there.

Practical usage: point the AWS SDK at MinIO

For a Node app using @aws-sdk/client-s3:

javascript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({
  endpoint: 'http://127.0.0.1:9000',  // MinIO instead of s3.amazonaws.com
  region: 'us-east-1',                 // arbitrary, but required
  credentials: {
    accessKeyId: 'YOUR-MINIO-ACCESS-KEY',
    secretAccessKey: 'YOUR-MINIO-SECRET-KEY',
  },
  forcePathStyle: true,                // MinIO uses path-style URLs
});

await s3.send(new PutObjectCommand({
  Bucket: 'my-bucket',
  Key: 'hello.txt',
  Body: 'Hello from MinIO',
}));

Three things to know:

  • endpoint points at MinIO's S3 port instead of AWS.
  • forcePathStyle: true — MinIO uses http://host:port/bucket/key URLs, not the virtual-host style AWS prefers (bucket.host:port/key). Without this flag, modern AWS SDKs default to virtual-host style and MinIO refuses.
  • region is required but unused. Anything works.

For Python boto3:

python
import boto3
s3 = boto3.client(
    's3',
    endpoint_url='http://127.0.0.1:9000',
    aws_access_key_id='YOUR-MINIO-ACCESS-KEY',
    aws_secret_access_key='YOUR-MINIO-SECRET-KEY',
    region_name='us-east-1',
)
s3.put_object(Bucket='my-bucket', Key='hello.txt', Body=b'Hello')

Same idea — different SDK, same MinIO. Once your app talks to MinIO locally, the only thing that changes when you point it at real AWS is the endpoint and credentials.

Generate access keys

The MINIO_ROOT_USER / MINIO_ROOT_PASSWORD env vars are the admin login for the console. For actual S3-API calls, generate access keys per application:

  1. Log into the console (http://localhost::console_port).
  2. Navigate to Identity → Access Keys → Create Access Key.
  3. Copy the access key and secret. The secret is shown only once.

Use those keys in your app's SDK config.

Compose example

yaml
services:
  minio:
    image: quay.io/minio/minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: :user
      MINIO_ROOT_PASSWORD: :password
    ports:
      - "::s3_port:9000"
      - "::console_port:9001"
    volumes:
      - minio-data:/data
    restart: unless-stopped

volumes:
  minio-data:

Common pitfalls

  • No volume on /data. Buckets and objects die with the container.
  • AWS SDK using virtual-host URLs. MinIO speaks path-style by default; turn on forcePathStyle: true or --addressing-style path depending on the SDK.
  • MINIO_ACCESS_KEY / MINIO_SECRET_KEY env vars. Those names worked in older MinIO releases. Current MinIO uses MINIO_ROOT_USER / MINIO_ROOT_PASSWORD.
  • Connecting from a container. Inside Docker, MinIO is reachable via the service name (minio:9000), not 127.0.0.1. The endpoint in your SDK depends on whether the calling app runs on the host or in a sibling container.
  • HTTPS in dev. MinIO can do TLS but the dev pattern is plain HTTP. Don't waste time on certs locally; if your prod code requires HTTPS, configure that conditionally.

What to do next

FAQ

Sources

Authoritative references this article was fact-checked against.

TagsDockerMinIOS3Object StorageAWSDevOps

Found this useful? Pass it on.

Copied
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts