How to Fix Docker Container Stuck in 'Created' Status with tail -f /dev/null Entrypoint
Threat/Impact Level: MEDIUM | Downtime Risk: HIGH | Time to Fix: 5–15 mins
TL;DR
- What broke: The container was created by the daemon but never transitioned to
running— the runtime hit a pre-start failure (bad volume mount, missing network, OOM kill at fork, or a corrupted image layer) before the entrypoint process could be exec'd. - How to fix it: Run
docker inspect <container_id>anddocker logs <container_id>immediately. TheState.Errorfield and OOMKilled flag will pinpoint the exact failure class. Fix the offending resource binding or image, thendocker rmthe dead container and re-run. - Sandbox: Use our Client-Side Sandbox below to auto-refactor your
docker-compose.ymlor Dockerfile — paste it in and get a corrected config without sending your secrets anywhere.
The Incident
You run docker ps -a and see this:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f9c1d82b44 myapp:latest "tail -f /dev/null" 3 minutes ago Created myapp_debug
Created is not a running state. It means the Docker daemon allocated the container filesystem and metadata but the runtime never called execve() on the entrypoint. The process does not exist. No PID. docker exec will fail with Error response from daemon: Container is not running.
This is a silent failure. No crash loop. No restart. Just a dead namespace sitting on disk.
The Attack Vector / Blast Radius
tail -f /dev/null is a placeholder entrypoint used to keep a container alive for manual exec access — common in debug sidecars, init containers, and CI job runners. The danger here is operational, not CVE-based, but the blast radius is real:
- CI/CD pipeline deadlock. A GitHub Actions or GitLab runner waiting on this container to become
healthywill hang until the job timeout, burning minutes or hours of runner quota. - Orchestrator confusion in Compose/Swarm.
depends_onwithcondition: service_healthywill block dependent services indefinitely. Your entire stack fails to start with zero obvious error surfaced at the compose level. - Volume lock contention. The created-but-never-started container holds a reference to its volume mounts. On some storage drivers (overlay2 with SELinux, NFS-backed volumes), this prevents other containers from acquiring a write lock on the same volume.
- Resource ghost. The container's network namespace and cgroup are allocated. On a dense host, hundreds of these ghost containers exhaust
net.ipv4.ip_local_port_rangeand cgroup PIDs limits without a single process running.
How to Fix It
Step 1: Get the actual error
# This is the only command that matters first
docker inspect <container_id> --format='{{json .State}}' | jq .
Look for:
"Error": "<message>"— direct cause"OOMKilled": true— host memory exhausted at fork time"ExitCode": 128+— signal-based kill before start
# Also pull daemon-level events
docker events --filter container=<container_id> --since 10m
Step 2: Common root causes and fixes
Cause A: Named volume or bind mount does not exist
# docker-compose.yml
services:
debug:
image: myapp:latest
- volumes:
- - /host/path/that/doesnt/exist:/app/data
+ volumes:
+ - ./data:/app/data # ensure host path exists: mkdir -p ./data
Cause B: Network referenced does not exist
# docker-compose.yml
services:
debug:
image: myapp:latest
- networks:
- - backend_net # network never declared or pre-created
+ networks:
+ - backend_net
+
+networks:
+ backend_net:
+ driver: bridge
Cause C: Image entrypoint override syntax error
# Dockerfile
- ENTRYPOINT tail -f /dev/null
+ ENTRYPOINT ["tail", "-f", "/dev/null"]
# Shell form does not receive signals correctly and can fail on minimal images
# (e.g., distroless, scratch) that have no /bin/sh
Cause D: Resource constraints killing the container at fork
# docker-compose.yml
services:
debug:
image: myapp:latest
deploy:
resources:
limits:
- memory: 4m # too low — cgroup OOM fires before tail starts
+ memory: 64m
Basic Fix (one-liner cleanup and restart)
docker rm a3f9c1d82b44
docker inspect myapp:latest --format='{{.Config.Entrypoint}}'
# Verify the image has /usr/bin/tail available
docker run --rm myapp:latest which tail
docker-compose up -d debug
Enterprise Best Practice
Stop using tail -f /dev/null as a keep-alive. It is a footgun. Use a proper init process:
# Dockerfile
- ENTRYPOINT ["tail", "-f", "/dev/null"]
+ RUN apk add --no-cache tini
+ ENTRYPOINT ["/sbin/tini", "--"]
+ CMD ["sleep", "infinity"]
# tini reaps zombie processes, handles SIGTERM correctly,
# and works on distroless/minimal images via multi-stage copy
For debug sidecars in Kubernetes, use ephemeral containers instead of baking a placeholder entrypoint into your image:
kubectl debug -it <pod> --image=busybox:latest --target=<container>
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Hadolint in your pipeline (catches shell-form ENTRYPOINT)
# .github/workflows/lint.yml
- name: Lint Dockerfile
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
failure-threshold: warning
# DL3025 fires on shell-form CMD/ENTRYPOINT
2. Checkov for Compose resource constraint validation
checkov -f docker-compose.yml --check CKV_DOCKER_7,CKV_DOCKER_8
3. Healthcheck with timeout — fail fast, don't hang
# docker-compose.yml
services:
debug:
image: myapp:latest
healthcheck:
test: ["CMD", "pgrep", "tail"]
interval: 5s
timeout: 3s
retries: 2
start_period: 5s
# Container moves to 'unhealthy' in 16s instead of hanging forever
4. OPA/Conftest policy — block placeholder entrypoints in production images
# policy/docker.rego
package docker
deny[msg] {
input.Entrypoint[_] == "tail"
msg := "Placeholder entrypoint 'tail' is not permitted in production images. Use tini or a real process."
}
conftest test docker-inspect-output.json --policy policy/
This gates your registry push and prevents the pattern from reaching production at all.