This guide shows you how to deploy Sure on Kubernetes using the official Helm chart. The chart supports web (Rails) and worker (Sidekiq) workloads, optional in-cluster PostgreSQL and Redis, and production-grade features like pre-upgrade migrations, pod security contexts, and horizontal pod autoscaling.
Prerequisites
- Kubernetes >= 1.25
- Helm >= 3.10
- Basic familiarity with Kubernetes and Helm
Features
- Web (Rails) deployment with service and optional ingress
- Worker (Sidekiq) deployment
- Optional database migrations via Helm hook job or initContainer
- Optional subcharts for PostgreSQL (CloudNativePG) and Redis (OT-CONTAINER-KIT redis-operator)
- Security best practices: runAsNonRoot, readOnlyRootFilesystem, no hardcoded secrets
- Scalability: replicas, resources, topology spread constraints, optional HPAs
- Optional CronJobs for custom tasks
Installation
Add Helm repositories
Add the Sure Helm repository:
helm repo add sure https://we-promise.github.io/sure
helm repo update
If you plan to use the bundled PostgreSQL or Redis subcharts, add their repositories as well:
helm repo add cloudnative-pg https://cloudnative-pg.github.io/charts
helm repo add ot-helm https://ot-container-kit.github.io/helm-charts
helm repo update
Quickstart (turnkey self-hosting)
This installs CloudNativePG operator with a Postgres cluster and Redis managed by the OT redis-operator.
For production stability, use immutable image tags (for example, image.tag=v1.2.3) instead of latest.
# Create namespace
kubectl create ns sure || true
# Install chart with a pinned image tag
helm upgrade --install sure sure/sure \
-n sure \
--set image.tag=v1.2.3 \
--set rails.secret.enabled=true \
--set rails.secret.values.SECRET_KEY_BASE=$(openssl rand -hex 32)
Expose the app via an ingress (see configuration below) or port-forward:
kubectl port-forward svc/sure 8080:80 -n sure
Navigate to http://localhost:8080 to access Sure.
Configuration
Using external Postgres and Redis
To use external managed databases instead of the bundled subcharts:
cnpg:
enabled: false
redisOperator:
managed:
enabled: false
redisSimple:
enabled: false
rails:
extraEnv:
DATABASE_URL: postgresql://user:pass@db.example.com:5432/sure
REDIS_URL: redis://:pass@redis.example.com:6379/0
Deployment profiles
Simple single-node
Minimal HA setup for development or small deployments:
image:
repository: ghcr.io/we-promise/sure
tag: "v1.0.0"
pullPolicy: IfNotPresent
rails:
existingSecret: sure-secrets
encryptionEnv:
enabled: true
settings:
SELF_HOSTED: "true"
cnpg:
enabled: true
cluster:
enabled: true
name: sure-db
instances: 1
storage:
size: 8Gi
storageClassName: longhorn
redisOperator:
enabled: true
managed:
enabled: true
mode: replication
replicas: 3
persistence:
enabled: true
className: longhorn
size: 8Gi
migrations:
strategy: job
HA k3s profile
High availability setup with multiple replicas and synchronous replication:
cnpg:
enabled: true
cluster:
enabled: true
name: sure-db
instances: 3
storage:
size: 20Gi
storageClassName: longhorn
minSyncReplicas: 1
maxSyncReplicas: 2
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
cnpg.io/cluster: sure-db
redisOperator:
enabled: true
managed:
enabled: true
mode: replication
replicas: 3
persistence:
enabled: true
className: longhorn
size: 8Gi
migrations:
strategy: job
initContainer:
enabled: true
hpa:
web:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
Secrets management
Create a Kubernetes secret with the required credentials:
apiVersion: v1
kind: Secret
metadata:
name: sure-secrets
type: Opaque
stringData:
# Rails secrets
SECRET_KEY_BASE: "__SET_SECRET__"
# Active Record Encryption keys (required for self-hosted mode)
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: "__SET_SECRET__"
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: "__SET_SECRET__"
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: "__SET_SECRET__"
# Redis password
redis-password: "__SET_SECRET__"
Apply the secret:
kubectl apply -f sure-secrets.yaml -n sure
Reference the secret in your values:
rails:
existingSecret: sure-secrets
redisOperator:
managed:
enabled: true
auth:
existingSecret: sure-secrets
passwordKey: redis-password
Ingress configuration
Enable ingress to expose Sure externally:
ingress:
enabled: true
className: "nginx"
hosts:
- host: finance.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts: [finance.example.com]
secretName: finance-tls
Horizontal pod autoscaling
Enable HPAs for automatic scaling based on CPU utilization:
hpa:
web:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
worker:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
Updating
To update to a new version of Sure:
# Update the Helm repository
helm repo update sure
# Update the deployment with a new image tag
helm upgrade sure sure/sure -n sure \
--set image.tag=v0.6.6-alpha.7 \
-f your-values.yaml
The chart will automatically run database migrations before deploying the new version.
Backup and restore
PostgreSQL backups with CloudNativePG
CloudNativePG supports volume snapshot backups:
cnpg:
cluster:
backup:
method: volumeSnapshot
volumeSnapshot:
className: longhorn
Manual backup
Create a manual backup of your PostgreSQL database:
# Get the primary pod name
PRIMARY_POD=$(kubectl get pod -n sure -l cnpg.io/cluster=sure-db,role=primary -o name)
# Create a backup
kubectl exec -n sure $PRIMARY_POD -- pg_dump -U sure sure_production > backup.sql
Restore from backup
# Copy backup to pod
kubectl cp backup.sql sure/$PRIMARY_POD:/tmp/backup.sql
# Restore
kubectl exec -n sure $PRIMARY_POD -- psql -U sure sure_production < /tmp/backup.sql
Troubleshooting
View logs
# Web logs
kubectl logs -n sure -l app.kubernetes.io/component=web
# Worker logs
kubectl logs -n sure -l app.kubernetes.io/component=worker
# Migration job logs
kubectl logs -n sure -l job-name=sure-migrate
Check pod status
Verify database connectivity
# Test connection from web pod
kubectl exec -n sure deploy/sure-web -- rails runner "puts ActiveRecord::Base.connection.execute('SELECT 1').first"
Run Helm tests
After installation, verify the deployment:
Uninstall
To remove Sure from your cluster:
helm uninstall sure -n sure
This will not delete PersistentVolumeClaims. To completely remove all data, manually delete the PVCs after uninstalling.
kubectl delete pvc -n sure --all
Getting help
If you find bugs or have feature requests: