Purpose
Get a working CleanStart test environment running locally or in staging so your QA team can start testing immediately. This guide walks through four options:
- Local with Docker Desktop (15 minutes) — fastest for developers
- Local Kubernetes with kind (20 minutes) — matches production K8s more closely
- Local Kubernetes with minikube (20 minutes) — alternative to kind
- Cloud staging cluster (30-60 minutes) — realistic infrastructure
Pick the option that matches your needs. Docker Desktop is fastest for getting started; Kubernetes options better simulate production.
Prerequisites (All Options)
Required Tools
Install these before starting:
# Check if already installedwhich dockerwhich gitwhich jqIf any are missing:
macOS (Homebrew):
brew install docker git jqbrew install --cask docker # Docker DesktopLinux (Ubuntu/Debian):
sudo apt-get updatesudo apt-get install -y docker.io git jq # Non-root Docker access (optional, but recommended)sudo usermod -aG docker $USERWindows: Docker Desktop for Windows (from docker.com) Git (from git-scm.com) WSL 2 backend (Docker Desktop setup).
Registry Authentication
You need to authenticate to CleanStart's registry to pull images:
# Create Docker credentials file (if needed)docker login registry.cleanstart.com# Username: (your CleanStart account)# Password: (your API token) # Verify authenticationdocker pull registry.cleanstart.com/python:3.12-stable# Should succeed without "unauthorized" errorIf you don't have credentials:
- Contact your CleanStart administrator
- Create an account at registry.cleanstart.com (if public signup available)
- Generate API token in account settings
Option 1: Local with Docker Desktop (15 minutes)
Best for: Developers, quick testing, learning the basics.
Step 1: Verify Docker Installation
docker version# Output shows both client and server docker info | grep "Total Memory"# Note available memory (need at least 2GB for testing)Step 2: Pull Your First CleanStart Image
# Pull Python imagedocker pull registry.cleanstart.com/python:3.12-stable # Verify the image existsdocker images | grep python# Output: cleanstart.../python 3.12-stable abc123... 150MBStep 3: Run a Container
# Start a container and interact with itdocker run --rm -it registry.cleanstart.com/python:3.12-stable # You should see Python promptPython 3.12.0 (main, ...)>>> # Test basic operations>>> import requests>>> print("CleanStart working!")CleanStart working!>>> exit()Step 4: Create docker-compose.yml for Multi-Service Testing
Use case: Test your app with a database and cache.
# docker-compose.ymlversion: '3.8' services: app: image: registry.cleanstart.com/python:3.12-stable build: context: . dockerfile: Dockerfile ports: - "8000:8000" environment: DATABASE_URL: postgresql://user:password@postgres:5432/testdb REDIS_URL: redis://redis:6379/0 depends_on: - postgres - redis volumes: - .:/app - /app/__pycache__ command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload postgres: image: postgres:15-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: testdb ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:7-alpine ports: - "6379:6379" volumes: postgres_data:Start the stack:
docker-compose up --build # In another terminal, test the APIcurl http://localhost:8000/health# Output: {"status": "ok"} # View logsdocker-compose logs appdocker-compose logs postgres # Stop everythingdocker-compose downStep 5: Test Basic Security Properties
# Test 1: No shell in imagedocker run --rm registry.cleanstart.com/python:3.12-stable /bin/bash# Should fail: "no such file or directory" # Test 2: Non-root userdocker run --rm registry.cleanstart.com/python:3.12-stable id# Output: uid=65532 (non-root) # Test 3: Read-only filesystemdocker run --rm registry.cleanstart.com/python:3.12-stable touch /test# Should fail: "Read-only file system"Verification Checklist
[ ] Docker Desktop is running (check system tray) [ ] Can authenticate to registry.cleanstart.com [ ] Can pull images without errors [ ] Can start containers [ ] Basic Python/Node/etc operations work [ ] docker-compose stack starts successfully [ ] Can access app on localhost:8000. Troubleshooting Docker Desktop:
Problem | Solution |
|---|---|
"Cannot connect to Docker daemon" | Start Docker Desktop, wait 30 seconds |
"denied: unauthorized" | Run |
"image not found" | Check image name spelling (case-sensitive) |
"port already in use" | Change port in docker-compose (8001:8000) |
"out of memory" | Increase Docker Desktop memory limit (preferences → resources) |
Option 2: Local Kubernetes with kind (20 minutes)
Best for: K8s testing, admission controller testing, pod security policies.
What is kind?
kind = Kubernetes in Docker. It runs a full Kubernetes cluster inside Docker containers. Perfect for testing K8s features like networking, RBAC, and operators.
Step 1: Install kind
# macOSbrew install kind # Linuxcurl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64chmod +x ./kindsudo mv ./kind /usr/local/bin/kind # Windowschoco install kind # if using chocolatey # Verifykind version# Output: kind v0.20.0 go1.21.0Step 2: Install kubectl
# macOSbrew install kubectl # Linuxcurl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"chmod +x kubectlsudo mv kubectl /usr/local/bin/ # Verifykubectl version --clientStep 3: Create a kind Cluster
# Create cluster named "cleanstart-test"kind create cluster --name cleanstart-test # Verify cluster is runningkubectl get nodes# Output: KIND-CONTROL-PLANE Ready master 2mStep 4: Load CleanStart Images into kind
kind runs Kubernetes in Docker. Docker images are isolated. You must load images into kind's Docker instance.
# Pull image locally firstdocker pull registry.cleanstart.com/python:3.12-stable # Load into kind clusterkind load docker-image registry.cleanstart.com/python:3.12-stable --name cleanstart-test # Verify image is in kind clusterkubectl run test-python --image=registry.cleanstart.com/python:3.12-stable --image-pull-policy=Neverkubectl get pod test-python# STATUS: Running # Cleanup test podkubectl delete pod test-pythonStep 5: Configure Registry Access for kind
For a more production-like setup, configure kind to pull directly from registry.cleanstart.com:
# Create kind cluster with registry configcat > /tmp/kind-config.yaml <<EOFkind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes:- role: control-plane kubeadmConfigPatches: - | kind: KubeletConfiguration registryMirrors: registry.cleanstart.com: endpoint: - "https://registry.cleanstart.com"containerdConfigPatches:- |- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.cleanstart.com"] endpoint = ["https://registry.cleanstart.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.cleanstart.com".auth] username = "<your_username>" password = "<your_api_token>"EOF kind create cluster --name cleanstart-registry --config /tmp/kind-config.yamlStep 6: Deploy a Test Application
# test-app-deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: test-appspec: replicas: 2 selector: matchLabels: app: test-app template: metadata: labels: app: test-app spec: securityContext: runAsNonRoot: true runAsUser: 65532 containers: - name: app image: registry.cleanstart.com/python:3.12-stable imagePullPolicy: Never # Use local image loaded with kind load ports: - containerPort: 8000 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: tmp mountPath: /tmp - name: run mountPath: /run livenessProbe: exec: command: - /usr/bin/python3 - -c - "import sys; sys.exit(0)" initialDelaySeconds: 5 periodSeconds: 10 volumes: - name: tmp emptyDir: {} - name: run emptyDir: {}---apiVersion: v1kind: Servicemetadata: name: test-appspec: type: LoadBalancer ports: - port: 8000 targetPort: 8000 selector: app: test-appDeploy:
kubectl apply -f test-app-deployment.yaml # Check deploymentkubectl get deployment test-app# STATUS: 2/2 ready # Check podskubectl get pods# Both pods should be Running # View logskubectl logs -l app=test-app # Port forward to testkubectl port-forward svc/test-app 8000:8000# In another terminal: curl http://localhost:8000 # Cleanupkubectl delete -f test-app-deployment.yamlVerification Checklist
[ ] kind cluster created successfully [ ] kubectl can connect to cluster [ ] CleanStart images loaded into kind [ ] Can deploy pods using CleanStart images [ ] Security context properly enforced (non-root, read-only FS) [ ] Port forwarding works. Troubleshooting kind:
Problem | Solution |
|---|---|
"could not connect to kind cluster" | Cluster may be down: |
"image not found in kind" | Load image: |
"ImagePullBackOff" | Check image exists in kind, set |
"out of memory" | Reduce kind cluster size or increase Docker memory |
Clean up kind:
kind delete cluster --name cleanstart-test# Removes all data and containerOption 3: Local Kubernetes with minikube (20 minutes)
Alternative to kind. Some teams prefer minikube.
Step 1: Install minikube
# macOSbrew install minikube # Linuxcurl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64chmod +x minikube-linux-amd64sudo mv minikube-linux-amd64 /usr/local/bin/minikube # Windowschoco install minikube # Verifyminikube versionStep 2: Start minikube Cluster
# Start default clusterminikube start --cpus=4 --memory=4096 # Verifykubectl get nodes# Output: minikube Ready master/worker # Check minikube statusminikube statusStep 3: Configure Registry Authentication
# Create secret for registry.cleanstart.comkubectl create secret docker-registry cleanstart-secret \ --docker-server=registry.cleanstart.com \ --docker-username=<your_username> \ --docker-password=<your_api_token> \ --docker-email=<your_email> # Verifykubectl get secrets# Should show: cleanstart-secret docker-registryStep 4: Configure Minikube's Docker Daemon
minikube has its own Docker daemon. You can either:
Option A: Pull images via minikube (easier)
# Use minikube's Dockereval $(minikube docker-env) # Now 'docker pull' uses minikube's Dockerdocker pull registry.cleanstart.com/python:3.12-stable # Deploy without imagePullPolicy overrideOption B: Use ImagePullSecrets in pods
apiVersion: apps/v1kind: Deploymentmetadata: name: test-appspec: template: spec: imagePullSecrets: - name: cleanstart-secret containers: - name: app image: registry.cleanstart.com/python:3.12-stableStep 5: Deploy and Test
Same as kind (Option 2, Step 6). Only difference is which cluster is running.
Option 4: Cloud Staging Cluster (30-60 minutes)
Best for: Realistic infrastructure, team testing, pre-production validation.
Subheading: AWS EKS
Prerequisites: AWS account with permissions to create EKS clusters aws-cli installed and configured eksctl installed (EKS control tool). Create cluster:
# Simple cluster (creates security groups, VPC, etc.)eksctl create cluster \ --name cleanstart-staging \ --region us-east-1 \ --node-type t3.medium \ --nodes 3 # Wait 15-20 minutes for cluster creation # Verifykubectl get nodes# Should show 3 nodes readyConfigure ECR (AWS's registry):
# Create registry (if needed)aws ecr create-repository \ --repository-name cleanstart/python \ --region us-east-1 # Tag CleanStart image for ECRdocker tag registry.cleanstart.com/python:3.12-stable \ 123456789.dkr.ecr.us-east-1.amazonaws.com/cleanstart/python:3.12-stable # Push to ECRaws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.comdocker push 123456789.dkr.ecr.us-east-1.amazonaws.com/cleanstart/python:3.12-stableDeploy:
# Same as kind/minikube, but use ECR image URLapiVersion: apps/v1kind: Deploymentmetadata: name: test-appspec: template: spec: containers: - name: app image: 123456789.dkr.ecr.us-east-1.amazonaws.com/cleanstart/python:3.12-stableSubheading: Google Cloud GKE
Prerequisites: Google Cloud account gcloud CLI installed and configured kubectl installed. Create cluster:
# Create GKE clustergcloud container clusters create cleanstart-staging \ --zone us-central1-a \ --num-nodes 3 \ --machine-type n1-standard-2 # Configure kubectlgcloud container clusters get-credentials cleanstart-staging --zone us-central1-a # Verifykubectl get nodesConfigure Artifact Registry:
# Create repositorygcloud artifacts repositories create cleanstart \ --location us-central1 \ --repository-format docker # Tag imagedocker tag registry.cleanstart.com/python:3.12-stable \ us-central1-docker.pkg.dev/PROJECT-ID/cleanstart/python:3.12-stable # Pushgcloud auth configure-docker us-central1-docker.pkg.devdocker push us-central1-docker.pkg.dev/PROJECT-ID/cleanstart/python:3.12-stableSubheading: Microsoft Azure AKS
Prerequisites: Azure account az CLI installed and configured kubectl installed. Create cluster:
# Create resource groupaz group create --name cleanstart-rg --location eastus # Create AKS clusteraz aks create \ --resource-group cleanstart-rg \ --name cleanstart-staging \ --node-count 3 \ --vm-set-type VirtualMachineScaleSets # Configure kubectlaz aks get-credentials \ --resource-group cleanstart-rg \ --name cleanstart-staging # Verifykubectl get nodesCloud Cluster: Verification Checklist
[ ] Cluster created and nodes are ready [ ] kubectl connected to cluster [ ] Container registry (ECR/Artifact Registry/ACR) created [ ] CleanStart image pushed to registry [ ] Network policies allow pod-to-pod communication [ ] Can deploy pods successfully [ ] Can access pods via port-forward or load balancer. Cleanup cloud resources (to avoid costs):
# EKSeksctl delete cluster --name cleanstart-staging # GKEgcloud container clusters delete cleanstart-staging --zone us-central1-a # AKSaz aks delete --resource-group cleanstart-rg --name cleanstart-staging --yesTest Fixtures (Sample Applications)
Ready-to-use configurations for common stacks:
Fixture 1: Python + PostgreSQL + Redis
# fixtures/python-web-stack.yamlversion: '3.8'services: app: image: registry.cleanstart.com/python:3.12-stable build: context: . dockerfile: Dockerfile.python environment: DATABASE_URL: postgresql://user:password@postgres:5432/testdb REDIS_URL: redis://redis:6379/0 depends_on: - postgres - redis postgres: image: postgres:15-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: testdb redis: image: redis:7-alpineFixture 2: Node.js + MongoDB
# fixtures/node-mongodb-stack.yamlversion: '3.8'services: app: image: registry.cleanstart.com/node:20-stable build: context: . dockerfile: Dockerfile.node environment: MONGODB_URL: mongodb://mongo:27017/testdb depends_on: - mongo mongo: image: mongo:6 environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: passwordFixture 3: Java + MySQL + Kafka
# fixtures/java-kafka-stack.yamlversion: '3.8'services: app: image: registry.cleanstart.com/java:21-stable build: context: . dockerfile: Dockerfile.java environment: DATABASE_URL: jdbc:mysql://mysql:3306/testdb KAFKA_BROKERS: kafka:9092 depends_on: - mysql - kafka mysql: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: testdb kafka: image: confluentinc/cp-kafka:7.5.0 environment: KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 zookeeper: image: confluentinc/cp-zookeeper:7.5.0 environment: ZOOKEEPER_CLIENT_PORT: 2181Sample Applications
Sample 1: Simple Python Flask App
# Dockerfile.pythonFROM registry.cleanstart.com/python:3.12-stable WORKDIR /appCOPY requirements.txt .RUN pip install -r requirements.txt COPY . . EXPOSE 8000CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]# app.pyfrom flask import Flask, jsonify app = Flask(__name__) @app.route('/health')def health(): return jsonify({'status': 'ok'}) @app.route('/api/test')def test(): return jsonify({'message': 'CleanStart works!'}) if __name__ == '__main__': app.run(debug=False)# requirements.txtFlask==3.0.0Gunicorn==21.2.0Requests==2.31.0Sample 2: Simple Node.js Express App
# Dockerfile.nodeFROM registry.cleanstart.com/node:20-stable WORKDIR /appCOPY package*.json ./RUN npm ci --only=production COPY . . EXPOSE 8000CMD ["node", "app.js"]// app.jsconst express = require('express');const app = express(); app.get('/health', (req, res) => { res.json({ status: 'ok' });}); app.get('/api/test', (req, res) => { res.json({ message: 'CleanStart works!' });}); app.listen(8000, () => { console.log('Server running on :8000');});// package.json{ "name": "cleanstart-test", "version": "1.0.0", "main": "app.js", "dependencies": { "express": "^4.18.2" }}Complete Setup Script
For fastest setup, use this script (works for Docker Desktop and local K8s):
#!/bin/bash# setup-cleanstart-test-env.sh set -e echo "╔════════════════════════════════════════════╗"echo "║ CleanStart Test Environment Setup ║"echo "╚════════════════════════════════════════════╝"echo "" # Ask user for environment typeecho "Choose environment:"echo "1) Docker Desktop (local, fast)"echo "2) kind (local K8s)"echo "3) minikube (local K8s)"read -p "Enter choice (1-3): " choice # Verify prerequisitesecho ""echo "Checking prerequisites..."which docker > /dev/null || { echo "❌ Docker not found"; exit 1; }which git > /dev/null || { echo "❌ Git not found"; exit 1; }echo "✓ Docker and Git found" # Create test directoryTEST_DIR="$HOME/cleanstart-test"mkdir -p "$TEST_DIR"cd "$TEST_DIR" # Clone sample apps (if not already cloned)if [ ! -d "sample-apps" ]; then echo "" echo "Cloning sample applications..." git clone https://github.com/cleanstart/sample-apps.git || truefi # Docker Desktop optionif [ "$choice" = "1" ]; then echo "" echo "Setting up Docker Desktop environment..." echo "Pulling CleanStart images..." docker pull registry.cleanstart.com/python:3.12-stable docker pull registry.cleanstart.com/node:20-stable echo "Creating docker-compose.yml..." cp sample-apps/docker-compose.yml . echo "" echo "✓ Setup complete!" echo "" echo "Next steps:" echo " docker-compose up --build" echo " curl http://localhost:8000/health" # kind optionelif [ "$choice" = "2" ]; then echo "" echo "Setting up kind Kubernetes cluster..." which kind > /dev/null || { echo "❌ kind not found. Install: brew install kind"; exit 1; } which kubectl > /dev/null || { echo "❌ kubectl not found. Install: brew install kubectl"; exit 1; } kind create cluster --name cleanstart-test || true echo "Loading CleanStart images into kind..." docker pull registry.cleanstart.com/python:3.12-stable kind load docker-image registry.cleanstart.com/python:3.12-stable --name cleanstart-test echo "Creating test deployment..." kubectl apply -f sample-apps/k8s/deployment.yaml echo "" echo "✓ Setup complete!" echo "" echo "Next steps:" echo " kubectl get pods" echo " kubectl port-forward svc/test-app 8000:8000" # minikube optionelif [ "$choice" = "3" ]; then echo "" echo "Setting up minikube Kubernetes cluster..." which minikube > /dev/null || { echo "❌ minikube not found. Install: brew install minikube"; exit 1; } which kubectl > /dev/null || { echo "❌ kubectl not found. Install: brew install kubectl"; exit 1; } minikube start --cpus=4 --memory=4096 || true echo "Creating registry secret..." read -p "Enter CleanStart username: " username read -sp "Enter CleanStart API token: " token echo "" kubectl create secret docker-registry cleanstart-secret \ --docker-server=registry.cleanstart.com \ --docker-username="$username" \ --docker-password="$token" \ --docker-email="$username@example.com" || true echo "Creating test deployment..." kubectl apply -f sample-apps/k8s/deployment-with-imagepullsecret.yaml echo "" echo "✓ Setup complete!" echo "" echo "Next steps:" echo " kubectl get pods" echo " kubectl port-forward svc/test-app 8000:8000"fi echo ""echo "Documentation:"echo " - Security Testing: docs/security-testing-playbook.md"echo " - Compatibility: docs/compatibility-testing-matrix.md"Run:
chmod +x setup-cleanstart-test-env.sh./setup-cleanstart-test-env.shVerification Checklist (All Options)
After setup, verify your environment:
# 1. Can pull imagesdocker pull registry.cleanstart.com/python:3.12-stable# Should complete without errors # 2. Can run containers (Docker Desktop)docker run --rm registry.cleanstart.com/python:3.12-stable python -c "print('works')"# Should output: works # 3. Can deploy to Kubernetes (if using kind/minikube)kubectl create deployment test --image=registry.cleanstart.com/python:3.12-stablekubectl wait --for=condition=available --timeout=60s deployment/test# Should deploy successfully # 4. Can verify security propertiesdocker run --rm registry.cleanstart.com/python:3.12-stable id# Should output uid=65532 (non-root)Troubleshooting Common Setup Issues
Problem | Symptom | Solution |
|---|---|---|
Registry auth fails | "denied: unauthorized" | Run |
Image not found | "manifest not found" | Check image name (case-sensitive), verify registry URL |
No internet | "name resolution failed" | Check network connectivity, proxy settings |
Port conflicts | "port already in use" | Change port in docker-compose or kill existing process |
Out of memory | "OOMKilled" or crashes | Increase Docker/minikube memory allocation |
k8s cluster won't start | kind/minikube hangs | Docker Desktop may need restart |
Can't connect to Docker | "cannot connect to daemon" | Start Docker Desktop, wait 30 seconds |
Images too large | "disk space" | Delete unused images: |
Next Steps
- Run security tests: See Security Testing Playbook
- Test your app: Copy your app into a sample fixture, test locally
- Test in Kubernetes: Deploy to kind/minikube to verify K8s behavior
- Move to staging: If using cloud option, test with real infrastructure
- Automate testing: Create CI pipeline that uses these environments
What to Read Next
Security Testing: Security Testing Playbook — verify security properties Compatibility: Compatibility Testing Matrix — ensure your frameworks work Kubernetes Operations: Kubernetes-Helm Operations — scale testing to teams.
