When a site that runs in Docker goes down, the cause usually sits in one of a short list of places: the container isn't running, it is running but not reachable, the app inside is unhealthy, the reverse proxy points at the wrong place, or the host has run out of disk or memory. Working through them in order is faster than guessing. This checklist goes from outside to inside and gives the command for each step. It assumes a Linux host with Docker Engine and, where relevant, Docker Compose v2 (docker compose).
Step 0: Confirm what users see
Record the symptom from outside first. A refusal, a timeout, a 502 and a 404 point to different layers.
curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n" https://example.com/
The HTTP स्टेटस चेकर और पोर्ट चेकर give an outside view. Interpret the symptom with connection refused, timed out, reset explained. If a CDN is in front, the Cloudflare एरर चेकर shows whether the error page is its own.
Step 1: Are the containers running?
docker ps # running containers
docker ps -a # includes stopped ones, with exit status
docker compose ps # for Compose projects, run in the project directory
Look at the STATUS column:
Up 3 days (healthy): running and passing its healthcheck.Up 5 minutes (unhealthy): running, failing its healthcheck.Restarting (1) 20 seconds ago: a crash loop.Exited (137) 2 hours ago: stopped; the code matters.
Exit codes worth knowing:
| Code | Usual meaning |
|---|---|
| 0 | Process finished normally; a web server should not do this |
| 1 | Application error on startup or crash |
| 125, 126, 127 | Docker could not run the command: bad options, not executable, command not found |
| 137 | Killed with SIGKILL, often by the kernel out-of-memory killer or docker kill |
| 139 | Segmentation fault |
| 143 | Terminated with SIGTERM, normally a clean stop |
Confirm an out-of-memory kill directly:
docker inspect --format '{{.State.OOMKilled}} exit={{.State.ExitCode}}' CONTAINER
If a container is stopped, restart it, but only after you read the logs (next step), or you lose the reason.
Step 2: Read the logs
docker logs --tail 100 CONTAINER
docker logs -f --since 10m CONTAINER # follow, recent only
docker compose logs --tail 100 -f web proxy # Compose, by service name
Look for the first error, not the last. Typical findings: a missing environment variable, a failed database connection, a port already in use inside the container, a permissions error on a mounted volume, or a migration that crashed on startup.
For host-level events such as kills and restarts:
docker events --since 1h --filter type=container
sudo dmesg -T | grep -iE "killed process|out of memory"
sudo journalctl -u docker -n 100 --no-pager
Step 3: Is the port published and the app listening correctly?
A container can run perfectly and still be unreachable. Two separate settings are involved.
Published ports map a host port to a container port. EXPOSE in a Dockerfile only documents a port; it doesn't publish it.
docker ps --format "table {{.Names}}\t{{.Ports}}"
docker port CONTAINER
You want something like 0.0.0.0:8080->80/tcp. If it shows 127.0.0.1:8080->80/tcp, the port is only available locally. This is intentional behind a reverse proxy but a problem if the world is meant to reach it. No -> means nothing is published.
In Compose:
services:
web:
image: myapp:latest
ports:
- "8080:80" # host 8080 -> container 80
# - "127.0.0.1:8080:80" # local only, for use behind a proxy
The app must listen on all interfaces inside the container. An app bound to 127.0.0.1 inside the container is unreachable from outside the container, even with published ports. Set the bind address to 0.0.0.0 (for example --host 0.0.0.0 in many dev servers, or HOST=0.0.0.0). Verify from inside:
docker exec CONTAINER sh -c "ss -ltn 2>/dev/null || netstat -ltn"
docker exec CONTAINER wget -qO- http://127.0.0.1:80/ | head
Then check from the host:
curl -sI http://127.0.0.1:8080/
sudo ss -ltnp | grep 8080
If the host answers but the internet cannot reach it, the problem is the firewall, security group, or DNS. Test from outside with the पोर्ट चेकर. Note that Docker publishes ports by writing its own firewall rules, and published ports can bypass host firewall tools like UFW rules. Don't assume a firewall rule blocks a published port; check how your setup behaves.
Step 4: Healthchecks
A healthcheck tells Docker whether the app is working, beyond the process being alive.
docker inspect --format '{{json .State.Health}}' CONTAINER | python3 -m json.tool
The output includes the status and the last few check results with output. Define one in your Dockerfile or Compose file:
services:
web:
image: myapp:latest
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/healthz || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
restart: unless-stopped
Points to remember:
- The check command runs inside the container, so the tool it uses (
curl,wget) must exist in the image. start_periodgives slow-starting apps time before failures count.- Plain Docker marks a container
unhealthy, but it doesn't restart it on its own. A restart policy only reacts to the process exiting. Don't assumeunhealthytriggers recovery; you need an orchestrator or an external watcher for that. - Use
depends_onwithcondition: service_healthyso the app starts after its database is ready:
web:
depends_on:
db:
condition: service_healthy
Design the endpoint with the advice in load balancer health checks.
Step 5: The reverse proxy
Most Docker sites have a proxy in front (nginx, Caddy, Traefik, or a host nginx). A 502 or 504 usually means the proxy can't reach the app container.
docker compose logs --tail 50 proxy
docker exec PROXY_CONTAINER nginx -t
docker exec PROXY_CONTAINER wget -qO- http://web:3000/healthz
Common causes:
- Wrong upstream name or port. The proxy must use the container's internal port (
web:3000), not the host-published port, when both are on the same Docker network. - Different networks. Containers can only reach each other by service name when they share a user-defined network. Check with
docker network inspect NETWORK. - Stale IP after recreation. nginx resolves hostnames in
proxy_passat startup. If the app container is recreated and gets a new IP, nginx keeps the old one and returns 502 until reloaded. Fix with a reload, or force runtime resolution through Docker's embedded DNS:
resolver 127.0.0.11 valid=10s;
set $upstream http://web:3000;
location / {
proxy_pass $upstream;
}
- Proxy started before the app and cached a failure. Add health-based
depends_onor reload the proxy. - Host nginx proxying to a container. Make sure the container port is published to
127.0.0.1and theproxy_passpoints there.
For the proxy's own error log messages and timeout tuning, read nginx 502 and 504 timeout tuning और fix 502 bad gateway.
Step 6: Disk full
A full disk is one of the most common reasons a Docker host quietly breaks: containers fail to start, databases crash, and logs can't be written.
df -h
df -i # inode exhaustion looks the same as a full disk
docker system df # space used by images, containers, volumes, build cache
sudo du -xh /var/lib/docker --max-depth=1 2>/dev/null | sort -h | tail
The usual culprits:
- Container logs. Docker's default
json-filelogging driver does not rotate logs unless you configure it. A chatty container can fill a disk. Find large logs:
sudo sh -c 'ls -lhS /var/lib/docker/containers/*/*-json.log | head'
Set rotation globally in /etc/docker/daemon.json, then restart the Docker daemon (existing containers need to be recreated to pick up the new default):
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
Or per service in Compose:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
- Unused images and build cache. After confirming nothing needed is affected:
docker image prune -a # removes images not used by any container
docker builder prune # removes build cache
Step 7: Memory and CPU
docker stats --no-stream
free -m
A container with too low a memory limit is OOM-killed (exit 137), and a host short on memory kills its largest process, which may be your database. Raise the limit or reduce the app's worker count.
Step 8: Restart policy and recent changes
If the whole host rebooted and the site stayed down, containers were probably not set to come back.
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' CONTAINER
systemctl is-enabled docker
Use restart: unless-stopped (or always) for services meant to run continuously, and make sure the Docker service is enabled at boot. A restart policy does not fix a crash loop caused by bad config. Also ask what changed: a new image (avoid the latest tag in production), an edited environment file, or a changed volume mount. docker compose config shows the final resolved configuration.
Quick triage table
| Symptom | First command | Likely cause |
|---|---|---|
| Connection refused from outside | docker ps, ss -ltnp | Container down, port unpublished, firewall |
| 502 from proxy | docker compose logs proxy | Wrong upstream, stale IP, app down |
| Container restarting | docker logs --tail 100 | Startup crash, missing config |
| Exit 137 | docker inspect ... OOMKilled | Out of memory |
| Everything fails, writes error | df -h, df -i | Disk or inodes full |
| Works locally, not remotely | docker port, firewall, DNS | Binding to 127.0.0.1, security group |
| Up but unhealthy | docker inspect ... .State.Health | App-level failure, check tool missing |
Prevent the next outage
Add log rotation and a restart policy to every service, alert on disk usage, and put an external check on the public URL. The free अपटाइम मॉनिटर supports keyword and JSON checks, so a "200 with an error page" is caught. For scheduled jobs inside containers see cron job and heartbeat monitoring, and after an incident use the incident postmortem template.
अगले कदम
- Check reachability from outside with the पोर्ट चेकर.
- See the real response with the HTTP Header Checker.
- Follow the broader why is my website down workflow.
- Tune the proxy with nginx 502 and 504 timeout tuning.
- Set up alerts with the अपटाइम मॉनिटर.
सामान्य प्रश्न
Why is my container running but the site unreachable?
Usually the port is not published, the app binds to 127.0.0.1 inside the container instead of 0.0.0.0, or a firewall or proxy is in the way. Check docker port, the app's bind address, and test with curl from the host.
Does Docker restart an unhealthy container automatically?
Not by itself. A restart policy only reacts when the container's main process exits. An unhealthy status is informational unless an orchestrator or external script acts on it.
Is it safe to run docker system prune?
Without --volumes it removes stopped containers, unused networks, dangling images and build cache. Adding --volumes can delete data in unused volumes, so back up and review first.
Why do I get 502 after redeploying one container?
The reverse proxy may have cached the old container IP. Reload the proxy, or configure it to resolve the service name at request time using Docker's embedded DNS at 127.0.0.11.
Get notified when this guide updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.