The web console MinIO Community used to ship — back, and backend-agnostic.

A self-hosted S3 console for every backend — without the lock-in.

Stowage is a self-hosted, OIDC-authenticated web dashboard for any S3-compatible backend — Garage, SeaweedFS, MinIO, AWS S3, R2, B2, Wasabi, Ceph. Multi-user RBAC, share links, audit logs, quotas. Single Go binary, AGPL-3.0-or-later.

v0.1.0 — every phase shipped · single static binary · pure-Go SQLite (no CGo) Go 1.26 · Svelte 5 · modernc.org/sqlite AGPL-3.0-or-later
Stowage dashboard with the Local MinIO backend selected, the 'big' bucket open, and a JPEG preview in the detail drawer.
Tested with
Garage SeaweedFS MinIO AWS S3 Backblaze B2 Cloudflare R2 Wasabi Ceph RGW + anything S3 v4

One S3 v4 driver, exercised against each of the above. Per-backend capability flags hide UI that the connected backend doesn't support.

What's in the box

One dashboard. Every backend. Every job.

Stowage talks raw S3 v4 to your storage and layers cross-cutting features at the proxy tier — share links, RBAC, quotas, audit, an SDK-compatible proxy, and an optional Kubernetes operator.

Bring your own backend

One generic AWS SigV4 client (internal/backend/s3v4) talks to anything S3-compatible. Per-backend capability flags hide UI the upstream cannot support.

  • Validated against MinIO, Garage, SeaweedFS, AWS S3, Backblaze B2, Cloudflare R2, Wasabi
  • Capabilities cover versioning, object lock, lifecycle, bucket policy, CORS, tagging, SSE
  • Native admin-API screens are gated until per-driver support ships

Deploys five different ways

Single binary, container, Compose, Helm, or stand up a dev environment with one command.

  • curl | sh / irm | iex installers — SHA256-verified release artefacts
  • stowage quickstart fetches MinIO into ./data and supervises it as a child process
  • Multi-stage distroless Dockerfile and a Compose file that wires Stowage + MinIO
  • Helm chart at deploy/chart (single replica, RWO PVC, optional Ingress + NetworkPolicy)
  • Optional cmd/operator binary reconciles S3Backend and BucketClaim CRDs

Modern object browser

Phases 2–4 in one place. The interactions teams actually use, not a flat list view.

  • Prefix nav, multi-select (shift / ctrl), bulk delete
  • Drag-and-drop multipart uploads, 16 MiB parts, pause / resume
  • Inline previews for text, images, PDF, video
  • Detail drawer: ETag, editable user metadata + object tags, single-object rename
  • Cross-bucket and cross-prefix move & copy with destination picker
  • Streamed bulk download-as-zip with server-side prefix expansion
  • Per-object version history with per-version download
  • Per-user pinned buckets and a unified /search across every backend

Share links that hold up

POST /api/shares mints a code; GET /s/:code resolves it through an HTML password gate. Same gate on every backend, even the ones that presign inconsistently.

  • argon2id-hashed passwords, per-IP rate limit, atomic download-cap enforcement
  • Expiry presets, inline vs attachment toggle, download-limit caps
  • Admin / scope toggle plus revoke on the "My shares" / "All shares" page

Admin surface

One pane for the things you actually need to do. Everything privileged is admin-gated and audited.

  • Bucket settings: versioning, public-read, CORS, policy, lifecycle
  • Storage quotas — soft + hard, scheduled scanner, HTTP 507 on hard cap
  • /admin/endpoints: add, edit, disable, delete, and test S3 endpoints without restart
  • /admin/dashboard: 24h request histogram, per-backend storage / object counts, top-10 buckets, recent 5xx
  • /admin/health: 20-tile rolling probe-history strip per backend
  • /admin/audit: SQLite-backed log with per-event JSON detail and CSV export
  • Per-session API rate limit (default 600 req/min, 429 + Retry-After)
  • RBAC: admin / user / readonly

