Skip to main content
CleanStart

Knowledge Hub

Deploying CleanStart Images with Helm Charts on Kubernetes

Related Fundamentals: Understand the orchestration context by reviewing Kubernetes Manifests and Deployments and Container Orchestration - Kubernetes.

CleanStart provides auto-generated Helm charts pre-configured with security best practices: non-root user (UID 65532), dropped Linux capabilities, read-only root filesystem, and network policies. These charts are Bitnami-compatible drop-in replacements—deploy CleanStart images in place of Bitnami with a 30-second values change.

Installing CleanStart Helm Charts

1. Add Helm Repository

# Add CleanStart Helm repositoryhelm repo add cleanstart https://helm.cleanstart.dev/chartshelm repo update # List available chartshelm repo search cleanstart# Output:# NAME                               CHART VERSION    APP VERSION    DESCRIPTION# cleanstart/apache                  13.0.0          2.4.57        Apache HTTP server# cleanstart/nginx                   14.1.0          1.25.0        NGINX web server# cleanstart/postgresql              14.0.0          15.2           PostgreSQL database# cleanstart/redis                   17.1.0          7.0.8          Redis in-memory data store# cleanstart/mysql                   9.3.0           8.0.32        MySQL database# cleanstart/mongodb                 13.0.0          6.0.0          MongoDB NoSQL database# cleanstart/kafka                   20.0.0          3.3.1          Apache Kafka message broker

2. Install Chart with Default Values

# Install with defaults (single replica, no persistence)helm install my-app cleanstart/nginx \  --namespace default \  --create-namespace # Install with custom release namehelm install my-release-name cleanstart/postgresql \  --namespace database # Verify installationhelm list -n databasekubectl get pods -n database

Security Defaults in CleanStart Charts

Every CleanStart Helm chart includes:

# Non-root usersecurityContext:  runAsNonRoot: true  runAsUser: 65532      # appuser  runAsGroup: 65532  fsGroup: 65532  capabilities:    drop:      - ALL             # Drop all Linux capabilities  readOnlyRootFilesystem: true  allowPrivilegeEscalation: false # Resource limitsresources:  requests:    memory: "256Mi"    cpu: "100m"  limits:    memory: "512Mi"    cpu: "500m" # Network policiesnetworkPolicy:  enabled: true  ingress:    - from:        - podSelector:            matchLabels:              app: frontend

Bitnami Drop-In Replacement

Side-by-Side Comparison

Aspect

Bitnami

CleanStart

Base OS

Debian

Alpine

Image Size

200-400MB

40-80MB

Security Context

UID 1001

UID 65532 (non-root)

Capabilities

Many allowed

All dropped

Read-only filesystem

Optional

Default

FIPS compliance

No

Yes

Scanning

Annual

Real-time

Switching from Bitnami to CleanStart

Before (Bitnami):

helm install my-postgres bitnami/postgresql \  --set auth.password=mypassword \  --set persistence.size=10Gi

After (CleanStart):

helm install my-postgres cleanstart/postgresql \  --set auth.password=mypassword \  --set persistence.size=10Gi

That's it! The interface is identical. The only difference is the security posture and image size.

Custom Deployment: PostgreSQL Example

Create values-prod.yaml:

# Image configurationimage:  repository: us-docker.pkg.dev/my-project/prod/postgresql  tag: 15.2-prod-signed  pullPolicy: IfNotPresent # Pod count and strategyreplicaCount: 2strategy:  type: RollingUpdate  rollingUpdate:    maxSurge: 1    maxUnavailable: 0 # Storage (persistent volume)persistence:  enabled: true  size: 50Gi  storageClassName: gce-ssd  annotations:    snapshot.storage.kubernetes.io/allow-volume-clone: "true" # Database initializationauth:  username: postgres  password: "$(POSTGRES_PASSWORD)"  # From secret  database: myapp_db  postgresPassword: "$(POSTGRES_ROOT_PASSWORD)" # Primary node configurationprimary:  resources:    requests:      memory: "2Gi"      cpu: "1000m"    limits:      memory: "4Gi"      cpu: "2000m"   # Liveliness and readiness probes  livenessProbe:    enabled: true    initialDelaySeconds: 30    periodSeconds: 10   readinessProbe:    enabled: true    initialDelaySeconds: 10    periodSeconds: 5 # Replication (hot standby)readReplicas:  enabled: true  replicaCount: 1  resources:    requests:      memory: "1Gi"      cpu: "500m"    limits:      memory: "2Gi"      cpu: "1000m" # Backup and WAL archivingbackup:  enabled: true  schedule: "0 2 * * *"  # 2 AM daily  destinationBucket: gs://my-backup-bucket  retentionDays: 30 # Monitoring and alertsmonitoring:  enabled: true  prometheus:    enabled: true  serviceMonitor:    enabled: true    interval: 30s # Network policiesnetworkPolicy:  enabled: true  ingress:    - from:        - namespaceSelector:            matchLabels:              app: myapp      ports:        - protocol: TCP          port: 5432 # Pod disruption budgetpodDisruptionBudget:  enabled: true  minAvailable: 1 # Security context (already defaults to secure settings)securityContext:  runAsNonRoot: true  runAsUser: 65532  capabilities:    drop:      - ALL  readOnlyRootFilesystem: true

