Summary
Recently, I was developing an API with NestJS and needed to find a way to upload and serve static files (assets) --- such as images, documents, and other files --- in a secure and practical way.
After doing a quick search using GPT, I decided to use MinIO, which is an object storage solution compatible with the AWS S3 protocol, but that runs locally (or anywhere) via Docker.
In this post, I’ll share how I organized the project, the configuration using Docker Compose, and how I integrated the API with MinIO to upload files and make them publicly accessible via URL.
Docker
The project runs in Docker containers, orchestrated by Docker Compose, with the following main services:
- api: NestJS backend, exposed on port
5400 - minio: object storage server (S3-compatible), on ports
9000(API) and9001(web console) - postgres: PostgreSQL database for persistence
- redis: cache and session store
- minio-init: special container used to initialize buckets and
define policies in MinIO using
mc(MinIO client)
services: api: build: context: . dockerfile: ./Dockerfile ports: - "5400:5400" env_file: - ./.env.production depends_on: postgres: condition: service_healthy minio: condition: service_started
minio: image: quay.io/minio/minio:latest command: server /data --console-address ":9001" ports: - "9000:9000" - "9001:9001" env_file: - ./.env.production volumes: - minio_data:/data healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/ready"] interval: 5s timeout: 3s retries: 10
minio-init: image: minio/mc depends_on: minio: condition: service_healthy entrypoint: > /bin/sh -c " mc alias set local http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD && mc mb -p local/$S3_BUCKET || true && mc mb -p local/posts || true && mc anonymous set download local/$S3_BUCKET && mc anonymous set download local/posts " env_file: - ./.env.production
volumes: minio_data:- MinIO runs with the web console available at
http://localhost:9001, where you can view and manage buckets. - The minio-init container runs after MinIO becomes healthy and
executes commands using the
mcclient to:- Create the buckets defined in the
$S3_BUCKETvariable and thepostsbucket. - Set those buckets as public for anonymous download (meaning anyone can view and download the files).
- Create the buckets defined in the
The command used in the docker-compose file for the minio-init
container:
mc anonymous set download local/$S3_BUCKETmc anonymous set download local/postsSets the buckets as publicly accessible for anonymous download, allowing anyone to access the files directly via URL.
The public URL to access a file is:
http://localhost:9000/<bucket>/<file-name>For example:
http://localhost:9000/posts/foto.pngService
Example of the service that handles the integration using the
@aws-sdk/client-s3 package:
// (service implementation omitted for brevity – use your original TypeScript code here)Source
If you’d like to see the complete backend code, including the full setup
and integrations, it’s available on my GitHub:
👉 https://github.com/nathan2slime/apl-atani
Some information may be outdated

