Exit code 0 Clean Docker & Kubernetes

Exit Code 0 (Success) in Docker & Kubernetes

Verdict · 0 = successful process exit (no signal)

Exit code 0 means the container's PID 1 returned success. Every thread stopped and the process called exit(0) or returned from main; no signal was involved. Kubernetes records the container's terminated reason as Completed. If the pod restarts anyway, the cause is a restart policy or a Job controller, not a crash.

Observed exit0
Signal / meaningSuccess
Default actionClean

Proof commands: confirm the exit status and the killer

Run these before changing anything. The first two identify what the runtime recorded; the third finds a kernel-level kill on the host. Replace the placeholder container ID, pod name or host path with your own.

Docker inspect

docker inspect <container_id> --format '{{.State.Status}} | ExitCode: {{.State.ExitCode}} | Restarts: {{.RestartCount}}'

Kubernetes state

kubectl get pod <pod_name> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.lastState.terminated.reason}{"\tExit:"}{.lastState.terminated.exitCode}{"\n"}{end}'

Restart loop

docker events --since 15m --filter event=die --format '{{.Actor.Attributes.name}} exit={{.Actor.Attributes.exitCode}}'

An exit code of 0 is a fact about the main process, not a health verdict. Liveness, readiness and traffic serving are separate signals that a process can fail while still exiting cleanly. If the exit code is 0 but the workload is unavailable, inspect why the process decided to finish.

Three production root causes

The main process returned success. Docker records Exited (0) and Kubernetes records Completed. A pod that restarts with code 0 has finished its work and is being restarted by restartPolicy: Always or by a Job that still needs more successful completions.

1. A one-shot container finished its work

Migration, backup, seed and CLI containers are designed to exit. Under restart: always or a Kubernetes restartPolicy of Always, the runtime starts them again even though the first run succeeded. Read restart: on-failure or Job objects instead of using a long-running deployment shape.

2. The application treats an expected condition as success

A worker that stops when its queue is empty, a shell script ending in exit 0, or a wrapper that discards the child status all return 0. Check the last lines of docker logs and the actual command and args, not just the image's default CMD.

3. A Kubernetes Job has not reached completions

A Job with completions: 3 restarts a pod that exits 0 after one item. The pod shows Completed, not CrashLoopBackOff, so read .status.succeeded, backoffLimit and Job events rather than only pod restarts.

Copy-ready fixes for Docker Compose and Kubernetes

The two manifests set explicit memory limits and a termination grace period. Use them as a baseline, then adjust the values to the measured working set of the service.

Compose

docker-compose.yml

For a one-shot service use restart: "no" and run it with docker compose run --rm. deploy.resources still prevents a batch job from taking the host down if it grows unexpectedly.

services:
  app:
    image: ghcr.io/acme/app:1.4.2
    restart: on-failure:3
    stop_grace_period: 10s
    deploy:
      resources:
        limits:
          memory: 256M
        reservations:
          memory: 128M
Kubernetes

deployment.yaml

A Deployment with this shape expects a long-running process. If the container is a batch or migration step, use a Job with restartPolicy: OnFailure, backoffLimit: 3 and an explicit completions value, then keep the Deployment for the API only.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: app
          image: ghcr.io/acme/app:1.4.2
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"

Frequently asked questions

Does exit code 0 mean the container is healthy?

No. Exit code 0 only records that the main process terminated with success. A container can exit 0 before it starts listening, after a failed readiness check, or while in-flight work is lost. In Kubernetes, health is expressed by readiness and liveness probes and by whether the process keeps running.

Why does a Kubernetes pod with exit code 0 keep restarting?

Because restartPolicy: Always restarts every container that stops, including one that returns 0. For finite work, use a Job with restartPolicy: OnFailure or Never. For a worker that should idle, keep the process alive and wait on its queue instead of exiting when the queue is empty.

How do I stop a finished container from restarting?

Set restart: "no" in Compose or run the container without --restart. In Kubernetes, choose the controller by workload type: Job for finite work, Deployment for long-running services. Restart counts without a changing image usually mean the process is exiting on its own.

Follow the status you actually observed, then check the related port or calculator when the failure involves networking, exposure or firewall rules.

Reference basis: POSIX signal semantics (128 + n), Docker container exit status conventions, and Kubernetes container termination behaviour. Verify the exact behaviour against your runtime version before relying on it in an incident.

Last reviewed 2026-11-06 · All exit codes · Developer tools · Privacy