- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes Init Containers: Initialisierung und Setup vor dem Haupt-Container
TL;DR
- Init Containers laufen vor dem Haupt-Container, sequenziell und muessen erfolgreich beenden, bevor der naechste startet.
- Typische Einsatzfelder: Warten auf Datenbank-Verfuegbarkeit, Konfig-Dateien herunterladen, Permissions setzen, Schema-Migrationen.
- Jeder Init Container hat eigene Resource Requests und Limits -- der hoechste Wert zaehlt fuer das Scheduling.
- Debugging mit
kubectl logs <pod> -c <init-container-name>undkubectl describe pod. - Seit Kubernetes 1.28 gibt es native Sidecar Containers (
restartPolicy: Alwaysin initContainers), die neben dem Haupt-Container weiterlaufen.
Was sind Init Containers?
Init Containers sind spezielle Container in einem Pod, die vor den eigentlichen Anwendungs-Containern starten. Sie erfuellen Setup-Aufgaben, die abgeschlossen sein muessen, bevor die Anwendung startet.
Die Regeln sind klar:
- Init Containers laufen sequenziell -- einer nach dem anderen, nicht parallel.
- Jeder Init Container muss mit Exit Code 0 beenden, bevor der naechste startet.
- Wenn ein Init Container fehlschlaegt, startet Kubernetes den gesamten Pod neu (je nach
restartPolicy). - Init Containers unterstuetzen keine Liveness-, Readiness- oder Startup-Probes.
- Init Containers koennen andere Images verwenden als der Haupt-Container.
Pod-Start:
Init Container 1 */} Init Container 2 */} App Container
(muss erfolgreich) (muss erfolgreich) (laeuft dauerhaft)
Der letzte Punkt ist besonders nuetzlich: Sie koennen ein minimales Alpine-Image fuer Setup-Tasks verwenden, waehrend der Haupt-Container ein schlankes Distroless-Image nutzt.
Praxis-Beispiel 1: Warten auf Datenbank
Das haeufigste Pattern: Der Anwendungs-Container braucht eine Datenbankverbindung, aber die Datenbank ist noch nicht bereit.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
initContainers:
- name: wait-for-postgres
image: busybox:1.36
command:
- sh
- -c
- |
echo "Warte auf PostgreSQL..."
until nc -z postgresql.database.svc.cluster.local 5432; do
echo "PostgreSQL nicht erreichbar, warte 2 Sekunden..."
sleep 2
done
echo "PostgreSQL ist bereit."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
containers:
- name: api-server
image: registry.internal/api-server:2.5.0
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: "postgresql.database.svc.cluster.local"
- name: DB_PORT
value: "5432"
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Ohne den Init Container wuerde der API-Server starten, die Datenbankverbindung versuchen, fehlschlagen und in einen CrashLoopBackOff geraten. Mit dem Init Container wartet der Pod sauber, bis PostgreSQL erreichbar ist.
Wer CrashLoopBackOff-Probleme systematisch debuggen moechte, findet einen ausfuehrlichen Workflow im Beitrag zu CrashLoopBackOff Troubleshooting.
Praxis-Beispiel 2: Warten auf mehrere Services
Manchmal muss die Anwendung auf mehrere Abhaengigkeiten warten -- Datenbank, Cache und Message Queue:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
initContainers:
- name: wait-for-postgres
image: busybox:1.36
command:
- sh
- -c
- |
until nc -z postgresql.database.svc.cluster.local 5432; do
echo "Warte auf PostgreSQL..."
sleep 2
done
echo "PostgreSQL bereit."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
- name: wait-for-redis
image: busybox:1.36
command:
- sh
- -c
- |
until nc -z redis.cache.svc.cluster.local 6379; do
echo "Warte auf Redis..."
sleep 2
done
echo "Redis bereit."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
- name: wait-for-rabbitmq
image: busybox:1.36
command:
- sh
- -c
- |
until nc -z rabbitmq.messaging.svc.cluster.local 5672; do
echo "Warte auf RabbitMQ..."
sleep 2
done
echo "RabbitMQ bereit."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
containers:
- name: order-service
image: registry.internal/order-service:3.1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
Die Init Containers laufen nacheinander: Erst PostgreSQL, dann Redis, dann RabbitMQ. Erst wenn alle drei verfuegbar sind, startet der Order-Service.
Praxis-Beispiel 3: Konfig-Dateien herunterladen
Init Containers koennen Konfigurationsdateien von einem zentralen Config-Service oder S3-Bucket herunterladen und ueber ein Shared Volume dem Haupt-Container bereitstellen:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-with-config
spec:
replicas: 2
selector:
matchLabels:
app: nginx-custom
template:
metadata:
labels:
app: nginx-custom
spec:
initContainers:
- name: download-config
image: curlimages/curl:8.5.0
command:
- sh
- -c
- |
echo "Lade Nginx-Konfiguration herunter..."
curl -sSL https://config.internal.corp/nginx/default.conf \
-o /config/default.conf
curl -sSL https://config.internal.corp/nginx/ssl.conf \
-o /config/ssl.conf
echo "Konfiguration geladen."
ls -la /config/
volumeMounts:
- name: nginx-config
mountPath: /config
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
- containerPort: 443
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
readOnly: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
volumes:
- name: nginx-config
emptyDir: {}
Das emptyDir-Volume wird von beiden Containern geteilt. Der Init Container schreibt, der Haupt-Container liest.
Praxis-Beispiel 4: Permissions und Verzeichnisse vorbereiten
Manche Anwendungen laufen als Non-Root-User und brauchen bestimmte Verzeichnisse mit korrekten Permissions:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch
spec:
serviceName: elasticsearch
replicas: 3
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
initContainers:
- name: fix-permissions
image: busybox:1.36
command:
- sh
- -c
- |
chown -R 1000:1000 /usr/share/elasticsearch/data
chmod 750 /usr/share/elasticsearch/data
securityContext:
runAsUser: 0
privileged: false
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
- name: increase-vm-max-map
image: busybox:1.36
command:
- sysctl
- -w
- vm.max_map_count=262144
securityContext:
privileged: true
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
ports:
- containerPort: 9200
- containerPort: 9300
securityContext:
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
Zwei Init Containers: Der erste setzt die Verzeichnis-Permissions, der zweite erhoeht den vm.max_map_count Kernel-Parameter, den Elasticsearch benoetigt. Beide muessen als Root laufen, waehrend Elasticsearch selbst als User 1000 laeuft.
Praxis-Beispiel 5: Datenbank-Migrationen
Schema-Migrationen vor dem Anwendungsstart sind ein klassischer Init-Container-Einsatz:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
initContainers:
- name: run-migrations
image: registry.internal/web-app:2.5.0
command:
- python
- manage.py
- migrate
---noinput
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: web-app-secrets
key: database-url
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
containers:
- name: web-app
image: registry.internal/web-app:2.5.0
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: web-app-secrets
key: database-url
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
Achtung bei Replicas: Bei mehreren Replicas laufen die Migrationen in jedem Pod. Die meisten Migrations-Frameworks (Django, Rails, Flyway) sind idempotent und verwenden Locking, sodass das kein Problem ist. Pruefen Sie aber, ob Ihr Framework das unterstuetzt.
Resource Requests fuer Init Containers
Init Containers haben eigene Resource Requests und Limits. Der Scheduler berechnet die Pod-Ressourcen wie folgt:
Effektive CPU Requests = max(
hoechster Init Container CPU Request,
Summe aller App Container CPU Requests
)
Ein Beispiel:
initContainers:
- name: init-1
resources:
requests:
cpu: 100m # Init 1
memory: 128Mi
- name: init-2
resources:
requests:
cpu: 500m # Init 2 (hoechster Init)
memory: 256Mi
containers:
- name: app
resources:
requests:
cpu: 250m # App Container
memory: 512Mi
Effektive Requests fuer Scheduling:
- CPU:
max(500m, 250m)= 500m - Memory:
max(256Mi, 512Mi)= 512Mi
Das bedeutet: Ein Init Container mit hohen Resource Requests kann dazu fuehren, dass der Pod auf einem groesseren Node gescheduled wird, obwohl der Haupt-Container weniger braucht. Halten Sie Init Container daher so schlank wie moeglich.
Mehr Details zum Resource Management finden Sie im Beitrag zu Kubernetes Resource Management.
Init Containers debuggen
Wenn ein Pod im Status Init:0/2 oder Init:CrashLoopBackOff haengt, helfen diese Befehle:
# Status aller Container (Init + App) anzeigen
kubectl describe pod my-pod
# Beispiel-Output:
# Init Containers:
# wait-for-postgres:
# State: Running
# Started: Wed, 10 Feb 2026 10:15:00 +0100
# run-migrations:
# State: Waiting
# Reason: PodInitializing
# Logs eines bestimmten Init Containers
kubectl logs my-pod -c wait-for-postgres
# Logs des vorherigen (gecrashteten) Init Containers
kubectl logs my-pod -c run-migrations --previous
# Events fuer den Pod
kubectl get events --field-selector involvedObject.name=my-pod --sort-by='.lastTimestamp'
Init Container haengt endlos
Wenn ein Init Container nie beendet (z.B. weil der Service nie erreichbar wird), bleibt der Pod im Status Init:0/1. Setzen Sie immer ein Timeout:
initContainers:
- name: wait-for-service
image: busybox:1.36
command:
- sh
- -c
- |
TIMEOUT=120
ELAPSED=0
until nc -z my-service.default.svc.cluster.local 8080; do
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "FEHLER: Timeout nach ${TIMEOUT}s. Service nicht erreichbar."
exit 1
fi
echo "Warte... (${ELAPSED}s/${TIMEOUT}s)"
sleep 2
ELAPSED=$((ELAPSED + 2))
done
echo "Service erreichbar."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
Fuer allgemeines Kubernetes-Troubleshooting empfehle ich den Beitrag zu Kubernetes Health Checks und Probes.
Anti-Patterns: Was Sie vermeiden sollten
Zu viele Init Containers
Jeder Init Container verlaengert die Pod-Startzeit. Fuenf Init Containers, die jeweils 10 Sekunden brauchen, ergeben 50 Sekunden Verzoegerung -- bei jedem Pod-Start, bei jedem Rolling Update.
# Schlecht: 5 separate Wait-Container
initContainers:
- name: wait-for-postgres # 5s
- name: wait-for-redis # 3s
- name: wait-for-rabbitmq # 4s
- name: wait-for-config # 8s
- name: wait-for-vault # 6s
# Gesamt: 26s Minimum vor dem App-Start
Besser: Kombinieren Sie mehrere Checks in einem Init Container:
initContainers:
- name: wait-for-dependencies
image: busybox:1.36
command:
- sh
- -c
- |
echo "Pruefe Abhaengigkeiten..."
until nc -z postgresql.database.svc.cluster.local 5432; do sleep 1; done
echo "PostgreSQL OK"
until nc -z redis.cache.svc.cluster.local 6379; do sleep 1; done
echo "Redis OK"
until nc -z rabbitmq.messaging.svc.cluster.local 5672; do sleep 1; done
echo "RabbitMQ OK"
echo "Alle Abhaengigkeiten bereit."
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
Lang laufende Init Containers
Init Containers sind fuer kurze Setup-Tasks gedacht. Wenn ein Init Container Minuten braucht (z.B. ein grosser Datenbank-Dump), ueberdenken Sie Ihre Architektur. Moegliche Alternativen:
- Kubernetes Jobs fuer einmalige Batch-Tasks
- Persistent Volumes fuer Daten, die nicht bei jedem Start neu geladen werden muessen
- Readiness Gates fuer komplexe Startup-Checks
Init Container als Sidecar missbrauchen
Ein Init Container beendet sich und laeuft nicht weiter. Wenn Sie einen dauerhaft laufenden Helfer-Prozess brauchen (z.B. Log-Forwarding, Config-Sync), nutzen Sie einen Sidecar-Container oder seit Kubernetes 1.28 native Sidecar Containers.
Native Sidecar Containers ab Kubernetes 1.28
Seit Kubernetes 1.28 gibt es native Sidecar Containers. Diese werden als Init Containers definiert, aber mit restartPolicy: Always:
apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
spec:
initContainers:
# Normaler Init Container -- laeuft einmal und beendet sich
- name: setup
image: busybox:1.36
command:
- sh
- -c
- echo "Setup abgeschlossen"
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
# Nativer Sidecar -- laeuft dauerhaft neben dem Haupt-Container
- name: log-forwarder
image: fluent/fluent-bit:2.2
restartPolicy: Always
volumeMounts:
- name: app-logs
mountPath: /var/log/app
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
containers:
- name: app
image: registry.internal/my-app:1.0.0
ports:
- containerPort: 8080
volumeMounts:
- name: app-logs
mountPath: /var/log/app
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: app-logs
emptyDir: {}
Die Vorteile nativer Sidecars gegenueber regulaeren Containern:
- Definierte Startreihenfolge: Der Sidecar startet vor dem Haupt-Container (wie ein Init Container).
- Definiertes Shutdown: Der Sidecar wird nach dem Haupt-Container gestoppt.
- Kein Race Condition: Der Haupt-Container startet erst, wenn der Sidecar bereit ist.
Wer mehr ueber das Sidecar Pattern und weitere Multi-Container-Patterns erfahren moechte, findet detaillierte Beispiele in unseren Beitraegen zum Kubernetes Adapter Pattern und zum Kubernetes Ambassador Pattern.
Init Containers in der Praxis: Checkliste
Bevor Sie Init Containers in Ihre Deployments einbauen, pruefen Sie folgende Punkte:
# 1. Brauche ich wirklich einen Init Container?
# Oder reicht ein Startup Probe / Readiness Probe?
# 2. Hat der Init Container ein Timeout?
# Endlos-Loops ohne Timeout sind gefaehrlich.
# 3. Sind Resource Requests gesetzt?
# Init Containers ohne Requests koennten OOMKilled werden.
# 4. Ist das Image schlank?
# busybox (1.4 MB) statt ubuntu (72 MB).
# 5. Logge ich genug fuer Debugging?
# echo-Statements helfen bei der Fehlersuche.
# 6. Wie wirkt sich die Startzeit auf Rolling Updates aus?
# Kalkulieren Sie die Init-Container-Zeit in die Update-Strategie ein.
Zusammenfassung
Init Containers sind ein essentielles Kubernetes-Feature fuer zuverlaessige Anwendungsstarts. Sie stellen sicher, dass Abhaengigkeiten verfuegbar sind, Konfigurationen geladen und Permissions gesetzt sind, bevor der Haupt-Container startet. Mit den nativen Sidecar Containers seit Kubernetes 1.28 schliessen Init Containers auch die Luecke zu dauerhaft laufenden Helfer-Prozessen.
Halten Sie Init Containers schlank, setzen Sie immer Timeouts und Resource Requests, und kombinieren Sie verwandte Checks in einem einzigen Init Container, um die Pod-Startzeit nicht unnoetig zu verlaengern.
Verwandte Artikel
- CrashLoopBackOff Troubleshooting Guide
- Kubernetes Health Checks und Probes
- Kubernetes Resource Management
- Kubernetes Adapter Pattern
- Kubernetes Ambassador Pattern
Wenn Sie Unterstuetzung bei der Optimierung Ihrer Kubernetes-Deployments brauchen, sprechen Sie uns an unter /kontakt.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
ArgoCD ApplicationSets: Multi-App Deployment Patterns
ArgoCD ApplicationSets automatisieren Multi-App und Multi-Cluster Deployments mit Generatoren. Praxis-Patterns für Git, Cluster und Matrix.
ConfigMaps richtig nutzen: Kubernetes Best Practices
ConfigMaps in Kubernetes erstellen, mounten und aktualisieren: Praktische Best Practices für Volumes, Env-Vars und Hot-Reload mit Reloader.
Java Spring Boot auf Kubernetes containerisieren
Spring Boot Anwendungen für Kubernetes containerisieren: Multi-Stage Dockerfile, JVM-Tuning, Health Checks mit Actuator und fertige Deployment-YAMLs.
.NET-Anwendungen auf Kubernetes containerisieren
.NET- und ASP.NET-Core-Anwendungen für Kubernetes containerisieren: Multi-Stage Dockerfile, Health Checks, Kestrel-Konfiguration und komplette Manifeste.
GitHub Actions: CI/CD Pipeline für Kubernetes
Eine vollständige CI/CD-Pipeline mit GitHub Actions für Kubernetes aufsetzen. Von Image-Build über Kustomize-Deployment bis zu Approval Gates.