Deploy with custom values:

# Install with production valueshelm install my-postgres cleanstart/postgresql \  --namespace database \  --create-namespace \  -f values-prod.yaml # Verify installationhelm status my-postgres -n databasekubectl get pods -n databasekubectl describe pod -n database -l app=postgresql

StatefulSet Configuration for Stateful Apps

CleanStart automatically uses StatefulSets for stateful applications:

# PostgreSQL (StatefulSet by default)helm install postgres cleanstart/postgresql -n database # Redis (StatefulSet with replicas for Sentinel)helm install redis cleanstart/redis \  -n cache \  --set replica.replicaCount=2 # Kafka (StatefulSet for broker scaling)helm install kafka cleanstart/kafka \  -n messaging \  --set broker.replicaCount=3

Verify StatefulSet deployment:

kubectl get statefulset -n database# Output:# NAME                     READY   AGE# my-postgres-postgresql   2/2     2m kubectl get pods -n database -o wide# Output:# NAME                           READY   STATUS    RESTARTS   AGE    IP# my-postgres-postgresql-0       1/1     Running   0          2m     10.0.0.5# my-postgres-postgresql-1       1/1     Running   0          2m     10.0.0.6

Deployment Examples

Example 1: NGINX with CleanStart

# Create namespacekubectl create namespace web # Install NGINXhelm install web-server cleanstart/nginx \  --namespace web \  --set service.type=LoadBalancer \  --set service.loadBalancerSourceRanges='{10.0.0.0/8,172.16.0.0/12}' # Verifykubectl get svc -n web# Output: LoadBalancer IP address # Access applicationcurl http://LOAD_BALANCER_IP

Example 2: PostgreSQL with High Availability

# values-ha.yamlprimary:  replicaCount: 1  persistence:    size: 100Gi    storageClassName: fast-ssd readReplicas:  enabled: true  replicaCount: 3  # 3 read replicas backup:  enabled: true  schedule: "0 */4 * * *"  # Every 4 hours  destinationBucket: gs://pg-backups monitoring:  enabled: true  prometheus:    enabled: true
helm install postgres cleanstart/postgresql \  -n database \  -f values-ha.yaml

Example 3: Redis Cluster with Sentinel

# values-redis-sentinel.yamlmode: sentinel  # or standalone, master-replica sentinel:  enabled: true  replicas: 3  quorum: 2 replica:  replicaCount: 2  persistence:    size: 50Gi monitoring:  enabled: true
helm install redis-sentinel cleanstart/redis \  -n cache \  -f values-redis-sentinel.yaml

Liveness and Readiness Probes

CleanStart charts include pre-configured probes:

# Automatic probe configuration by service typelivenessProbe:  httpGet:    path: /health    port: http  initialDelaySeconds: 30  periodSeconds: 10  timeoutSeconds: 5  failureThreshold: 3 readinessProbe:  httpGet:    path: /ready    port: http  initialDelaySeconds: 5  periodSeconds: 5  timeoutSeconds: 3  failureThreshold: 1

Test probes:

# Check probe statuskubectl describe pod POD_NAME -n NAMESPACE | grep -A 5 "Liveness\|Readiness" # Manually test endpointkubectl port-forward POD_NAME 8080:8080 -n NAMESPACEcurl http://localhost:8080/healthcurl http://localhost:8080/ready

Signal Forwarding and Graceful Shutdown

