Skip to main content
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.
Do not pass production secrets with --set on the command line. Use a values file with restricted permissions or a Kubernetes Secret as described in Secrets management.
# Create namespace
kubectl create ns sure || true

# Create a values file for the initial secret
SECRET_KEY_BASE=$(openssl rand -hex 32)
cat > values-quickstart.yaml <<EOF
image:
  tag: v1.2.3
rails:
  secret:
    enabled: true
    values:
      SECRET_KEY_BASE: "$SECRET_KEY_BASE"
EOF
chmod 600 values-quickstart.yaml

# Install chart with a pinned image tag
helm upgrade --install sure sure/sure \
  -n sure \
  -f values-quickstart.yaml
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, store connection strings in a Kubernetes Secret and reference them from your values file. See Secrets management for the general pattern.
cnpg:
  enabled: false

redisOperator:
  managed:
    enabled: false

redisSimple:
  enabled: false

rails:
  extraEnv:
    DATABASE_URL:
      valueFrom:
        secretKeyRef:
          name: external-db-credentials
          key: database-url
    REDIS_URL:
      valueFrom:
        secretKeyRef:
          name: external-db-credentials
          key: redis-url

Deployment profiles

Simple single-node

Minimal setup for development or small deployments:
  • encryptionEnv.enabled: true tells the chart to wire the Active Record encryption keys from your secret into the app container.
  • SELF_HOSTED: "true" enables self-hosted behavior inside Sure.
  • Replace longhorn with a storage class that exists in your cluster.
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: standalone
  replicas: 1
  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

Generate strong secrets before creating the Kubernetes Secret:
openssl rand -hex 32   # SECRET_KEY_BASE
openssl rand -hex 32   # ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
openssl rand -hex 32   # ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
openssl rand -hex 32   # redis-password
openssl rand -hex 64   # ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
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__"

  # Optional external service URLs
  database-url: "postgresql://user:pass@db.example.com:5432/sure"
  redis-url: "redis://:pass@redis.example.com:6379/0"
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=v1.2.4 \
  -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
[!NOTE] If you customized the PostgreSQL username or database name in your chart values, replace sure and sure_production in the backup and restore commands below.

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 -f /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

kubectl get pods -n sure

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:
helm test sure -n sure

Uninstall

To remove Sure from your cluster:
helm uninstall sure -n sure
This will not delete PersistentVolumeClaims. Before deleting PVCs, make sure the namespace does not contain volumes for anything other than Sure.
kubectl get pvc -n sure
kubectl delete pvc -n sure <sure-pvc-name>

Getting help

If you find bugs or have feature requests: