Kubernetes
Deploy Openfuse on Kubernetes with Deployments, Jobs, Secrets, and Ingress. Includes manifests for the API, Keycloak, UI, and the Config Importer as a Helm hook.
This guide covers deploying Openfuse on Kubernetes. For the single-server Docker Compose setup, see Docker Compose.
Prerequisites
- A Kubernetes cluster (1.26+)
- A managed PostgreSQL 17+ instance with two databases:
openfuseandkeycloak - An Ingress controller or Gateway API implementation
- Wildcard DNS records pointing to your ingress
Routing
Configure your Ingress or Gateway to route by hostname:
| Pattern | Target | Port |
|---|---|---|
*.api.<ROOT_DOMAIN> | API Service | 3000 |
sso.<ROOT_DOMAIN> | Keycloak Service | 8080 |
| Everything else | UI Service | 80 |
TLS termination happens at the Ingress. Use cert-manager for automated certificate management.
Secrets
apiVersion: v1
kind: Secret
metadata:
name: openfuse-secrets
type: Opaque
stringData:
DATABASE_PASSWORD: 'your-db-password'
SESSION_SECRET: 'your-session-secret-32-chars-min'
KC_BOOTSTRAP_ADMIN_PASSWORD: 'your-keycloak-admin-password'
KC_STAFF_BACKEND_CLIENT_SECRET: 'generated-secret'
KC_STAFF_BFF_CLIENT_SECRET: 'generated-secret'
KC_TENANTS_BACKEND_CLIENT_SECRET: 'generated-secret'
KC_TENANTS_BFF_CLIENT_SECRET: 'generated-secret'
KC_TENANTS_SDK_CLIENT_SECRET: 'generated-secret'For production, use External Secrets Operator or your cloud provider's secret store CSI driver instead of static Secret manifests.
Config Importer
Run the Config Importer as a Job. If you use Helm, add it as a pre-install/pre-upgrade hook:
apiVersion: batch/v1
kind: Job
metadata:
name: openfuse-config-importer
annotations:
'helm.sh/hook': pre-install,pre-upgrade
'helm.sh/hook-delete-policy': before-hook-creation
spec:
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: OnFailure
containers:
- name: config-importer
image: ghcr.io/openfuseio/openfuse-config-importer:1.0.0
env:
- name: KEYCLOAK_URL
value: 'http://keycloak:8080'
- name: KEYCLOAK_USER
value: 'admin'
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: openfuse-secrets
key: KC_BOOTSTRAP_ADMIN_PASSWORD
- name: KEYCLOAK_AVAILABILITYCHECK_ENABLED
value: 'true'
- name: IMPORT_FILES_LOCATIONS
value: '/config/*'
- name: IMPORT_VARSUBSTITUTION_ENABLED
value: 'true'
- name: IMPORT_VARSUBSTITUTION_UNDEFINEDISTERROR
value: 'true'
# ... remaining env varsThe image has realm YAML files baked in — no volume mounts needed. See the configuration reference for all Config Importer variables.
Run on first deployment and on upgrades that include realm config changes. Skip on routine restarts or scaling events.
API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: openfuse-api
spec:
replicas: 2
selector:
matchLabels:
app: openfuse-api
template:
metadata:
labels:
app: openfuse-api
spec:
containers:
- name: api
image: ghcr.io/openfuseio/openfuse-api:1.0.0
ports:
- containerPort: 3000
envFrom:
- secretRef:
name: openfuse-secrets
env:
- name: ROOT_DOMAIN
value: 'openfuse.example.com'
- name: DATABASE_HOST
value: 'your-rds-endpoint.amazonaws.com'
- name: DATABASE_SSL
value: 'true'
- name: KEYCLOAK_URL
value: 'http://keycloak:8080'
- name: KEYCLOAK_EXTERNAL_URL
value: 'https://sso.openfuse.example.com'
# ... remaining env vars
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10The API is stateless — sessions are encrypted into cookies using SESSION_SECRET, so no sticky sessions or session affinity is needed. Scale freely with multiple replicas. All replicas must share the same SESSION_SECRET. Database migrations use an advisory lock, so concurrent startups won't conflict.
Keycloak
Deploy Keycloak as a Deployment (single replica) or StatefulSet (if clustering):
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: ghcr.io/openfuseio/openfuse-keycloak:1.0.0
args: ['start']
ports:
- containerPort: 8080
- containerPort: 9000
env:
- name: KC_DB
value: 'postgres'
- name: KC_DB_URL
value: 'jdbc:postgresql://your-host:5432/keycloak?sslmode=require'
- name: KC_HOSTNAME
value: 'sso.openfuse.example.com'
- name: KC_PROXY_HEADERS
value: 'xforwarded'
- name: KC_HTTP_ENABLED
value: 'true'
- name: KC_HEALTH_ENABLED
value: 'true'
# ... remaining env vars
readinessProbe:
httpGet:
path: /health/ready
port: 9000
initialDelaySeconds: 60A single instance handles most workloads. For HA, see the Keycloak Clustering Guide.
UI
The UI is a static SPA. Deploy as a Deployment or serve from a CDN:
apiVersion: apps/v1
kind: Deployment
metadata:
name: openfuse-ui
spec:
replicas: 2
selector:
matchLabels:
app: openfuse-ui
template:
metadata:
labels:
app: openfuse-ui
spec:
containers:
- name: ui
image: ghcr.io/openfuseio/openfuse-ui:1.0.0
ports:
- containerPort: 80
env:
- name: VITE_API_BASE_HOST
value: 'api.openfuse.example.com'
- name: VITE_UI_BASE_HOST
value: 'openfuse.example.com'Alternatively, extract the static files and serve from any static host (S3 + CloudFront, Vercel, Netlify). Provide a config.js at the root for runtime configuration:
window.__OPENFUSE_CONFIG__ = {
VITE_API_BASE_HOST: 'api.openfuse.example.com',
VITE_API_PROTOCOL: 'https',
VITE_APP_ENV: 'production',
VITE_UI_BASE_HOST: 'openfuse.example.com',
VITE_UI_PROTOCOL: 'https',
}Configure SPA fallback (serve index.html for all routes). This eliminates one container and lets you use CDN caching.
Next steps
Docker Compose
Deploy Openfuse to production with managed databases, wildcard TLS, SMTP, and automated backups. A complete guide for running Openfuse reliably on your own infrastructure.
AWS ECS
Deploy Openfuse on AWS ECS with Fargate, ALB hostname-based routing, Secrets Manager, and CDK examples for the API, Keycloak, UI, and Config Importer.