Drop-in replacement guide for smooth migration from Bitnami to CleanStart container images.
Why Migrate from Bitnami?
Security Improvements
CleanStart offers faster patching with CVEs addressed in days rather than weeks, a minimal footprint with a smaller attack surface and fewer packages, transparent sourcing with supply chain clarity and verification, and compliance-ready variants including FIPS support for regulated environments.
Cost Reduction
Smaller images enable faster pulls and consume less storage, lower resource usage optimizes for container environments, reduced egress decreases bandwidth costs, and community-driven open-source eliminates the need for commercial licenses.
Operational Simplicity
All CleanStart images follow consistent patterns and conventions, better documentation provides language and framework-specific guides, language-native tooling works with npm, pip, Maven, cargo, and other package managers, and Kubernetes-friendly design is optimized for cloud-native deployments.
Quick Comparison
Aspect | Bitnami | CleanStart |
|---|---|---|
CVE patching | 7-14 days | 1-3 days |
Image size | 400-800 MB | 150-400 MB |
Non-root user | Yes | Yes |
Health checks | Basic | Pre-configured |
FIPS support | Extra fee | Variant available |
Cost | Subscription | Free |
Community | Commercial | Open |
The 30-Second Swap: Helm Charts
If you're using Bitnami Helm charts, the migration is trivial. The following diagram illustrates the Helm-based Bitnami to CleanStart migration flow:
graph TD A["Running Bitnami<br/>Helm Release<br/>postgres:15"] -->|Current| B["docker.io/<br/>bitnami/<br/>postgresql:15"] A -->|Upgrade| C["Update values.yaml"] C -->|Change| D["image.repository<br/>bitnami/postgresql →<br/>cleanstart/postgresql"] C -->|Update| E["image.tag<br/>15 → 16<br/>Optional"] D -->|Execute| F["helm upgrade<br/>my-release<br/>my-chart"] E -->|Execute| F F -->|Pull| G["docker.io/<br/>cleanstart/<br/>postgresql:16"] G -->|Replace| H["Rolling Update<br/>Old Pod"] H -->|Drain| I["Pod Evicted"] I -->|New| J["New Pod<br/>CleanStart<br/>Image"] J -->|Ready| K["Pod Running<br/>CleanStart"] K -->|Verify| L["Health Check<br/>Database Ready"] L -->|Success| M["Migration<br/>Complete<br/>30 seconds"] B -->|Compare| N["Bitnami<br/>400-800 MB<br/>7-14 day patches<br/>Commercial"] G -->|Compare| O["CleanStart<br/>150-400 MB<br/>1-3 day patches<br/>Free"] M -->|Benefits| P["Smaller Size<br/>Faster Patching<br/>Lower Cost"] style A fill:#ffcccc style C fill:#ffff99 style F fill:#ffff99 style G fill:#ccffcc style M fill:#99ff99 style P fill:#99ff99If you're using Bitnami Helm charts, the migration is trivial.
Step 1: Update values.yaml
# Before (Bitnami)image: registry: docker.io repository: bitnami/postgresql tag: "15" # After (CleanStart)image: registry: docker.io repository: cleanstart/postgresql tag: "16"Step 2: Deploy
helm upgrade my-release my-chart \ -f values.yamlThat's it! The Helm chart is compatible.
Language-Specific Migrations
Node.js Migration
Before (Bitnami):
FROM bitnami/node:18 COPY . .RUN npm install --productionAfter (CleanStart):
FROM cleanstart/node:20 COPY . .RUN npm install --productionTest:
docker build -t myapp:new .docker run --rm myapp:new node -v # Check versiondocker run --rm myapp:new npm -v # Check npmPython Migration
Before (Bitnami):
FROM bitnami/python:3.11 COPY requirements.txt .RUN pip install -r requirements.txtCOPY . .After (CleanStart):
FROM cleanstart/python:3.12 COPY requirements.txt .RUN pip install -r requirements.txtCOPY . .Java Migration
Before (Bitnami):
FROM bitnami/java:17 COPY target/*.jar app.jarAfter (CleanStart):
FROM cleanstart/java:21 COPY target/*.jar app.jarPostgreSQL Migration
Before (Bitnami):
docker run -d \ -e POSTGRESQL_USERNAME=user \ -e POSTGRESQL_PASSWORD=password \ -e POSTGRESQL_DATABASE=mydb \ bitnami/postgresql:15After (CleanStart):
docker run -d \ -e POSTGRES_USER=user \ -e POSTGRES_PASSWORD=password \ -e POSTGRES_DB=mydb \ cleanstart/postgresql:16Redis Migration
Before (Bitnami):
docker run -d \ -e REDIS_PASSWORD=password \ bitnami/redis:7After (CleanStart):
docker run -d \ -e REDIS_PASSWORD=password \ cleanstart/redis:7.2Environment Variable Mapping
CleanStart uses standard environment variables (not Bitnami's custom ones):
PostgreSQL
Bitnami | CleanStart | Usage |
|---|---|---|
|
| DB user |
|
| DB password |
|
| Default database |
MySQL
Bitnami | CleanStart | Usage |
|---|---|---|
|
| Root password |
|
| Regular user |
|
| User password |
|
| Default database |
MongoDB
Bitnami | CleanStart | Usage |
|---|---|---|
|
| Root user |
|
| Root password |
|
| Default database |
Step-by-Step Migration
Phase 1: Testing (1-2 days)
# Step 1: Create test container with CleanStart imagedocker pull cleanstart/postgresql:16docker run -d --name postgres-test \ -e POSTGRES_USER=testuser \ -e POSTGRES_PASSWORD=testpass \ -e POSTGRES_DB=testdb \ cleanstart/postgresql:16 # Step 2: Wait for startupsleep 5 # Step 3: Test connectivitydocker exec postgres-test psql -U testuser -d testdb -c "SELECT version();" # Step 4: Verify data restoration (if migrating existing data)docker exec postgres-test pg_restore -U testuser -d testdb /backup/dump.sql # Step 5: Run queriesdocker exec postgres-test psql -U testuser -d testdb -c "SELECT COUNT(*) FROM users;" # Step 6: Check disk usagedocker inspect postgres-test | jq '.[0].Size'Phase 2: Staging Deployment (3-5 days)
Create docker-compose.yml for staging:
version: '3.9'services: postgres: image: cleanstart/postgresql:16 container_name: postgres-staging environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} ports: - "5433:5432" volumes: - postgres-staging-data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] interval: 10s timeout: 5s retries: 5 volumes: postgres-staging-data:Deploy and validate:
docker-compose -f docker-compose.yml up -d# Run integration tests against staging./integration-tests.sh --environment stagingPhase 3: Production Migration (1 day)
For RDB (Redis) or In-Memory Caches
# No data persistence neededdocker stop redis-olddocker rm redis-olddocker run -d \ --name redis-new \ -p 6379:6379 \ cleanstart/redis:7.2For Databases with Persistent Data
# Step 1: Create backup of Bitnami databasedocker exec bitnami-postgres pg_dump -U user mydb > backup.sql # Step 2: Start CleanStart containerdocker run -d \ --name postgres-new \ -e POSTGRES_USER=user \ -e POSTGRES_PASSWORD=password \ -e POSTGRES_DB=mydb \ -v postgres-new-data:/var/lib/postgresql/data \ cleanstart/postgresql:16 # Step 3: Wait for startupsleep 10 # Step 4: Restore datadocker exec -i postgres-new psql -U user -d mydb < backup.sql # Step 5: Verify data integritydocker exec postgres-new psql -U user -d mydb -c "SELECT COUNT(*) FROM users;" # Step 6: Switch traffic (update connection strings)# Update app config to point to postgres-new # Step 7: Stop old containerdocker stop bitnami-postgresZero-Downtime Migration with Replication
For critical databases, use replication:
# Step 1: Configure Bitnami as masterdocker exec bitnami-postgres psql -U user -c \ "ALTER USER replicator WITH REPLICATION PASSWORD 'reppassword';" # Step 2: Start CleanStart as replicadocker run -d \ --name postgres-replica \ -e PGUSER=replicator \ -e PGPASSWORD=reppassword \ cleanstart/postgresql:16 \ pg_basebackup -h bitnami-postgres-ip -D /var/lib/postgresql/data -U replicator # Step 3: Monitor replication lagdocker exec postgres-replica psql -U replicator -c \ "SELECT slot_name, slot_type, active FROM pg_replication_slots;" # Step 4: Promote replica to masterdocker exec postgres-replica psql -U postgres -c "SELECT pg_promote();" # Step 5: Update connection strings and stop old masterTroubleshooting Migration Issues
Issue: Connection Refused
Problem: Application can't connect to new CleanStart container
Solution:
# Verify container is runningdocker ps | grep cleanstart # Check port mappingdocker inspect mydb | jq '.[0].NetworkSettings.Ports' # Test connectivitydocker exec mydb nc -zv localhost 5432Issue: Data Corruption
Problem: Data doesn't match between Bitnami and CleanStart
Solution:
# Export data from both containersdocker exec bitnami-db pg_dump -U user mydb > bitnami.sqldocker exec cleanstart-db pg_dump -U user mydb > cleanstart.sql # Compare schemasdiff <(grep "^CREATE" bitnami.sql) <(grep "^CREATE" cleanstart.sql) # Compare row countsdocker exec bitnami-db psql -U user -d mydb -c "SELECT COUNT(*) FROM users;"docker exec cleanstart-db psql -U user -d mydb -c "SELECT COUNT(*) FROM users;"Issue: Performance Degradation
Problem: CleanStart container runs slower than Bitnami
Solution:
# Check resource limitsdocker inspect cleanstart-db | jq '.[0].HostConfig.Memory' # Monitor CPU and memorydocker stats cleanstart-db # Compare performance# Bitnami testdocker exec bitnami-db psql -U user -d mydb -c "EXPLAIN ANALYZE SELECT * FROM large_table;" # CleanStart testdocker exec cleanstart-db psql -U user -d mydb -c "EXPLAIN ANALYZE SELECT * FROM large_table;"Issue: Configuration Incompatibility
Problem: Bitnami config options don't work with CleanStart
Solution:
# Check available environment variablesdocker run --rm cleanstart/postgresql:16 env | grep POSTGRES # Compare with Bitnami documentation# Use standard PostgreSQL configuration instead of Bitnami's custom vars # For advanced config, mount postgresql.confdocker run -d \ -v ./postgresql.conf:/etc/postgresql/postgresql.conf \ cleanstart/postgresql:16 \ postgres -c config_file=/etc/postgresql/postgresql.confKubernetes Migration
Update Deployments
# Current Bitnami deploymentkubectl set image deployment/postgres \ postgres=docker.io/bitnami/postgresql:15 # Migrate to CleanStartkubectl set image deployment/postgres \ postgres=docker.io/cleanstart/postgresql:16 --recordUpdate StatefulSets
# BeforeapiVersion: apps/v1kind: StatefulSetmetadata: name: postgresspec: template: spec: containers: - name: postgres image: bitnami/postgresql:15 # AfterapiVersion: apps/v1kind: StatefulSetmetadata: name: postgresspec: template: spec: containers: - name: postgres image: cleanstart/postgresql:16Apply changes:
kubectl apply -f statefulset.yamlkubectl rollout status statefulset/postgresHelm Chart Update
# Update values for Bitnami chart to use CleanStart imagehelm upgrade my-release bitnami/postgresql \ --set image.repository=cleanstart/postgresql \ --set image.tag=16 \ --set image.registry=docker.io # Or use custom valuescat > values.yaml <<EOFimage: registry: docker.io repository: cleanstart/postgresql tag: "16"EOF helm upgrade my-release bitnami/postgresql -f values.yamlPost-Migration Validation
# Verify imagedocker inspect myservice | grep -i image # Confirm versiondocker exec myservice postgres --versiondocker exec myservice redis-server --version # Check userdocker exec myservice id# Should output: uid=1000(appuser) gid=1000(appuser)... # Verify persistent volumedocker volume ls | grep myservice # Monitor logsdocker logs -f myservice # Load testing./load-test.sh myservice 1000 requestsRollback Plan
If issues occur after migration:
# Quick rollbackdocker stop postgres-newdocker run -d --name postgres-old \ -v postgres-old-data:/var/lib/postgresql/data \ bitnami/postgresql:15 # Or for Kuberneteskubectl rollout undo deployment/postgresCost Comparison
Before (Bitnami)
Bitnami license: $1,000/yearImage size (500 MB × 3 services): Large egress costsLarger containers: More memory/CPU neededStorage overhead: 5 GB totalAfter (CleanStart)
CleanStart license: Free (open source)Image size (200 MB × 3 services): Reduced egressSmaller containers: Less memory/CPUStorage overhead: 2 GB total Total savings: $1,000+ year, 30% reduced resource usageMigration Timeline
Phase | Duration | Tasks |
|---|---|---|
Planning | 3-5 days | Assess services, create plan |
Testing | 3-5 days | Local and staging testing |
Staging | 3-5 days | Deploy to staging, validate |
Production | 1-2 days | Execute migration, monitor |
Validation | 7 days | Monitor metrics, resolve issues |
Total | 2-3 weeks | Complete migration of 5-10 services |
Support Resources
Language Getting Started Guides. Application Setup Guides. Troubleshooting Guide. Bitnami Helm Values Reference — Comprehensive mapping of Bitnami chart values for configuration compatibility.
Next Steps
- Choose your first service for migration
- Follow the language-specific getting-started guide
- Test thoroughly in staging
- Execute production migration
- Monitor for 7 days
- Document lessons learned
