Operations 28 min read

Why Do Docker Containers Fail to Start? Common Causes and Troubleshooting Commands

This guide walks operators through the most frequent reasons Docker containers fail to start—such as image issues, misconfigurations, resource limits, permission problems, network errors, and volume mishaps—and provides a step‑by‑step command checklist to diagnose and fix each scenario.

Raymond Ops
Raymond Ops
Raymond Ops
Why Do Docker Containers Fail to Start? Common Causes and Troubleshooting Commands

Background

Docker container startup failures are a common operational problem. Containers can fail for many reasons, including missing or corrupted images, configuration errors, insufficient resources, network connectivity issues, permission restrictions, health‑check failures, or unavailable dependent services. After a container exits, docker ps without -a does not show it, and logs may be sparse, making troubleshooting harder.

Troubleshooting Process

When a container fails to start, follow these steps in order:

Step 1: Verify container status
    └─ docker ps -a | grep <container-name>

Step 2: View container exit information
    └─ docker logs <container-id>
    └─ docker inspect <container-id>

Step 3: Identify root cause
    ├─ Image problems (missing, corrupted, wrong tag)
    ├─ Configuration problems (port conflict, missing env vars, wrong command)
    ├─ Resource problems (memory, disk space)
    ├─ Permission problems (SELinux/AppArmor, user rights, mount rights)
    ├─ Network problems (DNS, port mapping, overlay network)
    ├─ Dependency problems (service not ready)
    └─ Health‑check failures (restart loops)

Step 4: Fix and verify
    └─ docker run / docker start

Step 1 – Confirm Container Status

Basic Status Check

# List all containers (including exited)
 docker ps -a

# Filter by name
 docker ps -a | grep <container-name>

# Formatted output for clarity
 docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}}"

Typical Status values: Exited (1) 5 minutes ago: exited with code 1 Created: created but not started Up 2 hours: running normally Restarting (1) (starting): repeatedly crashing

Quick Exit‑Code Inspection

# One‑line status, exit code, OOM flag, and command
 docker inspect <container-id> --format='{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.Config.Cmd}}'

# Full state JSON (use jq for parsing)
 docker inspect <container-id> | jq '.State'

Step 2 – View Container Logs

docker logs

# Standard output + error
 docker logs <container-id>

# Follow live output
 docker logs -f <container-id>

# Last 100 lines
 docker logs --tail 100 <container-id>

# Include timestamps
 docker logs --timestamps <container-id>

# Logs since a specific time
 docker logs --since "2024-01-15T10:00:00" <container-id>
 docker logs --since 30m <container-id>

# Filter for errors
 docker logs <container-id> 2>&1 | grep -i error

Why Logs May Be Empty

Application does not write to stdout/stderr : CMD/ENTRYPOINT redirects output to a file.

Wrong log driver : containers may use json-file, syslog, fluentd, awslogs, etc.

Log rotation removed the file .

# Check which log driver is configured
 docker inspect <container-id> --format='{{.HostConfig.LogConfig.Type}}'

