Web-based file manager for any S3-compatible storage (AWS, MinIO, Backblaze B2, DigitalOcean Spaces, Ceph, etc.).
# Start app + MinIO (for testing)
docker compose up -d
# Open http://localhost:3000
# Login: minioadmin / minioadmin
# Endpoint: http://minio:9000 | Region: us-east-1
Or run standalone:
docker run -p 3000:3000 \
-e SESSION_SECRET=$(openssl rand -base64 48) \
-e ORIGIN=http://localhost:3000 \
s3-viewer
Copy .env.example and adjust for your environment. Key variables:
| Variable | Description |
|---|---|
SESSION_SECRET |
Required in production. Encryption key for session cookies (AES-256-GCM). |
ORIGIN |
Public URL of the app. Must match for CSRF protection to work. |
S3_DEFAULT_ENDPOINT |
Pre-fills the endpoint field on the login form. |
S3_ACCESS_KEY + S3_SECRET_KEY |
Enables fixed credentials mode (skips login). |
OIDC_ISSUER + OIDC_CLIENT_ID + OIDC_CLIENT_SECRET |
Enables SSO login via OpenID Connect. |
Manual credentials (default) — users enter their own S3 access key, secret, endpoint, and region.
Fixed credentials — set S3_ACCESS_KEY and S3_SECRET_KEY env vars. Login is skipped; all users share the same S3 session. Only use behind a VPN or auth proxy.
OIDC / SSO — set OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET. Works with Keycloak, Okta, Authentik, Azure AD, Google, etc. Callback URL: https://<your-domain>/api/auth/oidc/callback. Can be combined with fixed credentials (SSO gate + shared S3 access) or manual credentials (SSO gate + per-user S3 keys).
| Method | Route | Description |
|---|---|---|
| POST | /api/auth/login |
Validate credentials & create session |
| POST | /api/auth/logout |
Clear session |
| GET | /api/auth/session |
Get current session info |
| GET | /api/auth/oidc/login |
Initiate OIDC login flow |
| GET | /api/auth/oidc/callback |
OIDC callback |
| GET | /api/s3/buckets |
List buckets |
| GET | /api/s3/objects |
List objects in a bucket/prefix |
| GET | /api/s3/download |
Get presigned download URL |
| POST | /api/s3/upload |
Get presigned upload URL |
| POST | /api/s3/delete |
Delete object(s) |
| POST | /api/s3/mkdir |
Create folder |
| POST | /api/s3/presign |
Generate shareable presigned URL |
| POST | /api/s3/copy |
Copy/move objects |
| GET | /api/s3/head |
Get object metadata |
| GET | /api/s3/versions |
List object versions |
| GET | /api/s3/search |
Recursive object search |
| GET/PUT/DELETE | /api/s3/tags |
Object tagging |
| GET/PUT/DELETE | /api/s3/buckets/cors |
CORS configuration |
| GET | /health |
Health check |
SESSION_SECRET must be a strong random string in production (openssl rand -base64 48)See Quick Start above for docker compose usage.
Pre-built multi-arch Docker images (amd64 + arm64) are published to GitHub Container Registry on every push to main and on version tags:
docker pull ghcr.io/OWNER/s3-viewer:latest
No authentication required — images are publicly available.
Available tags:
latest — latest build from main branchx.y.z — specific version (e.g., 1.0.0)x.y — minor version (e.g., 1.0)<sha> — specific commit SHAA Helm chart is included for deploying to Kubernetes clusters (including k3s).
helm install s3-viewer ./chart \
--set secrets.SESSION_SECRET="your-secret-here-min-32-chars!!" \
--set env.ORIGIN="https://s3-viewer.example.com"
helm install s3-viewer ./chart \
--set secrets.SESSION_SECRET="your-secret-here-min-32-chars!!" \
--set env.ORIGIN="https://s3-viewer.example.com" \
--set ingress.enabled=true \
--set ingress.className=traefik \
--set ingress.hosts[0].host=s3-viewer.example.com \
--set ingress.hosts[0].paths[0].path=/ \
--set ingress.hosts[0].paths[0].pathType=Prefix
helm install s3-viewer ./chart \
--set secrets.SESSION_SECRET="your-secret-here-min-32-chars!!" \
--set secrets.S3_ACCESS_KEY="your-access-key" \
--set secrets.S3_SECRET_KEY="your-secret-key" \
--set env.S3_ENDPOINT="https://s3.amazonaws.com" \
--set env.ORIGIN="https://s3-viewer.example.com"
Create a my-values.yaml:
image:
repository: ghcr.io/OWNER/s3-viewer
tag: latest
ingress:
enabled: true
className: traefik
hosts:
- host: s3-viewer.example.com
paths:
- path: /
pathType: Prefix
env:
ORIGIN: "https://s3-viewer.example.com"
S3_DEFAULT_ENDPOINT: "https://minio.internal:9000"
S3_DEFAULT_REGION: "us-east-1"
secrets:
SESSION_SECRET: "your-secret-here-min-32-chars!!"
Then install:
helm install s3-viewer ./chart -f my-values.yaml
If you prefer to manage secrets externally (recommended for production):
kubectl create secret generic s3-viewer-secrets \
--from-literal=SESSION_SECRET="your-secret-here" \
--from-literal=S3_ACCESS_KEY="your-key" \
--from-literal=S3_SECRET_KEY="your-secret"
helm install s3-viewer ./chart \
--set existingSecret=s3-viewer-secrets \
--set env.ORIGIN="https://s3-viewer.example.com"
| Parameter | Description | Default |
|---|---|---|
image.repository |
Container image repository | ghcr.io/OWNER/s3-viewer |
image.tag |
Image tag (defaults to appVersion) | "" |
service.type |
Kubernetes service type | ClusterIP |
service.port |
Service port | 3000 |
ingress.enabled |
Enable ingress | false |
ingress.className |
Ingress class name | "" |
env.ORIGIN |
External URL of the app | http://localhost:3000 |
env.S3_DEFAULT_ENDPOINT |
Pre-fill S3 endpoint in login | "" |
env.S3_DEFAULT_REGION |
Pre-fill S3 region in login | us-east-1 |
secrets.SESSION_SECRET |
Session encryption secret | "" (required) |
secrets.S3_ACCESS_KEY |
Fixed S3 access key | "" |
secrets.S3_SECRET_KEY |
Fixed S3 secret key | "" |
existingSecret |
Use existing K8s Secret | "" |
autoscaling.enabled |
Enable HPA | false |
resources |
CPU/memory resource limits | {} |
# Start MinIO
docker compose -f docker-compose.dev.yml up -d
# Install & run
npm install
npm run dev
# → http://localhost:5173 (minioadmin / minioadmin, endpoint: http://localhost:9000)
MIT