Minimal FastAPI bridge to a private MinIO bucket. Provides upload, download, delete, and health endpoints while keeping MinIO off the public network.
- Docker Compose stack with MinIO, bootstrapper, and FastAPI app
- Streamed uploads/downloads so large files do not exhaust memory
- Presigned upload/download endpoints for direct browser access to the object store
- Opinionated bootstrap script that creates the bucket, user, and policy on startup
- Container healthchecks and startup retries to ensure MinIO is ready before serving traffic
- Docker + Docker Compose v2
- Copy of the repo and permission to run containers locally
cp .env.example .env
# Update credentials if needed, then:
docker compose up -d --buildThe API becomes available at http://localhost:8080 (or the port set in APP_PORT) once the bootstrap container finishes.
Environment variables are read from .env by Docker Compose.
| Variable | Purpose |
|---|---|
MINIO_ROOT_USER, MINIO_ROOT_PASSWORD |
Admin credentials used by the MinIO server and bootstrap container |
MINIO_BUCKET |
Bucket created at startup and used by the app |
OMS_ACCESS_KEY, OMS_SECRET_KEY |
Application user with RW access to the bucket |
S3_REGION |
Region name passed to the AWS SDK (defaults to us-east-1) |
APP_PORT |
Port exposed by FastAPI inside the container (default 8080) |
minio: MinIO server on an internal Docker network; console exposed athttp://localhost:9001minio-init: One-shot container that waits for MinIO, then provisions the bucket, IAM policy, and app userapp: FastAPI service exposing the REST API onhttp://localhost:8080
Base URL: http://localhost:8080
| Method | Path | Description |
|---|---|---|
POST |
/files/upload |
Multipart upload; optional key query parameter to override filename |
GET |
/files/{key} |
Stream file download |
DELETE |
/files/{key} |
Remove object from bucket |
GET |
/files/{key}/metadata |
Return metadata (length, type, etag, last modified) without streaming the object |
POST |
/files/presign-upload |
Generate a presigned PUT URL so clients can upload directly to MinIO |
POST |
/files/presign-download |
Generate a presigned GET URL for direct downloads |
GET |
/health |
Verifies connectivity to MinIO |
# Upload a file
curl -F "file=@README.md" http://localhost:8080/files/upload
# Download the same file
curl -o README.copy.md http://localhost:8080/files/README.md
# Remove it again
curl -X DELETE http://localhost:8080/files/README.md
# Inspect metadata without downloading
curl http://localhost:8080/files/README.md/metadata
# Request an upload URL that expires in 15 minutes
curl \
-X POST http://localhost:8080/files/presign-upload \
-H 'Content-Type: application/json' \
-d '{
"key": "uploads/example.txt",
"content_type": "text/plain",
"content_length": 1024,
"expires_in": 900
}'- Presigned URLs embed the
S3_ENDPOINThost. Ensure that value points to a hostname or load balancer reachable by any client that consumes the URL (for examplehttps://storage.example.com). - If you prefer to keep MinIO private, skip the presigned endpoints and keep using the streaming upload/download routes instead.
- Clients must send the headers included in the presign response verbatim; otherwise MinIO will reject the request.
- Treat BucketBridge as an internal utility service. Expose the presign endpoints from your primary API (where business logic and authentication already live), then have that API call the wrapper over the private Docker network.
- By routing presign requests through the main API, you can reuse your existing AAA mechanisms, attach business rules (ownership checks, rate limits, audit logging), and prevent untrusted callers from talking to the wrapper directly.
- If you must expose the wrapper externally, secure it like any public API: verify JWTs on every request, restrict credentials to the least privilege required, and apply network-level controls (e.g., reverse proxy ACLs or service mesh policies).
- App source lives in
app/main.py; S3 helper inapp/s3wrap.py - Python dependencies defined in
app/requirements.txt init/bootstrap.shcontains the provisioning logic for the MinIO user and bucket
Instead of building locally, you can use the pre-built multi-architecture image from GitHub Container Registry:
# Pull the latest image (supports amd64 and arm64)
docker pull ghcr.io/parhamdavari/bucketbridge:latest
# Or pull a specific version
docker pull ghcr.io/parhamdavari/bucketbridge:v1.0.0Update your docker-compose.yml to use the pre-built image:
app:
image: ghcr.io/parhamdavari/bucketbridge:latest
# Remove the 'build: ./app' line
depends_on:
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
environment:
# ... your S3 configurationStop the stack with docker compose down. To remove MinIO data as well, add the -v flag.