Docker 容器不斷重新啟動?逐步找出重啟循環原因
Docker container keeps restarting? Find out why the loop won't stop
這篇指南整理 Docker 容器持續重新啟動時的排查流程,從容器狀態、退出代碼和事件紀錄入手,再檢查日誌、依賴服務、健康檢查及記憶體、磁碟與連接埠。文中指出,重啟政策只控制重試頻率,不能修復啟動故障;Docker Engine 本身亦不會因健康檢查失敗而重新啟動容器。
You deployed to a VPS and docker ps keeps flipping between Restarting (1) and a few seconds of Up. The restart policy keeps bringing the process back, but it never fixes whatever kills it. Before you touch config, add memory or blame the database, find out why the process stops. Everything below is read-only; run it only on servers you're authorized to access, and replace YOUR_CONTAINER with the real name or ID. Docker access is root-equivalent, so don't expose the API or hand it to unknown helpers while debugging.
If the container exited once and stayed down, or you just want to decode a specific number, start with Docker exit codes: read the number before you restart anything. This post is about loops.
1. Check it's really a loop and write down the state
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} finished={{.State.FinishedAt}} restarts={{.RestartCount}}' YOUR_CONTAINER
docker events --since 30m --until "$(date +%s)" --filter container=YOUR_CONTAINER --filter event=die --filter event=start
Make sure it's the image and service you meant to deploy, and note the incident time and the last deploy. A restarts count that grows between runs, or die/start pairs repeating in the events, means a loop. FinishedAt is UTC.
The number in Restarting (N) is the last exit code. It points at the next check rather than giving the answer:
| Code | Usually means |
|---|---|
| 0 | Command finished; a service may have daemonized into the background |
| 1 | Application error, read the logs |
| 125 / 126 / 127 | Docker couldn't run it / not executable / not found |
| 137 | SIGKILL, not proof of OOM |
| 139 / 143 | Segfault / stopped with SIGTERM |
2. Restart policy and Docker's backoff
docker inspect --format 'policy={{.HostConfig.RestartPolicy.Name}} max_retries={{.HostConfig.RestartPolicy.MaximumRetryCount}}' YOUR_CONTAINER
| Policy | Behaviour |
|---|---|
no (default) |
Never restarts |
on-failure[:N] |
Restarts only on non-zero exit, optionally at most N times, then stays Exited
|
always |
Restarts whenever it stops; after a manual stop, only when the daemon or container is restarted |
unless-stopped |
Like always, but a container you stopped stays stopped across daemon restarts |
Docker backs off between attempts: 100 ms, doubling each time, capped at one minute. If the container stays up for at least 10 seconds, the delay resets. That's why a crashing container spends longer and longer in Restarting and only flashes Up. In Compose this is the service's restart: key.
Changing the policy doesn't fix a broken startup; it only changes how often you see it fail. Also, don't let both Docker and systemd restart the same container.
3. Read a bounded window of logs
docker logs --since 15m --tail 200 --timestamps YOUR_CONTAINER
Look for the first error before each stop, not the last line. In a loop it repeats. Empty output doesn't mean healthy: the app may log to files, or the logging driver may not expose stdout.
-
Connection refused to the database: go to step 4. Inside a container,
localhostis not the other service. -
Permission denied / missing file: check mount paths, ownership, the runtime user and startup args. Don't
chmod 777your way out. - Config or migration failure: diff recent changes and work out rollback and data compatibility before editing production config.
4. Dependencies that aren't ready yet
Classic loop: the app starts before its database, cache or API is ready, fails its first connection, exits, and gets restarted until the dependency happens to be up.
docker compose ps
docker compose logs --since 15m --timestamps YOUR_DEPENDENCY
- Plain
depends_ononly waits for the dependency's container to start, not to be ready.condition: service_healthywaits for its healthcheck, so it only helps if that service has one. - Check that the app's address matches the dependency's service name and port on the same network.
- If the app gives up after one failed connection, a short retry in the app usually beats relying on restarts.
5. Healthchecks: what they do and don't restart
docker inspect --format '{{json .State.Health}}' YOUR_CONTAINER
On plain Docker Engine, unhealthy is only a label. Docker does not restart a container because its healthcheck fails. If unhealthy containers keep getting restarted, something else is doing it (Swarm, an orchestrator, an autoheal-style helper), and that tool's logs are where to look.
A check that's too strict, or has no start period for a slow app, can keep dependents waiting on service_healthy or keep triggering that external tool. Read the check's own output before blaming the app.
6. Memory, disk and ports
docker inspect --format 'memory_limit_bytes={{.HostConfig.Memory}}' YOUR_CONTAINER
docker stats --no-stream YOUR_CONTAINER
df -h
-
Memory:
OOMKilled=trueis evidence Docker recorded an OOM kill; a 137 alone isn't.docker statsis a snapshot and won't show the earlier peak. - Disk: a full filesystem breaks databases and apps at startup. Find what filled it before deleting anything.
-
Ports: "port is already allocated" means something else holds it;
sudo ss -ltnpshows what.
7. Turn evidence into a next step
Write down the symptom, the evidence, the likely cause and the smallest next check. For production, define impact, a maintenance window and rollback first. Deleting data or containers and widening permissions are not universal fixes.
Illustrative example
Illustrative, not a real incident: after a host reboot the app shows Restarting (1) and its restart count keeps climbing while the database is Up. The app's first log line after every start is a refused connection to the database, and the database log shows it was still initializing at those times. Compose uses depends_on without a health condition. That supports "dependency not ready". The fixes, a database healthcheck plus condition: service_healthy, or a connection retry in the app, are changes to plan, followed by checking that the restart count stops growing.
A restart policy hides the failure; the exit code, events and the first log error tell you what it is.
I work on OpsMate, which puts an SSH terminal and an AI assistant on one page, so you can run the commands above yourself or ask the AI to compare the logs, restart count, policy, health and dependency status for you. Low-risk actions such as service restarts and log rotation run automatically by default, dangerous commands are blocked, and every step is logged. Each time you ask the AI for help, recent terminal output is sent to the cloud AI along with your message, so remove sensitive information first. You get 500 free AI requests per month. Read the full guide and start free on itops.sh.
Drafted with AI assistance and reviewed before publishing.
來源:DEV Community · dev.to