Skip to content

S3 Storage

By default NORA stores artifacts on the local filesystem. For production deployments you can switch to any S3-compatible backend — AWS S3, MinIO, RustFS, Ceph RGW, and others.

VariableDefaultDescription
NORA_STORAGE_MODElocalSet to s3 to enable object storage
NORA_STORAGE_S3_URLS3-compatible endpoint URL (e.g. http://minio:9000)
NORA_STORAGE_BUCKETregistryBucket name. Must exist before NORA starts
NORA_STORAGE_S3_ACCESS_KEYAccess key. If omitted, anonymous access is used
NORA_STORAGE_S3_SECRET_KEYSecret key
NORA_STORAGE_S3_REGIONus-east-1Region. Required by some S3 implementations

MinIO is the most popular self-hosted S3-compatible storage. This example includes an init container that creates the bucket automatically.

services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: noraadmin
MINIO_ROOT_PASSWORD: changeme-minio-secret
ports:
- 9000:9000 # S3 API
- 9001:9001 # MinIO Console
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 5s
retries: 5
createbucket:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:9000 noraadmin changeme-minio-secret;
mc mb --ignore-existing myminio/nora-storage;
exit 0;
"
nora:
image: ghcr.io/getnora-io/nora:latest
depends_on:
createbucket:
condition: service_completed_successfully
environment:
NORA_HOST: "0.0.0.0"
NORA_STORAGE_MODE: s3
NORA_STORAGE_S3_URL: http://minio:9000
NORA_STORAGE_BUCKET: nora-storage
NORA_STORAGE_S3_ACCESS_KEY: noraadmin
NORA_STORAGE_S3_SECRET_KEY: changeme-minio-secret
NORA_STORAGE_S3_REGION: us-east-1
ports:
- 4000:4000
restart: unless-stopped
volumes:
minio-data:
Terminal window
# Push a Docker image
docker tag alpine:latest localhost:4000/test/alpine:latest
docker push localhost:4000/test/alpine:latest
# Pull it back
docker rmi localhost:4000/test/alpine:latest
docker pull localhost:4000/test/alpine:latest
# Upload a raw file
curl -X PUT -d "hello s3" http://localhost:4000/raw/test/hello.txt
curl http://localhost:4000/raw/test/hello.txt

Check MinIO Console at http://localhost:9001 — you should see objects in the nora-storage bucket.

RustFS is a lightweight S3-compatible storage written in Rust. Configuration is identical to MinIO with one difference: the health check endpoint is /health instead of /minio/health/live.

services:
rustfs:
image: rustfs/rustfs:latest
command: server /data --console-address ":9001"
environment:
RUSTFS_ROOT_USER: noraadmin
RUSTFS_ROOT_PASSWORD: changeme-rustfs-secret
ports:
- 9000:9000 # S3 API
- 9001:9001 # Console
volumes:
- rustfs-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
interval: 5s
timeout: 5s
retries: 5
createbucket:
image: minio/mc:latest
depends_on:
rustfs:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myrustfs http://rustfs:9000 noraadmin changeme-rustfs-secret;
mc mb --ignore-existing myrustfs/nora-storage;
exit 0;
"
nora:
image: ghcr.io/getnora-io/nora:latest
depends_on:
createbucket:
condition: service_completed_successfully
environment:
NORA_HOST: "0.0.0.0"
NORA_STORAGE_MODE: s3
NORA_STORAGE_S3_URL: http://rustfs:9000
NORA_STORAGE_BUCKET: nora-storage
NORA_STORAGE_S3_ACCESS_KEY: noraadmin
NORA_STORAGE_S3_SECRET_KEY: changeme-rustfs-secret
NORA_STORAGE_S3_REGION: us-east-1
ports:
- 4000:4000
restart: unless-stopped
volumes:
rustfs-data:

For AWS S3, point NORA_STORAGE_S3_URL to the regional endpoint:

environment:
NORA_STORAGE_MODE: s3
NORA_STORAGE_S3_URL: https://s3.eu-central-1.amazonaws.com
NORA_STORAGE_BUCKET: my-nora-registry
NORA_STORAGE_S3_ACCESS_KEY: AKIA...
NORA_STORAGE_S3_SECRET_KEY: wJal...
NORA_STORAGE_S3_REGION: eu-central-1
[storage]
mode = "s3"
s3_url = "http://minio:9000"
bucket = "nora-storage"
s3_access_key = "noraadmin"
s3_secret_key = "changeme"
s3_region = "us-east-1"

NORA starts but artifacts go to local storage

Section titled “NORA starts but artifacts go to local storage”

Check NORA_STORAGE_MODE is set to exactly s3 (lowercase). Any misspelling causes a silent fallback to local mode. Run:

Terminal window
docker exec nora env | grep NORA_STORAGE

You should see:

NORA_STORAGE_MODE=s3
NORA_STORAGE_S3_URL=http://minio:9000
NORA_STORAGE_BUCKET=nora-storage
...

Ensure the S3 service is healthy before NORA starts. In Docker Compose, use depends_on with condition: service_healthy.

NORA does not create buckets. Pre-create with mc mb or an init container (see examples above).

Proxy-only downloads (Cargo, PyPI, Go, Maven, NuGet, Terraform, Conan, RubyGems, Pub) stream responses directly from upstream without storing them in S3. These packages will not appear in the web UI or in storage listings. Only published packages (via npm publish, twine upload, cargo publish, docker push, curl -X PUT /raw/) and cached tarballs (npm, Ansible) are persisted to storage.