A modern, full-stack web interface for managing Garage distributed object storage systems. Built with React, TypeScript, Go, and Fiber for production-ready S3-compatible storage management.
Garage UI provides a comprehensive dashboard for managing your Garage S3 storage cluster, featuring bucket management, object operations, user access control, and real-time cluster monitoring—all through an intuitive web interface.
- Bucket Management - Create, configure, and delete S3 buckets with visual controls
- Object Operations - Upload, download, and manage objects with drag-and-drop support
- User & Access Control - Manage access keys, permissions, and user credentials
- Cluster Monitoring - Real-time cluster health metrics, node status, and performance statistics
- Multi-Auth Support - Flexible authentication with None, Basic Auth, and OIDC/OAuth2 modes
- Modern UI/UX - Responsive React interface with dark mode, built on Tailwind CSS and shadcn/ui
- Production Ready - Docker/Kubernetes deployment, health checks, and Prometheus metrics
- API-First Design - RESTful API with Swagger/OpenAPI documentation
- Garage S3 storage cluster running (v2.0.0+)
- Docker & Docker Compose (recommended) OR
- Go 1.25+ and Node.js 25+ (for development)
- Clone the repository:
git clone https://github.com/Noooste/garage-ui.git
cd garage-ui- Configure the application:
cp config.yaml.example config.yaml
# Edit config.yaml with your Garage endpoints and credentials- Start the services:
docker-compose up -d- Access the UI:
Open your browser to http://localhost:8080
The Docker Compose setup includes both Garage and Garage UI services for quick local development.
docker run -d \
-p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
-e GARAGE_UI_GARAGE_ENDPOINT=http://your-garage:3900 \
-e GARAGE_UI_GARAGE_ADMIN_ENDPOINT=http://your-garage:3903 \
noooste/garage-ui:latestPull the latest image:
docker pull noooste/garage-ui:latestThe Docker image is multi-platform, supporting linux/amd64 and linux/arm64.
# Add the Helm repository (if available)
helm repo add garage-ui https://helm.noste.dev/
# Install the chart
helm install garage-ui garage-ui/garage-ui \
--set garage.endpoint=http://garage:3900 \
--set garage.adminEndpoint=http://garage:3903 \
--set garage.adminToken=your-admin-tokenOr use the local Helm chart:
helm install garage-ui ./helm/garage-ui -f custom-values.yamlBackend:
cd backend
go mod download
swag init # Generate API docs
go build -o garage-ui .Frontend:
cd frontend
npm install
npm run buildRun:
# From project root
./backend/garage-ui --config config.yamlGarage UI can be configured via YAML file or environment variables.
Create a config.yaml based on the provided example:
# Server configuration
server:
host: "0.0.0.0"
port: 8080
environment: "production"
# Garage S3 Configuration
garage:
endpoint: "garage:3900"
region: "eu-west-1" # Ensure to match Garage region from garage.toml
admin_endpoint: "http://garage:3903"
admin_token: "your-admin-token-here"
# Authentication Configuration
# You can enable one or both authentication methods
auth:
# Admin authentication (username/password)
admin:
enabled: false # Set to true to enable admin login
username: "admin"
password: "secure-password"
# OIDC authentication (Keycloak, Auth0, etc.)
oidc:
enabled: false # Set to true to enable OIDC login
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-client-secret"
issuer_url: "https://auth.example.com/realms/master"
auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth"
token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token"See config.yaml.example for all available options.
All configuration can be set via environment variables with the prefix GARAGE_UI_:
GARAGE_UI_SERVER_PORT=8080
GARAGE_UI_GARAGE_ENDPOINT=http://garage:3900
GARAGE_UI_GARAGE_ADMIN_ENDPOINT=http://garage:3903
GARAGE_UI_GARAGE_ADMIN_TOKEN=your-token
GARAGE_UI_AUTH_ADMIN_ENABLED=true
GARAGE_UI_AUTH_ADMIN_USERNAME=admin
GARAGE_UI_AUTH_ADMIN_PASSWORD=password
GARAGE_UI_AUTH_OIDC_ENABLED=false
GARAGE_UI_LOGGING_LEVEL=infoConfiguration Priority: Environment variables override YAML file settings.
garage-ui/
├── backend/ # Go backend (Fiber framework)
│ ├── internal/
│ │ ├── auth/ # Authentication services (JWT, OIDC)
│ │ ├── config/ # Configuration loading
│ │ ├── handlers/ # HTTP request handlers
│ │ ├── middleware/ # CORS, auth middleware
│ │ ├── models/ # Data models and types
│ │ ├── routes/ # API route definitions
│ │ └── services/ # Business logic (S3, Garage Admin)
│ ├── pkg/ # Shared packages
│ ├── main.go # Application entry point
│ └── go.mod # Go dependencies
│
├── frontend/ # React + TypeScript frontend
│ ├── src/
│ │ ├── components/ # Reusable UI components
│ │ │ ├── buckets/ # Bucket-specific components
│ │ │ ├── charts/ # Data visualization
│ │ │ ├── layout/ # App layout and navigation
│ │ │ └── ui/ # shadcn/ui component library
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # API client and utilities
│ │ ├── pages/ # Page components (Dashboard, Buckets, etc.)
│ │ ├── types/ # TypeScript type definitions
│ │ └── App.tsx # Main application component
│ ├── package.json # Node dependencies
│ └── vite.config.ts # Vite build configuration
│
├── helm/ # Kubernetes Helm chart
│ └── garage-ui/
│ ├── Chart.yaml # Chart metadata
│ ├── values.yaml # Default values
│ └── templates/ # K8s resource templates
│
├── .github/workflows/ # CI/CD pipelines
│ ├── build.yml # Docker build and push
│ └── release.yml # Helm chart releases
│
├── Dockerfile # Multi-stage build
├── docker-compose.yml # Local development stack
└── config.yaml.example # Configuration template
Key Directories:
backend/internal/handlers/- REST API endpoints for buckets, objects, users, cluster, and monitoringfrontend/src/pages/- Main application views (Dashboard, Buckets, Cluster, Access Control)backend/internal/services/- Core business logic interfacing with Garage Admin API and S3frontend/src/lib/api.ts- Axios-based API client with automatic error handling
1. Start Garage (using Docker Compose):
docker-compose up garage -d2. Run the backend:
cd backend
go run main.go --config ../config.yamlThe backend will start on http://localhost:8080 with hot-reload (use air for auto-reload).
3. Run the frontend:
cd frontend
npm install
npm run devThe frontend dev server starts on http://localhost:3000 with API proxy to backend.
Backend:
go run main.go # Start server
go test ./... # Run tests
swag init # Regenerate API docs
go build -o garage-ui . # Build binaryFrontend:
npm run dev # Development server (Vite)
npm run build # Production build
npm run lint # ESLint checks
npm run preview # Preview production build# Backend tests
cd backend && go test ./internal/...
# Frontend tests (if available)
cd frontend && npm testdocker build -t garage-ui:dev .The Dockerfile uses a multi-stage build:
- Frontend build - Node.js Alpine to compile React app
- Backend build - Go Alpine to compile binary
- Runtime - Minimal Alpine with ca-certificates
Once the backend is running, access the Swagger UI documentation at:
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check endpoint |
/api/v1/buckets |
GET | List all buckets |
/api/v1/buckets |
POST | Create a new bucket |
/api/v1/buckets/{name} |
GET | Get bucket details |
/api/v1/buckets/{name} |
DELETE | Delete a bucket |
/api/v1/buckets/{name}/objects |
GET | List objects in bucket |
/api/v1/objects/upload |
POST | Upload object to bucket |
/api/v1/objects/download |
GET | Download object |
/api/v1/objects/delete |
DELETE | Delete object |
/api/v1/users |
GET | List access keys |
/api/v1/users |
POST | Create access key |
/api/v1/users/{id} |
DELETE | Delete access key |
/api/v1/cluster/status |
GET | Cluster node status |
/api/v1/cluster/health |
GET | Cluster health metrics |
/api/v1/cluster/statistics |
GET | Storage statistics |
/api/v1/metrics |
GET | Prometheus metrics |
Authentication: Bearer token in Authorization header when using OIDC or session-based auth.
Garage UI supports flexible authentication with the ability to enable one or both methods simultaneously:
If neither admin nor OIDC authentication is enabled, the application runs without authentication - suitable for private networks or development.
auth:
admin:
enabled: false
oidc:
enabled: falseSimple username/password authentication using JWT tokens.
auth:
admin:
enabled: true
username: "admin"
password: "your-secure-password"
oidc:
enabled: falseEnterprise-grade authentication with providers like Keycloak, Auth0, Okta, etc.
auth:
admin:
enabled: false
oidc:
enabled: true
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-client-secret"
issuer_url: "https://auth.example.com/realms/master"
auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth"
token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token"
userinfo_url: "https://auth.example.com/realms/master/protocol/openid-connect/userinfo"
session_max_age: 86400 # 24 hours
cookie_secure: true # Enable in production with HTTPSYou can enable both admin and OIDC authentication simultaneously. Users will be presented with both options on the login page:
auth:
admin:
enabled: true
username: "admin"
password: "your-secure-password"
oidc:
enabled: true
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-client-secret"
issuer_url: "https://auth.example.com/realms/master"
auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth"
token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token"
userinfo_url: "https://auth.example.com/realms/master/protocol/openid-connect/userinfo"
session_max_age: 86400
cookie_secure: trueRole-Based Access (Optional):
auth:
oidc:
role_attribute_path: "resource_access.garage-ui.roles"
admin_role: "admin"-
Use HTTPS - Always enable TLS in production:
auth: oidc: cookie_secure: true
-
Set Strong Admin Token - Generate a secure token for Garage Admin API:
openssl rand -base64 32
-
Configure CORS - Restrict allowed origins:
cors: allowed_origins: - "https://garage-ui.yourdomain.com"
-
Enable Monitoring - Expose metrics endpoint for Prometheus:
# Metrics available at /api/v1/metrics -
Resource Limits - Set appropriate CPU/memory limits in Kubernetes/Docker
The application provides a health check endpoint:
curl http://localhost:8080/health
# Response: {"status":"ok","version":"0.1.0"}Docker health check is configured automatically (every 30s).
The Helm chart includes:
- Deployment with configurable replicas
- Service (ClusterIP/LoadBalancer)
- Ingress with TLS support
- ConfigMap for configuration
- NetworkPolicy for security
- ServiceMonitor for Prometheus (optional)
Example custom values:
# custom-values.yaml
replicaCount: 2
image:
repository: noooste/garage-ui
tag: latest
garage:
endpoint: "http://garage.storage.svc.cluster.local:3900"
adminEndpoint: "http://garage.storage.svc.cluster.local:3903"
adminToken: "your-secure-token"
ingress:
enabled: true
className: nginx
hosts:
- host: garage-ui.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: garage-ui-tls
hosts:
- garage-ui.example.com
config:
auth:
admin:
enabled: false
oidc:
enabled: true
client_id: garage-ui
client_secret: secret
issuer_url: https://auth.example.com/realms/masterInstall with:
helm install garage-ui ./helm/garage-ui -f custom-values.yaml- Go 1.25+ - High-performance backend runtime
- Fiber v3 - Express-inspired web framework
- Viper - Configuration management
- Minio Go SDK - S3 client library
- go-oidc - OpenID Connect support
- Zerolog - Structured logging
- Swagger/Swag - API documentation
- React 19 - UI framework
- TypeScript 5.9+ - Type safety
- Vite 7 - Build tool and dev server
- React Router 7 - Client-side routing
- TanStack Query - Server state management
- Zustand - Client state management
- React Hook Form + Zod - Form validation
- Tailwind CSS 4 - Utility-first styling
- shadcn/ui - Component library
- Lucide Icons - Icon library
- Recharts - Data visualization
- Axios - HTTP client
Contributions are welcome! Whether it's bug reports, feature requests, or code contributions, your input helps improve Garage UI.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow existing code style (use
gofmtfor Go, ESLint for TypeScript) - Add tests for new features when possible
- Update documentation for API changes
- Keep commits atomic and descriptive
- Ensure all tests pass before submitting PR
Please open an issue on GitHub with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, Docker version, etc.)
Issue: "Failed to connect to Garage Admin API"
- Solution: Verify
garage.admin_endpointandgarage.admin_tokenin config.yaml - Check Garage Admin API is accessible:
curl http://garage:3903/status -H "Authorization: Bearer your-token"
Issue: "CORS errors in browser console"
- Solution: Configure CORS in config.yaml to allow frontend origin:
cors: allowed_origins: - "http://localhost:3000" # For dev
Issue: "401 Unauthorized with OIDC"
- Solution: Verify OIDC configuration, especially
issuer_url,client_id, andclient_secret - Check provider configuration allows the redirect URL:
http://your-app/auth/callback
Issue: "Objects not appearing after upload"
- Solution: Check S3 endpoint connectivity and bucket permissions
- Verify Garage bucket exists:
garage bucket list
Enable debug logging for troubleshooting:
logging:
level: "debug"
format: "json"Or via environment variable:
GARAGE_UI_LOGGING_LEVEL=debugThis project is licensed under the MIT License - see the LICENSE file for details.
- Garage - The lightweight, geo-distributed S3 storage system
- shadcn/ui - Beautiful, accessible React components
- Fiber - Fast and lightweight Go web framework
- All open-source contributors who made this project possible
- Documentation: GitHub Wiki (coming soon)
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ for the Garage community