Cross-backend by design

Move data between vendors without leaving the dashboard.

  • POST /object/copy with dst_backend streams HEAD → GetObject → PutObject through the proxy
  • Quota-checked on the destination, audited as object.transfer
  • Unified /search fans out across all backends in parallel — bucket names plus object prefixes

Observability built in

Prometheus + a starter Grafana dashboard, plus a SQLite audit log queryable from the UI.

  • /metrics with bounded label cardinality: request count / duration / response-bytes histograms, SQLite size, Go runtime
  • Sample dashboard at deploy/grafana/stowage.json — rate by status class, p50/p95/p99, 5xx-in-last-5-min, top-10 backends
  • Audit recorder is sync-to-SQLite by default with a batched async wrapper for the proxy hot path
  • audit.sampling.proxy_success_read_rate=0.0 — successful reads stay quiet, mutations and denials always record

SDK-compatible S3 proxy

Optional :8090 listener verifies SigV4 with a stdlib-only verifier and re-signs upstream. Tenants point an AWS SDK at Stowage and the upstream credentials never leave the server.

  • Per-credential bucket scope enforced before the upstream call
  • Quota pre-check + audit (s3.proxy.*) on every request
  • Synthesised ListBuckets so each credential only sees what it was granted
  • Endpoint and virtual-credential secrets sealed AES-256-GCM under STOWAGE_SECRET_KEY

Single binary, no surprises

One Go 1.26 binary, ~30 MB image. SvelteKit bundle embedded via //go:embed. SQLite via modernc.org/sqlite — pure Go, no CGo, no sidecar database.

  • Cross-compiles to linux/darwin/windows × amd64/arm64
  • CLI verbs: serve | quickstart | create-admin | hash-password
  • Default listeners: dashboard :8080, optional embedded S3 proxy :8090
  • Plaintext HTTP — TLS termination expected at a reverse proxy you already operate

Free forever

AGPL-3.0-or-later (OSI-approved). One Stowage — no community edition, no enterprise edition, no feature gating.

  • "Or later" suffix accepts future FSF revisions automatically
  • Contributions arrive under DCO — no CLA, no copyright transfer
  • No telemetry. The binary makes no outbound calls beyond the backends you configure.
How it works

A proxy you trust, in front of storage you own.

Stowage holds the credentials. Every privileged action — auth, RBAC, quota, share-link gating, audit — happens at the proxy tier, so it works the same whether you're a homelab operator with one MinIO or a platform team brokering buckets across a cluster.

Dashboard plus shareable links, in front of any S3.

The default shape. One Stowage process serves the dashboard on :8080 and resolves /s/:code links for share recipients. SQLite holds users, sessions, audit, share metadata, virtual credentials, and sealed endpoint secrets.

  • 1
    TLS terminates at your reverse proxy — nginx, Caddy, Traefik — whichever you already operate. Stowage trusts X-Forwarded-* only inside server.trusted_proxies.
  • 2
    Stowage holds the S3 credentials — authenticates the user (OIDC or local), checks RBAC + quota, signs upstream calls with the credentials it holds.
  • 3
    Backends stay private — MinIO, Garage, SeaweedFS, AWS S3, B2, R2, Wasabi — connect any combination. Capability flags hide UI the upstream cannot support.
  • 4
    Share links resolve at the proxy — /s/:code applies the argon2id password gate, the per-IP rate limit, and the atomic download cap before a single byte streams.
Benchmarks

Real numbers, under a real constraint.

Both Stowage and a co-located MinIO pinned to 1 CPU / 200 MiB RAM (GOMEMLIMIT=180 MiB). 16 concurrent workers, 15 s per case, loopback. Captured 2026-04-26 — relative numbers under a constrained envelope, not production SLOs.

Sustained throughput
700–1,200 req/s

Upstream-bound S3 calls through the proxy. Synthesised + reject paths sustain ~9–10k req/s without ever calling the upstream.