# If json‑file, view the raw log file
 cat /var/lib/docker/containers/<container-id>/*-json.log | tail -100

Step 3 – Inspect Container Details

docker inspect

reveals every configuration detail from creation to runtime.

# Full JSON output (very large)
 docker inspect <container-id>

# Extract specific sections with jq
 docker inspect <container-id> --format='{{json .State}}' | jq .
 docker inspect <container-id> --format='{{json .Config}}' | jq .
 docker inspect <container-id> --format='{{json .HostConfig}}' | jq .

# Common fields to display
 docker inspect <container-id> --format='
State: {{.State.Status}}
ExitCode: {{.State.ExitCode}}
OOMKilled: {{.State.OOMKilled}}
Error: {{.State.Error}}
StartedAt: {{.State.StartedAt}}
FinishedAt: {{.State.FinishedAt}}
Path: {{.Path}}
Args: {{.Args}}
WorkingDir: {{.Config.WorkingDir}}
Cmd: {{.Config.Cmd}}
Entrypoint: {{.Config.Entrypoint}}
Env: {{range .Config.Env}}{{.}} {{end}}'

Common Exit Codes

0 : Normal exit – process completed.

1 : General error – application‑level misconfiguration.

125 : Docker daemon error – e.g., memory limit exceeded.

126 : Command not executable – permission or path issue for CMD/ENTRYPOINT.

127 : Command not found – missing binary or PATH problem.

137 : SIGKILL (OOM) – out‑of‑memory kill.

139 : SIGSEGV – segmentation fault.

143 : SIGTERM – graceful stop via docker stop.

Step 4 – Categorized Diagnosis

4.1 Image Problems

Typical error messages:

Error: image nginx:1.24 not found
Layer already exists
docker: Error response from daemon: manifest for xxx not found

Key commands:

# List local images
 docker images

# Inspect image details
 docker inspect nginx:1.24

# Pull the image
 docker pull nginx:1.24

# Verify tag existence
 docker manifest inspect nginx:1.24

# Remove dangling images
 docker image prune -f

Common scenarios:

Tag is latest but not updated : image built but not pushed or retagged.

Wrong registry address : private registry URL typo.

Image deleted : another machine overwrote the tag; local digest is stale.

Cross‑architecture pull : pulling an x86 image on ARM leads to manifest incompatibility.

# Check image architecture
 docker inspect <image> | grep Architecture

# Show digests
 docker images --digests

Fixes:

# Re‑pull the correct image
 docker pull <image>:<tag>

# For private registries
 docker login registry.example.com
 docker pull registry.example.com/my-app:v1.2.3

# If digest changed, roll back to the old digest
 docker images --digests | grep <image>
 docker run --rm <image>@sha256:xxxxdigestxxxx

4.2 Configuration Problems

Port Conflict

docker: Error response from daemon: Ports are not available: bind address port already in use.

Check which process occupies the port:

# Linux
 ss -tlnp | grep :80
 netstat -tlnp | grep :80

# macOS (no ss/netstat)
 lsof -i :80

# Verify Docker daemon listening ports
 ps aux | grep dockerd

Fixes:

# Use a different host port
 docker run -p 8080:80 nginx

# Stop the service using the port
 systemctl stop nginx
 kill $(lsof -t i:80)

# Ensure no other container uses the same port
 docker ps --format "{{.Names}} {{.Ports}}"

Missing or Wrong Environment Variables

FATAL: Required environment variable DATABASE_URL is not set

Inspect environment variables:

# Show env vars from the container
 docker inspect <container-id> --format='{{range .Config.Env}}{{.}} {{end}}'

# Compare with expected values
 docker inspect <container-id> | jq '.Config.Env'

# If using an .env file, verify its presence
 cat .env

Fixes:

# Pass the variable at run time
 docker run -e "DATABASE_URL=postgres://user:pass@host:5432/db" my-app

# Docker‑compose syntax
 docker-compose run -e "DATABASE_URL=..." app

Incorrect Startup Command

# Exit code 127 or 126 indicates command not found
 docker: Error response from daemon: OCI runtime create failed: ...

Check CMD and ENTRYPOINT:

# Show CMD
 docker inspect <container-id> --format='{{.Config.Cmd}}'

# Show ENTRYPOINT
 docker inspect <container-id> --format='{{.Config.Entrypoint}}'

# Test the command manually
 docker run --rm <image> <cmd> <args>

# Open a shell inside the image
 docker run --rm -it <image> sh

Common mistakes:

ENTRYPOINT and CMD order reversed.

Using shell form (e.g., CMD python app.py) which interferes with signal handling.

Path errors when WORKDIR differs from the binary location.

# Correct JSON form
 ENTRYPOINT ["python", "app.py"]
 CMD ["--help"]

# Ensure the binary path matches WORKDIR
 CMD ["/app/app.py"]

4.3 Resource Problems

Out‑of‑Memory (OOM)

# docker logs may be empty
# docker inspect shows OOMKilled: true
# Exit code 137 or 143

Check OOM status and host memory:

# OOM flag
 docker inspect <container-id> | grep OOMKilled

# Host memory
 free -h

# cgroup limits
 cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
 cat /sys/fs/cgroup/memory/docker/<container-id>/memory.usage_in_bytes

# Kernel OOM messages
 dmesg | grep -i oom | tail -20
 journalctl | grep -i oom | tail -20

Fixes:

# Increase memory limit
 docker run --memory=1g my-app

# Remove limit (not recommended for production)
 docker run --memory="" my-app

# Investigate application memory leaks
 docker stats --no-stream

Disk Space Exhaustion

no space left on device
Error: disk quota exceeded
docker: writing tmp: no space left on device

Check disk usage:

# Host filesystem
 df -h
 df -h /var/lib/docker

# Docker resource usage
 docker system df
 docker ps -s | sort -k3 -rh | head

# Clean up
 docker system prune -af
 docker builder prune -af

Fixes:

# Remove unused resources
 docker system prune -a --volumes

# Delete container logs
 > /var/lib/docker/containers/<container-id>/*-json.log

# Configure log rotation (daemon.json)
 {
   "log-driver": "json-file",
   "log-opts": {"max-size": "100m", "max-file": "3"}
 }

4.4 Permission Problems

SELinux / AppArmor

permission denied: /var/www/html
docker: Error response from daemon: OCI runtime create failed: ... operation not permitted

Check SELinux/AppArmor status:

# SELinux (CentOS/RHEL)
 getenforce
 ausearch -m AVC -ts recent

# AppArmor (Ubuntu)
 aa-status
 apparmor_parser -r /etc/apparmor.d/*

# Privileged mode
 docker inspect <container-id> | grep Privileged

Fixes:

# Temporarily disable SELinux (not for production)
 setenforce 0

# Run privileged (not recommended)
 docker run --privileged my-app

# Proper SELinux label
 docker run -v /data:/data:Z my-app

# Disable AppArmor profile for testing
 docker run --security-opt apparmor=unconfined my-app

Filesystem Permissions

cannot create directory '/var/log/xxx': Permission denied
read-only file system

Inspect read‑only flag and mount permissions:

# Check read‑only rootfs
 docker inspect <container-id> | grep ReadonlyRootfs

# Inspect volume permissions
 ls -la /var/lib/docker/volumes/<volume-name>/_data

# Check container user
 docker inspect <container-id> | grep -E "User|WorkingDir"

Fixes:

# Set correct workdir and ownership in Dockerfile
 WORKDIR /app
 RUN chown -R appuser:appuser /app

# Run as non‑root user
 docker run -u appuser my-app

# If root is required, use privileged mode
 docker run -u root --privileged my-app

4.5 Network Problems

Dependent Service Not Ready

# Container starts but cannot connect to database
dial tcp 192.168.1.21:5432: connection refused
Container may be in Restarting state

Check connectivity from the container:

# Test TCP port
 docker exec <app-container-id> nc -zv db-host 5432

# Test HTTP endpoint
 docker exec <app-container-id> curl -v http://api-host:8080/health

# DNS resolution
 docker exec <app-container-id> nslookup db-host
 cat /etc/resolv.conf

# Inspect network settings
 docker inspect <container-id> | grep -A 10 "Networks"

Solutions:

# Use depends_on for start order (docker‑compose)
 services:
   app:
     image: my-app
     depends_on:
       - db
       - redis
   db:
     image: postgres:15
   redis:
     image: redis:alpine

# Add healthcheck with condition
 services:
   db:
     image: postgres:15
     healthcheck:
       test: ["CMD-SHELL", "pg_isready -U postgres"]
       interval: 5s
       timeout: 3s
       retries: 5
   app:
     image: my-app
     depends_on:
       db:
         condition: service_healthy

# Application‑level retry (Python example)
 import time, psycopg2
 def connect_with_retry(max_retries=10, delay=5):
     for i in range(max_retries):
         try:
             return psycopg2.connect(os.environ['DATABASE_URL'])
         except psycopg2.OperationalError as e:
             print(f"Attempt {i+1} failed: {e}")
             time.sleep(delay)
     raise Exception("Could not connect to database after retries")

Health‑check Failure Causing Restart Loop

# docker ps shows constantly restarting
# docker inspect shows Health status not healthy
# Application logs look fine

Inspect health‑check configuration and logs:

# Show health config
 docker inspect <container-id> | grep -A 10 "Health"

# Show health JSON
 docker inspect <container-id> --format='{{json .State.Health}}' | jq .

# View health‑check log entries
 docker inspect <container-id> | grep -A 5 "Log"

Typical fix:

# Define a proper healthcheck in docker‑compose.yml
 services:
   app:
     image: my-app
     healthcheck:
       test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
       interval: 30s
       timeout: 10s
       retries: 3
       start_period: 40s

4.6 Volume Problems

Host Directory Does Not Exist

docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /data/logs.

Check host directory existence and permissions:

# Verify directory
 ls -la /data/logs

# Check permissions
 ls -la /data

# List Docker volumes
 docker volume ls
 docker volume inspect <volume-name>

Fixes:

# Create the directory
 mkdir -p /data/logs
 chmod 755 /data/logs

# Use a named volume (Docker creates it automatically)
 docker volume create my-data

Volume Overwrites Container Data

Explanation: a bind mount completely masks the container’s target directory. If the host path contains data, the container’s original files become invisible.

Fixes:

# Avoid bind‑mounting over important container directories; use an empty directory instead.
# Use a named volume to share data without overwriting.
 services:
   app:
     volumes:
       - app_data:/var/lib/app

 volumes:
   app_data:
     driver: local

Common Troubleshooting Commands (Quick Reference)

# Container status
 docker ps -a
 docker ps -a | grep <name>

# Logs
 docker logs <id>
 docker logs -f <id>
 docker logs --tail 100 <id>
 docker logs --timestamps <id>

# Detailed info
 docker inspect <id>
 docker inspect <id> --format='{{.State.Status}}'
 docker inspect <id> | jq '.State'

# Images
 docker images
 docker pull <image>:<tag>
 docker rmi <image-id>

# Networks
 docker network ls
 docker network inspect <network-name>
 docker exec <id> cat /etc/resolv.conf

# Resource usage
 docker stats --no-stream
 df -h
 docker system df

# Cleanup
 docker system prune -af
 docker image prune -af
 docker builder prune -af
 docker container prune

Summary

When a Docker container fails to start, the most effective first steps are to examine docker logs and docker inspect. These commands reveal exit codes, OOM flags, configuration errors, and health‑check status, solving the majority of issues. If the logs are inconclusive, use docker run --rm -it <image> sh to enter the image and reproduce the startup process manually.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockernetworkContainertroubleshootingpermissionslogsresource limitsinspect
Raymond Ops
Written by

Raymond Ops

Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.