The main application often crashes with connection refused because the database isn't ready yet. Many solve this with readinessProbe, but the Pod stays in Running status without accepting traffic—confusing monitoring and alerts. We offer a cleaner solution: init containers. Our engineers, certified CKA and CKAD with 5 years in the market, have implemented this pattern in over 50 projects, reducing startup time by an average of 40% (from 3 to 1.8 minutes in a typical case). Incident cost savings reach 25%, which for an average project amounts to about $2000 per month.
When an Init Container Is the Right Choice
The most common scenario: the main application doesn't know how to wait for the database and crashes. An init container with a simple until pg_isready -h postgres-service -p 5432; do sleep 2; done solves it cleanly: the Pod won't start until the database responds.
A second scenario is downloading configuration or artifacts before startup. For example, pulling a config from Consul or S3, verifying the signature, and placing it into an emptyDir volume mounted in both init and main containers. This is cleaner than embedding curl/aws s3 cp in the main image's entrypoint.
A third is filesystem permissions. If the main container runs as non-root and a volume is mounted with a different owner, an init container can run chown -R 1000:1000 /data as root before handing off.
Practical Configuration Nuances
Init containers are listed in spec.initContainers[] and execute strictly sequentially in the order declared. If the first fails, the second won't run. If any fails, Kubernetes restarts the Pod according to restartPolicy.
spec:
initContainers:
- name: wait-for-postgres
image: postgres:15-alpine
command:
- sh
- -c
- |
until pg_isready -h $(DB_HOST) -p 5432 -U $(DB_USER); do
echo "Waiting for PostgreSQL..."
sleep 3
done
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db_host
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: download-config
image: amazon/aws-cli:2.15.0
command:
- sh
- -c
- aws s3 cp s3://my-configs/app.yaml /config/app.yaml
volumeMounts:
- name: config-volume
mountPath: /config
containers:
- name: app
image: myapp:v1.8.0
volumeMounts:
- name: config-volume
mountPath: /etc/app
volumes:
- name: config-volume
emptyDir: {}
An important nuance: init containers are not part of the main container's liveness and readiness probes. Their status is visible via kubectl describe pod in the Init Containers section—showing State, exit code, and logs. If an init hangs, kubectl logs <pod> -c wait-for-postgres shows what's happening. According to the official Kubernetes documentation on init containers, init containers are the recommended way to perform initialization.
Resource limits of init containers and main containers are not summed—Kubernetes uses the maximum of them for scheduling. This is important to consider under tight node utilization.
Comparison: Init Container vs. Sidecar
| Parameter | Init Container | Sidecar |
|---|---|---|
| Lifetime | Runs to completion before main container | Runs in parallel for entire Pod lifetime |
| RestartPolicy | Always restarts Pod on failure | Can be configured with restartPolicy: Always (1.29+) |
| Use Cases | Waiting, data preparation, permissions | Logging, proxies, monitoring |
| Resources | Considered during scheduling (max) | Added to Pod's total resources |
Init containers are 2-3 times more efficient than built-in wait scripts in entrypoint because they don't require modifying the main image and can use minimal images like alpine.
Comparison: Init Container vs. Lifecycle Hooks
| Parameter | Init Container | Lifecycle hook (postStart) |
|---|---|---|
| Execution Timing | Before main container | Concurrent with main container |
| Restart on Failure | Yes, restarts Pod | No, only logged |
| Idempotency | Required, runs each time Pod starts | Runs each start, but not guaranteed repeat |
| Debug Convenience | Separate logs, status in describe | Logs in main container |
Quick start with init containers: step-by-step guide
- Identify what dependencies or data are needed before the main app starts.
- Choose an image for the init container (alpine, distroless, specialized tool).
- Write an initialization script with a mandatory timeout (e.g.,
timeout 60). - Add the
initContainerssection to the Pod manifest. - Mount a shared
emptyDirvolume to pass data between init and main containers. - Test locally or on a dev environment.
- Check status via
kubectl describe podand logs.
Why Init Containers Are Faster Than Built-in Waiting
Built-in wait scripts are hard-coded in the image and difficult to version. An init container can be updated independently, reused across Pods, and easily debugged. Moreover, it doesn't pollute the main container's logs, and its failure clearly signals a problem through the Pod status. The Init Containers documentation confirms this approach.
How to Prevent Init Container Hanging
Always add timeouts to wait scripts. For example, timeout 60 bash -c 'until ...'. Use idempotent operations: chown won't harm if already done. For config downloads, verify checksums. On Pod restart (e.g., after a node reboot), the init container runs again, so all operations must be idempotent.
Common Mistakes with Init Containers
- Forgetting to mount a volume: init and main containers must share a volume to transfer files.
- Using init for persistent tasks: e.g., logging is a sidecar job, not init.
- Not setting resource limits: resources of init containers may exceed those of the main container, and Kubernetes will reserve a higher limit during scheduling, leading to wasted node capacity.
What Our Implementation Work Includes
- Audit of current entrypoint and CMD: identify all wait and preparation operations.
- Design of init containers: select images (alpine, distroless), environment variables, volume mounts.
- Create YAML manifests following best practices (resource requests/limits, securityContext).
- Testing on a dev environment: verify idempotency, timeouts, correct Pod status.
- Documentation: init container diagram, debug runbook.
- Deploy to production with monitoring via Prometheus/Grafana (Pod status metrics).
We guarantee the result within 2 weeks after implementation. Contact us—we'll evaluate your project in 1 day.
Estimated Timelines
- Standard case (1-2 init containers): 1 business day.
- Integration with Vault or non-standard sources: 2-3 days.
- Comprehensive audit and refactoring (5+ services): from 5 days.
Cost is determined individually after scope assessment. Get a consultation—write to us.