Median latency
4–10 ms p50

Under a 1 CPU / 200 MiB cap. p99 tails sit on top of MinIO's — both processes are CPU-saturated.

Proxy overhead
+1–3 ms

Added p50 vs talking direct to MinIO at the same envelope. PutObject is faster than direct in our runs.

Proxy casereq/sp50 (ms)p99 (ms)
ListBuckets (synthesised)8,9321.346.15
HeadBucket1,6374.3366.02
ListObjectsV27229.5882.93
HeadObject1,0706.1674.45
GetObject 1 KiB8757.9178.35
GetObject 1 MiB21479.89217.38
GetObject (presigned)9086.8879.50
GetObject (anonymous)9846.1778.03
PutObject 1 KiB56613.4088.36
PutObject 1 MiB140104.47272.97
DeleteObject1,2056.0868.98
Auth failure (bad sig)10,6450.7529.86
Scope violation7,6721.4115.96
Headline finding

Under matched 1 CPU / 200 MiB limits, the proxy adds +1–3 ms p50 / 0–11% throughput cost for upstream-bound S3 calls vs talking direct to MinIO. PutObject (1 KiB and 1 MiB) is faster than direct in our runs. Synthesised paths — ListBuckets, scope reject, bad-sig reject — are much faster than MinIO's equivalent reject paths because the proxy answers without ever calling the upstream.

Why login is intentionally slow

POST /auth/login/local is argon2id at m=65,536 (~64 MiB) per hash, capped by a 10-attempts/15-min/IP limiter. Concurrency cannot safely exceed 1 inside a 200 MiB container — that single-digit req/s is the design, not a regression.

Stowage vs the field

The comparison table

Wouldn't feel legit without one of these

StowageMinIOOpenMaxIOGarageCyberduck
LicenseAGPL-3.0-or-laterAGPL-3.0; CE archived Apr 2026AGPL-3.0; dormant since Jun 2025AGPL-3.0GPL-3.0
What it isDashboard + SigV4 proxy + K8s operatorS3 storage server + bundled ConsoleFork of MinIO Console (UI only)Distributed S3 store (Rust)Desktop transfer client
Storage backendsAny S3 v4Own engineMinIO onlyOwn engineS3, B2, Azure, GCS, FTP/SFTP, WebDAV, SMB, +others
Web dashboard embedded SvelteKit~ browser only post-May 2025 restores admin UI community only desktop only
Share links password + IP limit + cap~ presigned only post-May 2025~ passthrough to MinIO~ presigned only~ presigned (no password / cap)
Cross-backend copy POST /object/copy no no no~ manual drag/drop
SigV4 proxy w/ scope :8090, scope, quota is the endpoint no is the endpoint
Audit + metrics audit CSV + /metrics audit + /metrics~ inherits from MinIO /metrics + OTel
K8s operator optional, Helm~ separate operator repo no community charts
Single binary Go, no CGo Go~ needs MinIO server Rust desktop app
AuthN OIDC, local, static~ OIDC moved to AIStor 2025~ no OIDC (inherits MinIO)~ tokens + S3 keys only~ per-profile creds
As of 2026-04. Each project's status pulled from its own repository, license file, and release notes. Glyph key: yes · ~ partial · no · not applicable.
Quickstart

One command to a working dashboard.

The installer downloads, SHA256-verifies, and exec's the matching release asset — by default into stowage quickstart, which fetches a bundled MinIO into ./data and supervises it as a child process. Reach for Compose, the Helm chart, or a packaged binary when you're past kicking the tyres.

curl | sh / irm | iex

Downloads the matching release asset from stowage-dev/stowage, verifies the SHA256 against the published SHA256SUMS, drops stowage into the current directory, and exec's it. With no args, the binary defaults to stowage quickstart — fetches a bundled MinIO into ./data, runs it as a child process with random credentials, and starts Stowage against it.

