Bitnami Helm charts are the most widely used open-source Helm charts in production Kubernetes environments. CleanStart images work as drop-in replacements in Bitnami charts because they maintain API compatibility while adding security hardening, reduced attack surface, and verifiable provenance. This reference shows exactly which values.yaml fields to change and which to keep unchanged.
How Bitnami Charts Work
Bitnami Helm charts follow a consistent structure where a values.yaml file provides default configuration, Jinja2 templates consume these values, and the templates generate Kubernetes manifests. The key integration point for container images lives in the image: section of values.yaml. This section contains the registry (where the image comes from), the repository (the image name), and the tag (version). These three fields determine which container image Kubernetes pulls, while everything else—persistence, networking, RBAC, resource limits—remains unchanged. This is why migrating to CleanStart images requires modifying only this section.
The Universal Swap Pattern
Every Bitnami chart to CleanStart migration follows the same three-line pattern. The before state shows Bitnami with registry docker.io, repository bitnami/postgresql, and tag 15.2-debian-11-r0. The after state shows CleanStart with registry registry.cleanstart.com, repository cleanstart/postgresql, and tag 15.2. You should change three fields: image.registry becomes registry.cleanstart.com, image.repository becomes cleanstart/{app-name}, and image.tag becomes the matching version (use 15.2, not Bitnami's full suffix).
Every other value in the chart remains identical. CleanStart images inherit the same interface, environment variables, volumes, and configuration patterns as Bitnami equivalents.
Complete values.yaml Examples
PostgreSQL with CleanStart
# PostgreSQL Helm Chart - values.yaml with CleanStart Imagesglobal: postgresql: auth: username: appuser password: "changeme-in-production" database: appdb image: registry: registry.cleanstart.com repository: cleanstart/postgresql tag: "15.2" pullPolicy: IfNotPresent auth: username: appuser password: "changeme-in-production" database: appdb primary: persistence: enabled: true size: 10Gi storageClassName: "standard" resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL containerSecurityContext: enabled: true runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL livenessProbe: enabled: true initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 readinessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 metrics: enabled: false backup: enabled: falseRedis with CleanStart
# Redis Helm Chart - values.yaml with CleanStart Imagesarchitecture: standalone image: registry: registry.cleanstart.com repository: cleanstart/redis tag: "7.2" pullPolicy: IfNotPresent auth: enabled: true password: "changeme-in-production" master: persistence: enabled: true size: 8Gi storageClassName: "standard" resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL containerSecurityContext: enabled: true runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL livenessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 5 readinessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 5 metrics: enabled: false sentinel: enabled: falseNGINX with CleanStart
# NGINX Helm Chart - values.yaml with CleanStart ImagesreplicaCount: 2 image: registry: registry.cleanstart.com repository: cleanstart/nginx tag: "1.25" pullPolicy: IfNotPresent service: type: LoadBalancer port: 80 targetPort: 8080 ingress: enabled: false resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "250m" securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL containerSecurityContext: enabled: true runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL livenessProbe: enabled: true initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 5 metrics: enabled: false persistence: enabled: false startupProbe: enabled: falseMongoDB with CleanStart
# MongoDB Helm Chart - values.yaml with CleanStart Imagesarchitecture: standalone image: registry: registry.cleanstart.com repository: cleanstart/mongodb tag: "6.0" pullPolicy: IfNotPresent auth: enabled: true rootPassword: "changeme-in-production" username: appuser password: "changeme-in-production" database: appdb persistence: enabled: true size: 20Gi storageClassName: "standard" resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL containerSecurityContext: enabled: true runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL livenessProbe: enabled: true initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 6 readinessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 6 metrics: enabled: false replicaSet: enabled: falseKafka with CleanStart
# Kafka Helm Chart - values.yaml with CleanStart ImagesreplicaCount: 3 image: registry: registry.cleanstart.com repository: cleanstart/kafka tag: "3.6" pullPolicy: IfNotPresent broker: resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" persistence: enabled: true size: 30Gi storageClassName: "standard" securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL containerSecurityContext: enabled: true runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL livenessProbe: enabled: true initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: enabled: true initialDelaySeconds: 5 periodSeconds: 10 zookeeper: enabled: true image: registry: registry.cleanstart.com repository: cleanstart/zookeeper tag: "3.9" persistence: enabled: true size: 8Gi resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "250m" metrics: kafka: enabled: false jmx: enabled: falseSecurity Defaults That Come Free
When you swap to CleanStart images in any Bitnami chart, several hardening features activate automatically. CleanStart applications run as UID 65532 (Bitnami uses UID 1001), which is standardized across Linux distributions as the unprivileged "nobody" equivalent, preventing privilege escalation attacks targeting fixed UIDs.
The root filesystem is mounted read-only, so applications write only to /tmp and mounted volumes. This prevents attackers from modifying binaries or system libraries after container start.
CleanStart images contain no shell, no package manager, and no debugging tools. Attackers cannot exec into containers or install backdoors even if they gain container access.
All Linux capabilities are dropped by default (--cap-drop=ALL). The image runs without the ability to load kernel modules, change file ownership, modify network settings, or access raw sockets.
CleanStart offers FIPS 140-3 variants where cryptographic operations use FIPS-validated modules. Request images tagged -fips (e.g., cleanstart/postgresql:15.2-fips) for compliance environments.
Every image includes a Bill of Materials (SBOM) in SPDX format and SLSA Level 3 provenance. Verify with cosign:
cosign verify-attestation \ --certificate-identity-regexp=".*@cleanstart.dev" \ --certificate-oidc-issuer-regexp="https://token.actions.githubusercontent.com" \ registry.cleanstart.com/cleanstart/postgresql:15.2 | jq '.payload | @base64d | fromjson'Bitnami Values You Can Remove
Several fields in standard Bitnami values.yaml become unnecessary with CleanStart. Remove vulnerability scanning configuration since CleanStart images ship verified and scanned. Simplify or remove custom security contexts for hardening since CleanStart handles these by default. Remove package manager or init container updates since there's no package manager in CleanStart images. Remove shell or debugging sidecars since distroless design has no shell. Simplify vulnerability patch version pinning since CleanStart handles patch updates (use "15.2" instead of "15.2-debian-11-r42").
Handling the UID Difference
Bitnami uses UID 1001; CleanStart uses UID 65532. This affects file permissions in persistent volumes. Choose a migration strategy based on your persistence layer.
Strategy 1: Init Container for Permission Fixes uses a busybox init container to fix ownership before the main container starts. Strategy 2: fsGroup for Automatic Permission Delegation lets Kubernetes automatically chown mounted volumes based on the fsGroup setting. Strategy 3: Replace Volume Content exports data from old pods, deletes old PVCs, deploys with CleanStart, and restores the data. Strategy 4: Accept Temporary Permission Warnings mounts volumes with default Kubernetes permissions and uses fsGroup in securityContext to fix automatically on pod start.
Multi-Service Stack Example
Migrating a full Bitnami-based stack requires changing only the image sections. Here's a complete Helm umbrella chart values.yaml deploying PostgreSQL, Redis, and NGINX with CleanStart images:
# Umbrella Chart - values.yaml with CleanStart Images# Deploy: helm install mystack bitnami/common -f values.yaml postgresql: enabled: true auth: username: postgres password: "changeme-in-production" database: appdb image: registry: registry.cleanstart.com repository: cleanstart/postgresql tag: "15.2" primary: persistence: enabled: true size: 20Gi securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" redis: enabled: true auth: enabled: true password: "changeme-in-production" image: registry: registry.cleanstart.com repository: cleanstart/redis tag: "7.2" master: persistence: enabled: true size: 10Gi securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "250m" nginx: enabled: true image: registry: registry.cleanstart.com repository: cleanstart/nginx tag: "1.25" replicaCount: 2 service: type: LoadBalancer port: 80 securityContext: enabled: true fsGroup: 65532 runAsUser: 65532 runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: - ALL resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "250m"Deploy the full stack:
helm repo add bitnami https://charts.bitnami.com/bitnamihelm repo add cleanstart https://helm.cleanstart.devhelm repo update helm install mystack bitnami/common \ --values values-cleanstart.yaml \ --namespace production \ --create-namespaceVerifying Your Migration
After deploying CleanStart images via Helm, verify the migration completed correctly.
Check Image References by running kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'. Expected output includes registry.cleanstart.com/cleanstart/* for all CleanStart services.
Verify Running UID by running kubectl exec -it postgresql-0 -- id. Expected output is uid=65532(nobody) gid=65532(nogroup) groups=65532(nogroup).
Confirm Read-Only Root Filesystem by running kubectl exec -it postgresql-0 -- touch /test-file. Expected error is "Read-only file system".
Verify No Shell Access by running kubectl exec -it postgresql-0 -- /bin/sh. Expected error is "OCI runtime exec failed: exec failed: unable to start container process".
Check Signatures and SBOM using cosign to verify image authenticity and extract SBOM details.
Verify Pod Security Policy Compliance by checking that all CleanStart pods run with UID 65532 and have read-only root filesystem enabled.
Monitor Resource Usage with kubectl top pods to verify CleanStart images typically use 20-30% less memory than Bitnami equivalents.
Cross-References
Migrating from Bitnami: Step-by-Step Guide. Bitnami Compatibility Matrix. Helm Charts in Kubernetes. Helm Fundamentals and Best Practices.
