Signed-off-by: Noste <83548733+Noooste@users.noreply.github.com>
Garage UI Helm Chart
A Helm chart for deploying Garage UI, a modern web interface for managing Garage distributed object storage systems.
Table of Contents
- Overview
- Prerequisites
- Quick Start
- Installation
- Configuration
- Examples
- Upgrading
- Accessing the Application
- Monitoring
- Troubleshooting
- Uninstalling
Overview
Garage UI provides an intuitive web interface for managing your Garage S3 storage cluster, featuring:
- Bucket Management - Create, delete, and configure S3 buckets
- Object Operations - Upload, download, and delete objects through the UI
- User Access Control - Manage users, access keys, and permissions
- Cluster Monitoring - View cluster health, status, and statistics
- Authentication - Support for no auth, basic auth, and OIDC/SSO
Prerequisites
Before installing this chart, ensure you have:
- Kubernetes
1.19+or later - Helm
3.0+or later - Garage S3 instance running and accessible
- Garage Admin Token - Required for administrative operations
Required Information
You will need the following information from your Garage installation:
- Garage S3 Endpoint - The Garage S3 API endpoint (default port:
3900) - Garage Admin Endpoint - The Garage Admin API endpoint (default port:
3903) - Admin Token - Bearer token for authenticating with the Admin API
To find your admin token, check your Garage server's configuration file.
Quick Start
The fastest way to get started with Garage UI:
Step 1: Create a values file
Create a file named my-values.yaml with your Garage configuration:
config:
garage:
endpoint: "http://garage:3900" # Your Garage S3 endpoint
admin_endpoint: "http://garage:3903" # Your Garage Admin endpoint
admin_token: "YOUR_ADMIN_TOKEN_HERE" # Your admin token
region: "garage" # S3 region (can be any value)
Step 2: Install the chart
helm install garage-ui ./helm/garage-ui -f my-values.yaml
Step 3: Access the UI
# Forward port to access locally
kubectl port-forward svc/garage-ui 8080:80
# Open in your browser
open http://localhost:8080
That's it! You should now have Garage UI running and accessible.
Installation
Installing from local chart
If you've cloned the repository:
helm install garage-ui ./helm/garage-ui -f my-values.yaml
Installing with inline values
You can also set values directly on the command line:
helm install garage-ui ./helm/garage-ui \
--set config.garage.endpoint=http://garage:3900 \
--set config.garage.admin_endpoint=http://garage:3903 \
--set config.garage.admin_token=your-token-here
Verify installation
Check that the pod is running:
kubectl get pods -l app.kubernetes.io/name=garage-ui
View the logs:
kubectl logs -l app.kubernetes.io/name=garage-ui
Configuration
Configuration Structure
The chart uses a structured config section that maps directly to the application's configuration file. You can override any value using Helm's standard methods.
Minimal Configuration
The absolute minimum configuration requires only the Garage endpoints and admin token:
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
admin_token: "your-admin-token"
Common Configuration Options
Server Settings
config:
server:
port: 8080 # Application port
environment: production # Environment (production/development/staging)
Authentication Configuration
No Authentication (default, suitable for private networks):
config:
auth:
admin:
enabled: false
oidc:
enabled: false
Admin Authentication (username/password with JWT):
config:
auth:
admin:
enabled: true
username: "admin"
password: "your-secure-password"
oidc:
enabled: false
OIDC/SSO (recommended for production):
config:
auth:
admin:
enabled: false
oidc:
enabled: true
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-oidc-secret"
issuer_url: "https://auth.example.com/realms/master"
# ... additional OIDC settings
Both Authentication Methods (admin and OIDC simultaneously):
config:
auth:
admin:
enabled: true
username: "admin"
password: "your-secure-password"
oidc:
enabled: true
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-oidc-secret"
issuer_url: "https://auth.example.com/realms/master"
# ... additional OIDC settings
CORS Configuration
config:
cors:
enabled: true
allowed_origins:
- "https://garage-ui.example.com" # Recommended: specific origins
# - "*" # Or: allow all origins (less secure)
Logging
config:
logging:
level: info # debug, info, warn, error
format: json # json (recommended for production), text
Kubernetes Resource Configuration
Ingress
Enable external access with Ingress:
ingress:
enabled: true
className: nginx
hosts:
- host: garage-ui.example.com
paths:
- path: /
pathType: Prefix
With TLS:
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: garage-ui.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: garage-ui-tls
hosts:
- garage-ui.example.com
Resource Limits
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
Replicas and High Availability
replicaCount: 3
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- garage-ui
topologyKey: kubernetes.io/hostname
Complete Parameters Reference
For a complete list of all available parameters, see the values.yaml file which includes detailed comments for every configuration option.
Examples
Example 1: Basic Installation (In-Cluster Garage)
For Garage running in the same Kubernetes cluster:
# values-basic.yaml
config:
garage:
endpoint: "http://garage.garage-system.svc.cluster.local:3900"
admin_endpoint: "http://garage.garage-system.svc.cluster.local:3903"
admin_token: "GgJLd9J...your-token-here"
region: "garage"
Install:
helm install garage-ui ./helm/garage-ui -f values-basic.yaml
Example 2: External Access with Ingress
# values-ingress.yaml
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
admin_token: "your-admin-token"
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
hosts:
- host: garage-ui.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: garage-ui-tls
hosts:
- garage-ui.example.com
Install:
helm install garage-ui ./helm/garage-ui -f values-ingress.yaml
Example 3: With Admin Authentication
# values-admin-auth.yaml
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
admin_token: "your-admin-token"
auth:
admin:
enabled: true
username: "admin"
password: "super-secret-password-change-me"
oidc:
enabled: false
ingress:
enabled: true
className: nginx
hosts:
- host: garage.local
paths:
- path: /
pathType: Prefix
Install:
helm install garage-ui ./helm/garage-ui -f values-admin-auth.yaml
Example 4: Production Setup with OIDC (Keycloak)
# values-production.yaml
replicaCount: 3
config:
server:
environment: production
garage:
endpoint: "https://s3.garage.example.com"
admin_endpoint: "https://admin.garage.example.com"
admin_token: "your-admin-token"
region: "us-east-1"
auth:
admin:
enabled: false
oidc:
enabled: true
provider_name: "Keycloak"
client_id: "garage-ui"
client_secret: "your-oidc-client-secret"
scopes:
- openid
- email
- profile
issuer_url: "https://auth.example.com/realms/production"
auth_url: "https://auth.example.com/realms/production/protocol/openid-connect/auth"
token_url: "https://auth.example.com/realms/production/protocol/openid-connect/token"
userinfo_url: "https://auth.example.com/realms/production/protocol/openid-connect/userinfo"
cookie_secure: true
cookie_http_only: true
cookie_same_site: "lax"
cors:
enabled: true
allowed_origins:
- "https://garage-ui.example.com"
logging:
level: info
format: json
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
hosts:
- host: garage-ui.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: garage-ui-tls
hosts:
- garage-ui.example.com
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 200m
memory: 256Mi
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- garage-ui
topologyKey: kubernetes.io/hostname
Install:
helm install garage-ui ./helm/garage-ui -f values-production.yaml
Example 5: With Prometheus Monitoring
# values-monitoring.yaml
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
admin_token: "your-admin-token"
serviceMonitor:
enabled: true
interval: 30s
labels:
prometheus: kube-prometheus
Install:
helm install garage-ui ./helm/garage-ui -f values-monitoring.yaml
Example 6: Using Kubernetes Secrets for Admin Token (Recommended)
For improved security, store the admin token in a Kubernetes secret instead of in values files:
Option A: Use an existing secret
First, create a Kubernetes secret:
kubectl create secret generic garage-admin-token \
--from-literal=admin-token='your-admin-token-here'
Then configure the chart to use it:
# values-with-secret.yaml
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
# Leave admin_token empty when using existingSecret
admin_token: ""
# Reference the existing secret
existingSecret:
name: "garage-admin-token"
key: "admin-token"
Install:
helm install garage-ui ./helm/garage-ui -f values-with-secret.yaml
Option B: Let the chart create the secret
If you provide admin_token in values but don't configure existingSecret, the chart will automatically create a secret for you:
# values-auto-secret.yaml
config:
garage:
endpoint: "http://garage:3900"
admin_endpoint: "http://garage:3903"
# Chart will create a secret with this value
admin_token: "your-admin-token"
This approach keeps the token out of the ConfigMap while still allowing you to manage it through Helm values.
Upgrading
Upgrade to a new version
helm upgrade garage-ui ./helm/garage-ui -f my-values.yaml
Upgrade with new values
helm upgrade garage-ui ./helm/garage-ui \
--set config.logging.level=debug \
--reuse-values
Check upgrade history
helm history garage-ui
Rollback to previous version
helm rollback garage-ui
Accessing the Application
Method 1: Port Forward (Development)
Quick access for testing:
kubectl port-forward svc/garage-ui 8080:80
Then open: http://localhost:8080
Method 2: Ingress (Production)
If you've enabled Ingress with a domain:
# Access via your configured domain
open https://garage-ui.example.com
Method 3: NodePort (Alternative)
Change service type to NodePort:
service:
type: NodePort
port: 80
Find the assigned port:
kubectl get svc garage-ui
Method 4: LoadBalancer (Cloud Environments)
For cloud providers with LoadBalancer support:
service:
type: LoadBalancer
port: 80
Get the external IP:
kubectl get svc garage-ui
Monitoring
Prometheus ServiceMonitor
Enable Prometheus metrics scraping (requires Prometheus Operator):
serviceMonitor:
enabled: true
interval: 30s
path: /api/v1/monitoring/metrics
labels:
prometheus: kube-prometheus
Metrics Endpoint
The application exposes metrics at:
- Path:
/api/v1/monitoring/metrics - Format: Prometheus format (proxies Garage Admin API metrics)
Health Checks
Health endpoint available at:
- Path:
/health - Response:
{"status": "ok", "version": "0.1.0"}
Used by Kubernetes liveness and readiness probes.
Troubleshooting
Pods Not Starting
Check pod status and logs:
kubectl get pods -l app.kubernetes.io/name=garage-ui
kubectl describe pod -l app.kubernetes.io/name=garage-ui
kubectl logs -l app.kubernetes.io/name=garage-ui
Common issues:
- Missing admin token: Ensure
config.garage.admin_tokenis set orconfig.garage.existingSecret.namepoints to a valid secret - Secret not found: If using
existingSecret, verify the secret exists:kubectl get secret <secret-name> - Unreachable Garage: Verify endpoints are accessible from within the cluster
- Invalid OIDC config: Check all OIDC URLs and credentials when using
auth.oidc.enabled: true
Cannot Access the UI
For Ingress issues:
kubectl get ingress
kubectl describe ingress garage-ui
Check Ingress controller logs:
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
Common issues:
- DNS not configured: Ensure your domain points to the Ingress controller
- Certificate issues: Check cert-manager logs if using automatic TLS
- Ingress class mismatch: Verify
ingress.classNamematches your controller
Configuration Not Updating
The deployment includes a ConfigMap checksum annotation that automatically triggers pod restarts when configuration changes. If it's not working:
kubectl rollout restart deployment/garage-ui
Connection to Garage Fails
Test connectivity from within the pod:
kubectl exec -it deployment/garage-ui -- sh
# Inside the pod:
curl http://garage:3900
curl http://garage:3903/health
Authentication Issues
Admin Auth:
- Verify username/password in
config.auth.admin - Ensure
config.auth.admin.enabledis set totrue - Check browser developer tools for 401 errors
- Verify JWT token is being sent in Authorization header
OIDC:
- Ensure
config.auth.oidc.enabledis set totrue - Verify all OIDC URLs are accessible
- Check OIDC provider logs
- Ensure redirect URI is registered:
https://your-domain/auth/oidc/callback - Verify client ID and secret
View Application Logs
# Follow logs
kubectl logs -f -l app.kubernetes.io/name=garage-ui
# View recent logs
kubectl logs --tail=100 -l app.kubernetes.io/name=garage-ui
# Export logs
kubectl logs -l app.kubernetes.io/name=garage-ui > garage-ui.log
Debug Mode
Enable debug logging:
config:
logging:
level: debug
Uninstalling
Remove the release
helm uninstall garage-ui
This removes all Kubernetes components associated with the chart.
Clean up completely
If you want to remove all associated resources:
# Uninstall the release
helm uninstall garage-ui
# Remove any remaining ConfigMaps
kubectl delete configmap -l app.kubernetes.io/name=garage-ui
# Remove any remaining Secrets
kubectl delete secret -l app.kubernetes.io/name=garage-ui
Advanced Configuration
Using Secrets for Sensitive Data
The chart supports using Kubernetes secrets for sensitive data to avoid storing credentials in values files.
Admin Token from Secret
The chart has built-in support for storing the admin token in a Kubernetes secret:
Method 1: Use an existing secret (recommended)
# Create the secret
kubectl create secret generic garage-admin-token \
--from-literal=admin-token='your-admin-token-here'
Configure in values:
config:
garage:
existingSecret:
name: "garage-admin-token"
key: "admin-token"
Method 2: Let the chart create the secret
Simply provide the admin_token in values, and the chart will automatically create a secret:
config:
garage:
admin_token: "your-admin-token"
The chart will:
- Create a secret named
<release-name>-admin-tokenwith the token - Remove the token from the ConfigMap
- Inject it into the pod via environment variable
This keeps sensitive data out of ConfigMaps while maintaining easy Helm-based management.
JWT Private Key for Session Tokens
The chart automatically manages JWT private keys for signing session tokens. You have three options:
Method 1: Auto-generation (recommended for most cases)
By default, if you don't provide a JWT private key, the chart will automatically generate an Ed25519 private key on first install and persist it in a Kubernetes secret. This key will be reused across upgrades, ensuring session tokens remain valid.
config:
auth:
# Leave both empty - chart will auto-generate and persist a key
jwt_private_key: ""
jwt_private_key_secret:
name: ""
The chart will:
- Run a pre-install/pre-upgrade Job to generate an Ed25519 key
- Store it in a secret named
<release-name>-jwt-key - Preserve the secret across chart upgrades (using
helm.sh/resource-policy: keep)
Method 2: Provide your own key inline
Generate a key manually and include it in values:
# Generate Ed25519 private key
openssl genpkey -algorithm ED25519 -out jwt-key.pem
config:
auth:
jwt_private_key: |
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEI...
-----END PRIVATE KEY-----
jwt_private_key_secret:
name: ""
Method 3: Use an existing Kubernetes secret
Create the secret manually:
# Generate key
openssl genpkey -algorithm ED25519 -out jwt-key.pem
# Create secret
kubectl create secret generic my-jwt-key \
--from-file=jwt-key.pem=jwt-key.pem
Configure in values:
config:
auth:
jwt_private_key: ""
jwt_private_key_secret:
name: "my-jwt-key"
key: "jwt-key.pem"
Important Notes:
- Ed25519 keys are recommended over RSA for better performance and security
- The auto-generated key persists across upgrades, so tokens remain valid
- For multi-replica deployments, all pods share the same key from the secret
- The secret is marked with
helm.sh/resource-policy: keepto prevent deletion during uninstall
OIDC Client Secret
For OIDC credentials, you can similarly use secrets:
Method 1: Let the chart create the secret
config:
auth:
oidc:
enabled: true
client_secret: "your-oidc-secret"
# Chart will create a secret automatically
Method 2: Use an existing secret
kubectl create secret generic garage-oidc \
--from-literal=client-secret='your-oidc-secret'
config:
auth:
oidc:
enabled: true
client_secret: "" # Leave empty when using existingSecret
existingSecret:
name: "garage-oidc"
key: "client-secret"
Network Policies
Enable network policies for enhanced security:
networkPolicy:
enabled: true
policyTypes:
- Ingress
- Egress
This restricts network traffic to/from the pods. Requires a CNI that supports NetworkPolicy (e.g., Calico, Cilium).
Custom Container Images
Use a custom or private registry:
image:
repository: myregistry.example.com/garage-ui
tag: "v1.0.0"
pullPolicy: Always
imagePullSecrets:
- name: myregistrykey
Version Compatibility
| Chart Version | App Version | Kubernetes | Garage |
|---|---|---|---|
| 0.1.1 | v0.0.6 | 1.19+ | 0.8+ |
Additional Resources
- GitHub Repository: https://github.com/Noooste/garage-ui
- Garage Documentation: https://garagehq.deuxfleurs.fr/
- Issue Tracker: https://github.com/Noooste/garage-ui/issues
- Helm Documentation: https://helm.sh/docs/
Support
For help and support:
- Documentation: Check this README and the values.yaml file
- GitHub Issues: Report bugs or request features at https://github.com/Noooste/garage-ui/issues
- Garage Community: Visit https://garagehq.deuxfleurs.fr/ for Garage-specific questions
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
License
This Helm chart is open source and distributed under the same license as Garage UI.