# Linux / macOS / WSL 
$ curl -fsSL https://stowage.dev/install.sh | sh
# Windows PowerShell 
PS> irm https://stowage.dev/install.ps1 | iex

Set STOWAGE_NO_RUN=1 to download and verify without executing.

Our promises

Four commitments, after MinIO went into maintenance mode.

MinIO Community Edition entered maintenance mode in December 2025. These commitments cost us nothing to put in writing — and would have spared a lot of teams a bad week.

01

Copyleft, forever.

AGPL-3.0-or-later, OSI-approved. Not source-available. Not SSPL, not BSL, not Elastic. The "or later" suffix accepts future FSF revisions automatically.

02

No community edition.

There is one Stowage. Every feature in this repo — dashboard, share links, audit log, SigV4 proxy, operator, Grafana dashboard, cross-backend copy — is in the AGPL build. The build will never be feature-stripped to push you toward a paid tier.

03

No silent removals.

Feature changes that affect operators are announced in the changelog, in advance of release. The reason this project exists is that MinIO did the opposite in May 2025.

04

No browser-side credentials.

The browser holds an HttpOnly + Secure session cookie. S3 keys live on the server, sealed AES-256-GCM under STOWAGE_SECRET_KEY. CSRF: double-submit cookie + X-CSRF-Token on every mutation.

Security defaults

Concrete guarantees you can verify in the code.

Not a marketing page — every line below maps to a file in the repo. Read the source, or read the audit log; both will show the same thing.

  • TLS termination expected at a reverse proxy you already operate. Stowage trusts X-Forwarded-* only inside server.trusted_proxies (CIDR list).
  • Endpoint and virtual-credential secrets sealed AES-256-GCM under STOWAGE_SECRET_KEY (env, hex/base64) or server.secret_key_file — auto-generated mode-0600 on first boot if absent.
  • Sessions: HttpOnly + Secure cookies, configurable lifetime + idle timeout. Per-session API rate limit defaults to 600 req/min (429 + Retry-After).
  • Per-IP login rate limit: 10 attempts / 15 min, hard-coded.
  • Public share gate: argon2id (m=65,536, ~64 MiB per hash), per-IP rate limit, atomic download-cap enforcement.
  • RBAC: admin / user / readonly. requireWriter blocks readonly mutations; admin paths gated by requireAdmin.
  • Embedded S3 proxy: stdlib-only SigV4 verifier, per-credential bucket scope enforced before the upstream call.
  • Audit coverage: every auth event, share lifecycle event, object mutation, bucket settings change, quota event, and proxy event is recorded.
  • No telemetry. The binary makes no outbound calls beyond the backends you configure.
FAQ

Everything operators ask, before they ask.

Can't find an answer? Open a discussion or read the docs.

