Running TinyS3 in Docker
This documents the published image, how it is configured, and every option it accepts.
Quick start (zero-config)
The image runs a full single-node server out of the box — no config file:
bash
docker run --rm -v tiny_s3_data:/data -p 8000:8000 tinys3/serverThat starts server /data and prints a banner with the endpoints and the root credentials (generated on first run, saved in the /data volume). Set your own with -e TINYS3_ROOT_USER=... -e TINYS3_ROOT_PASSWORD=.... For a cluster or any advanced setup, override the command with the YAML path (see How configuration works).
The image
| Name / tags | tinys3/server:latest and tinys3/server:<version> |
| Base | eclipse-temurin:25-jre (glibc). -PjibBaseImage=eclipse-temurin:25-jre-alpine for a smaller musl image |
| Platform | multi-arch linux/amd64 + linux/arm64 (native on Apple-Silicon/ARM hosts) |
| Default command | server /data — zero-config single-node server |
| JVM flags | heap scales to the container (MaxRAMPercentage=75), UseG1GC (-PjibGc=zgc for ZGC), jdk.incubator.vector for the erasure SIMD path |
EXPOSE | 8000 — the default listen port |
Performance-tuned by design: the heap tracks the container's memory limit (docker run -m 4g just uses more, no rebuild), the GC is selectable, and the erasure codec's vector path is enabled. Build with Jib:
bash
./gradlew jibDockerBuild --no-configuration-cache -PjibPlatforms=arm64 # local (one arch)
./gradlew jib # push multi-archBuild knobs: -PjibGc=zgc (low-pause GC), -PjibBaseImage=… (alpine for size), -PjibImage=myrepo/tinys3 (target repo), -PjibPlatforms=amd64,arm64.
How configuration works
Two ways to run:
Zero-config (default) —
docker run tinys3/serverrunsserver /data. Credentials come fromTINYS3_ROOT_USER/TINYS3_ROOT_PASSWORD(or are generated and saved in/data). Single-node only.YAML config — override the command to pass a mounted config, the only way to run a cluster, server pools, erasure, or TLS:
bashdocker run -v "$PWD/config.yaml:/etc/tinys3/config.yaml:ro" -v tiny_s3_data:/data -p 8000:8000 \ tinys3/server server --config /etc/tinys3/config.yaml
The two encryption keys are always environment variables (kept out of the config file so the key never sits next to the data); other JVM tunables go through JAVA_TOOL_OPTIONS.
Minimal run
bash
docker run --rm \
-v "$PWD/config.yaml:/etc/tinys3/config.yaml:ro" \
-v tiny_s3_data:/data \
-p 8000:8000 \
tinys3/server --config /etc/tinys3/config.yamlwith a config.yaml:
yaml
server:
port: 8000
auth:
accessKey: "your-access-key"
secretKey: "your-secret-key"
region: "us-east-1"
storage:
mode: simple
path: /dataConfig file reference
Everything the YAML accepts, with defaults.
server
| Key | Default | Meaning |
|---|---|---|
server.port | 8000 | Port the server listens on. Publish this port. |
server.baseUrl | — | External base URL (e.g. http://localhost:8000); used when generating/validating presigned URLs behind a proxy. |
server.tls.enabled | false | Serve HTTPS. |
server.tls.certFile | — | PEM certificate path (required when TLS enabled). |
server.tls.keyFile | — | PEM private-key path (required when TLS enabled). |
server.tls.keyPassword | — | Password for an encrypted key file. |
auth
| Key | Default | Meaning |
|---|---|---|
auth.accessKey | — (required) | Root access key. Also becomes the IAM account root credential. |
auth.secretKey | — (required) | Root secret key. |
auth.region | — | Region reported to clients, e.g. us-east-1. |
storage
| Key | Default | Meaning |
|---|---|---|
storage.mode | — | simple (single filesystem) or erasure (Reed-Solomon across disks). |
storage.path | — | Data directory (simple mode). |
storage.erasure.dataShards | 4 | Data shards. |
storage.erasure.parityShards | 2 | Parity shards — tolerated simultaneous disk losses. |
storage.erasure.chunkSize | 16777216 (16 MB) | Chunk size for large objects. |
storage.erasure.disks | — | List of disk paths (need dataShards + parityShards of them). |
See storage-modes.md for the trade-offs.
healing (erasure mode)
| Key | Default | Meaning |
|---|---|---|
healing.enabled | true | Run the background health scanner. |
healing.scanIntervalMinutes | 60 | Minutes between scans. |
healing.maxConcurrentHeals | 4 | Concurrent heal operations. |
healing.healOnStartup | false | Run a scan immediately on startup. |
jobs
| Key | Default | Meaning |
|---|---|---|
jobs.restartActiveOnStartup | true | Resume jobs that were active when the server last stopped. |
Environment variables
| Variable | Meaning |
|---|---|
TINY_S3_SSE_MASTER_KEY | Base64, 32 bytes. Enables server-side encryption (SSE-S3 / SSE-KMS). Unset → requests asking for SSE are refused with 501 rather than stored in cleartext. |
TINY_S3_DISK_ENCRYPTION_KEY | Base64, 32 bytes. Enables encryption at rest for erasure shards (AES-256-CTR) and keys the per-shard integrity check into a MAC. Erasure mode only. |
JAVA_TOOL_OPTIONS | Standard JVM override, e.g. -Xmx1g to raise the heap above the baked-in 256 MB. |
Generate a key: openssl rand -base64 32.
Losing an encryption key loses the data it protected. There is deliberately no fallback key stored beside the objects.
JVM system properties
Pass via JAVA_TOOL_OPTIONS=-Dkey=value.
| Property | Default | Meaning |
|---|---|---|
tinys3.sse.masterKey | — | Alternative to TINY_S3_SSE_MASTER_KEY. |
tinys3.diskEncryption.key | — | Alternative to TINY_S3_DISK_ENCRYPTION_KEY. |
tinys3.lifecycle.debugIntervalSeconds | 0 (off) | Shrinks a lifecycle "day" to N seconds for testing expiration quickly. Leave off in production. |
Volumes and persistence
Mount these to keep data across container restarts:
| What | Where | Notes |
|---|---|---|
| Config file | /etc/tinys3/config.yaml (:ro) | Referenced by --config. |
| Object data (simple) | storage.path, e.g. /data | |
| Object data (erasure) | each storage.erasure.disks path | One volume per disk; spread across real devices for actual fault tolerance. |
| IAM state | .tinys3-iam.json under storage.path (simple) or the first disk (erasure) | Users, groups, roles, policies and access keys. Persisted here — keep it on a durable volume or IAM identities are lost on restart. |
| System metadata | .tinys3.sys under the storage root | Credentials and internal metadata; lives with the data. |
| TLS certs | wherever certFile / keyFile point | Mount the PEM files read-only. |
docker-compose
Ready-made files in app/:
docker-compose.yaml— simple mode, one data volume.docker-compose-erasure.yaml— erasure mode, six disk volumes.
Both mount an example config and pass --config. Simple mode:
yaml
services:
tiny-s3:
image: tinys3/server
ports:
- "8000:8000"
volumes:
- ./examples/simple-server.yaml:/etc/tinys3/config.yaml:ro
- tiny_s3_data:/data
command: ["--config", "/etc/tinys3/config.yaml"]
volumes:
tiny_s3_data:Recipes
Erasure across six disks:
bash
docker run --rm \
-v "$PWD/config-erasure.yaml:/etc/tinys3/config.yaml:ro" \
-v d1:/storage/disk1 -v d2:/storage/disk2 -v d3:/storage/disk3 \
-v d4:/storage/disk4 -v d5:/storage/disk5 -v d6:/storage/disk6 \
-p 8000:8000 \
tinys3/server --config /etc/tinys3/config.yamlTLS:
bash
docker run --rm \
-v "$PWD/config-tls.yaml:/etc/tinys3/config.yaml:ro" \
-v "$PWD/certs:/etc/tinys3/certs:ro" \
-v tiny_s3_data:/data \
-p 8443:8443 \
tinys3/server --config /etc/tinys3/config.yamlwith server.tls.certFile: /etc/tinys3/certs/cert.pem etc. See app/examples/config-tls.yaml.
Encryption at rest + SSE:
bash
docker run --rm \
-e TINY_S3_DISK_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
-e TINY_S3_SSE_MASTER_KEY="$(openssl rand -base64 32)" \
-v "$PWD/config-erasure.yaml:/etc/tinys3/config.yaml:ro" \
-v d1:/storage/disk1 ... \
-p 8000:8000 \
tinys3/server --config /etc/tinys3/config.yaml(Generate the keys once and store them securely — reusing the same keys is required to read the data back.)
More heap:
bash
docker run --rm -e JAVA_TOOL_OPTIONS="-Xmx1g" ... tinys3/server --config /etc/tinys3/config.yamlGotchas
- The published image is multi-arch (linux/amd64 + linux/arm64). The registry publish (
jib) builds a multi-arch manifest by default (app/build.gradle.kts,jibPlatformsdefaults toamd64,arm64), so Docker pulls the image matching the host. A localjibDockerBuildloads only one arch into the daemon — pass-PjibPlatforms=arm64(oramd64) to pick which. EXPOSE 8080is not the listen port. Map whateverserver.portis (default 8000).--configis mandatory. A container started without it exits immediately with usage.- The root credential is the IAM account root. Additional identities are created through the IAM API and persisted in
.tinys3-iam.json— put that on a durable volume.