CleanStart Helm charts handle SIGTERM properly for graceful shutdown:

# In chart templatelifecycle:  preStop:    exec:      command: ["/bin/sh", "-c", "sleep 15"] terminationGracePeriodSeconds: 30

This ensures:

  1. Pod receives SIGTERM
  2. Application drains connections (15 second pause)
  3. Pod terminates cleanly after 30 seconds max

Verify graceful shutdown:

# Delete pod and observe logskubectl delete pod POD_NAME -n NAMESPACEkubectl logs POD_NAME -n NAMESPACE --timestamps=true | tail -20# You should see "Shutting down gracefully..."

ConfigMap and Secret Integration

Using ConfigMaps for Configuration

apiVersion: v1kind: ConfigMapmetadata:  name: app-config  namespace: defaultdata:  app.conf: |    server.port: 8080    database.pool_size: 20    logging.level: info
# Deploy ConfigMapkubectl apply -f configmap.yaml # Reference in Helm valueshelm install my-app cleanstart/myapp \  --set-file config.file=./app.conf # Or create from ConfigMapkubectl create configmap app-config --from-file=app.conf helm install my-app cleanstart/myapp \  --set config.existingConfigMap=app-config

Using Secrets for Sensitive Data

# Create secret from CLIkubectl create secret generic db-credentials \  --from-literal=username=postgres \  --from-literal=password=ACTUAL_PASSWORD \  -n database # Reference in Helm valueshelm install postgres cleanstart/postgresql \  --set auth.existingSecret=db-credentials \  --set auth.secretKeys.adminPasswordKey=password

Upgrades and Rollbacks

# Check current versionhelm list -n database # Upgrade to new versionhelm upgrade my-postgres cleanstart/postgresql \  -n database \  --values values-prod.yaml \  --wait # Monitor upgrade progresskubectl rollout status statefulset/my-postgres-postgresql -n database # Rollback to previous versionhelm rollback my-postgres -n database # Get release historyhelm history my-postgres -n database

Troubleshooting

Pod Not Starting

# Check pod eventskubectl describe pod POD_NAME -n NAMESPACE # Check logskubectl logs POD_NAME -n NAMESPACEkubectl logs POD_NAME -n NAMESPACE --previous  # If crashed # Check resource availabilitykubectl describe node

Readiness Probe Failing

# Manual endpoint testkubectl exec POD_NAME -n NAMESPACE -- \  curl -v http://localhost:8080/ready # Check probe configurationkubectl get pod POD_NAME -n NAMESPACE -o yaml | \  grep -A 10 readinessProbe

Storage Issues

# Check PVC statuskubectl get pvc -n NAMESPACE # Describe PVCkubectl describe pvc CLAIM_NAME -n NAMESPACE # Check storage classkubectl get storageclasskubectl describe storageclass STORAGE_CLASS_NAME

Network Policy Blocking Traffic

# Test connectivitykubectl exec SOURCE_POD -n NAMESPACE -- \  curl -v http://DEST_POD:PORT # Review network policieskubectl get networkpolicy -n NAMESPACEkubectl describe networkpolicy POLICY_NAME -n NAMESPACE

Performance Tuning

# values-performance.yamlresources:  requests:    memory: "4Gi"    cpu: "2000m"  limits:    memory: "8Gi"    cpu: "4000m" # Use fast storagepersistence:  storageClassName: fast-ssd  # or nvme # Enable caching layerscache:  enabled: true  size: 2Gi

Monitoring Integration

All CleanStart Helm charts integrate with Prometheus:

# Enable monitoringhelm install my-app cleanstart/nginx \  --set monitoring.enabled=true \  --set monitoring.prometheus.enabled=true # Access metricskubectl port-forward svc/my-app-metrics 9090:9090curl http://localhost:9090/metrics

AI/ML Workload Deployment with Helm

For deploying AI and machine learning workloads to Kubernetes with GPU support, resource allocation, and specialized model serving patterns, see AI Container Helm Deployments. For detailed Bitnami Helm chart value references and configuration options, see Helm Chart Reference.

Uninstalling Charts

# Uninstall releasehelm uninstall my-app -n NAMESPACE # Uninstall and keep release history for rollbackhelm uninstall my-app -n NAMESPACE --keep-history # Clean up PVCs (important for stateful apps)kubectl delete pvc -l release=my-app -n NAMESPACE