Does Stowage store my files? +
No. Stowage proxies S3-compatible storage — your bytes live on whatever backend you point it at (MinIO, Garage, SeaweedFS, AWS S3, B2, R2, Wasabi). Stowage's SQLite database holds users, sessions, audit rows, share metadata, virtual credentials, and sealed endpoint secrets — never object payloads.
Is MinIO still being maintained? +
As of December 2025, MinIO Community Edition is in maintenance mode — no new features, no PRs accepted, and only critical security fixes evaluated case-by-case. Pre-built binaries and Docker images are no longer published to public registries. The MinIO server still works, but the project has clearly pivoted toward its commercial AIStor product. Stowage gives you back a fully featured web console; pair it with a maintained backend like Garage, SeaweedFS, or RustFS for a complete migration off MinIO.
Is Stowage a MinIO Console replacement? +
Yes. Stowage covers the object browsing, user management, OIDC sign-in, bucket configuration, and admin workflows that the MinIO Community Edition console used to provide before February 2025. It is not a MinIO server — you keep running MinIO (or migrate to another S3-compatible backend) and point Stowage at it.
Does Stowage work with Garage / SeaweedFS / Cloudflare R2 / Backblaze B2? +
Yes — Stowage talks raw S3 v4 to all of them, and is tested against each. Per-backend capability flags hide UI for features the connected backend does not support, so you never see options that will not work. Connect any combination of Garage, SeaweedFS, MinIO, AWS S3, R2, B2, Wasabi, and Ceph RGW to a single Stowage instance.
How does Stowage compare to Filestash, Cyberduck, or s3manager? +
Filestash and Cyberduck are general file clients that happen to support S3; they are great for desktop browsing but do not give you proxy-enforced share links, audit logs, RBAC, or quotas. s3manager is a single-user web GUI without OIDC or multi-backend support. Stowage is purpose-built as a multi-user S3 console with first-class OIDC, RBAC, audit, quotas, cross-backend copy, and unified search — features that work the same on every backend you connect.
Why AGPL and not MIT? +
To prevent any future vendor — including the next MinIO — from forking Stowage, embedding it in a paid product, and starving the upstream. Self-hosters and internal corporate users have zero practical obligation. SaaS operators who modify Stowage and expose it must publish their changes. See LICENSE.md for the full rationale.
Will admin features ever be moved behind a paywall? +
No. Stowage exists because that happened to MinIO Console in May 2025. The AGPL license + DCO contributor model + maintainer-held copyright are the structural commitments that make "won’t be quietly stripped down later" credible. There is no community edition and no enterprise edition — only Stowage.
Can I use Stowage in production? +
Yes. v1.0 ships all eight planned phases plus the post-v1 endpoint manager, embedded S3 proxy, and Kubernetes operator. The defaults — TLS-terminating reverse proxy in front, STOWAGE_SECRET_KEY set, OIDC if you have it — are documented and required for serious deployments.
What's the supported scale? +
Single-replica, SQLite-backed, in-process limiter — designed for one team or one organisation per deployment. Under 1 CPU / 200 MiB the dashboard sustains ~5–6k req/s on health endpoints and ~800–1,100 req/s on object-shaped routes; the SigV4 proxy sustains ~700–1,200 req/s on upstream-bound S3 calls and ~9–10k req/s on synthesised / reject paths. See the Benchmarks section.
How do tenants get S3 SDK access without seeing the upstream credentials? +
An admin POSTs to /api/admin/s3-credentials to mint a virtual credential, and hands the {access_key, secret_key} pair to the tenant. They point their AWS SDK at http://stowage:8090 and it Just Works. Bucket scope is enforced before forwarding upstream; ListBuckets is synthesised per credential so tenants only see what they were granted; quotas + audit apply identically to dashboard and SDK uploads.
Does it run on Kubernetes? +
Optionally. Helm chart at deploy/chart/ (single replica because of SQLite) plus a separate operator binary that reconciles S3Backend + BucketClaim CRDs and brokers virtual credentials via Kubernetes Secrets. You can deploy Stowage alone, the operator alone (against an existing Stowage), or both.
What happens if STOWAGE_SECRET_KEY is missing? +
The dashboard and YAML-defined backends still work. The UI-managed endpoint manager and the embedded S3 proxy’s virtual-credential CRUD return 503 secret_key_unset until you provide a key (or let the auto-generated key file be created).
Why not run Stowage on HTTPS directly? +
Stowage deliberately leaves TLS termination to a reverse proxy you already operate (nginx / Caddy / Traefik / cloud LB). It keeps Stowage’s surface area small, lets you reuse certificate automation, and keeps X-Forwarded-* handling explicit via server.trusted_proxies.
Is there telemetry? +
No outbound calls beyond the S3 backends you configure. The Prometheus /metrics endpoint is exposed, not exported — your Prometheus scrapes it. /metrics is unauthenticated; restrict at the reverse-proxy or NetworkPolicy layer.
Ready when you are

Stop logging into eight S3 consoles.

Three commands, one dashboard, every backend you already have.