Exit code 255 Fatal Docker & Kubernetes

Exit Code 255 (Exit Out of Range) in Docker & Kubernetes

Verdict · 255 = exit status -1 (8-bit wrap) or ssh fatal error

Exit code 255 has two common meanings. A process that returns -1 or calls exit(-1) appears to its parent as 255 because the status is truncated to 8 bits. The OpenSSH client also uses 255 for its own fatal errors. Neither is a Linux signal and 128+n does not apply.

Observed exit255
Signal / meaningExit Out of Range
Default actionFatal

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}} | OOMKilled: {{.State.OOMKilled}}'

Kubernetes state

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

SSH transport

ssh -vvv -o BatchMode=yes -o ConnectTimeout=5 user@host true; echo "exit=$?"

If the container produced normal application logs and then returned 255, the program chose that status. If there are no remote logs at all, suspect ssh or another transport wrapper. The transport's stderr, not the remote application log, contains the reason for a 255 that happened before the session started.

Three production root causes

Either an application returned -1, which truncates to 255, or the ssh client reported a fatal transport error. Check whether the process ran at all: an ssh failure never reached the remote command. Read ssh -vvv for the first connection or authentication error.

1. ssh could not connect or authenticate

DNS failure, TCP reset, a changed host key, a rejected key, wrong permissions on ~/.ssh, a missing ProxyJump or a server that closed the session all produce 255. The remote command never ran, so remote logs are empty. Use ssh -vvv and fix the first transport-level error rather than retrying in a loop.

2. The application returns -1 as a generic failure

C code returning -1 from main, a wrapper that propagates a negative status, or a language binding that maps every error to -1 produces 255 after the 8-bit truncation. This looks like an SSH failure only if the command is remote; check whether the process itself logged a failure first.

3. docker exec or kubectl exec propagated 255

When the exec itself succeeds, both commands propagate the remote command's exit status. A command that returns 255 is not a transport failure of kubectl or Docker; if the exec cannot start or connect, the failure is reported separately. Capture the command's own stderr and exit code, then decide whether the failure came from the executable or from the API connection.

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

A program that returns -1 will be restarted forever by restart: always. Fix the status mapping in the application, then use on-failure with a bounded retry count for genuinely transient failures.

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

Kubernetes records the container's status verbatim. 255 is not a kubelet-generated code, so read the application's own log and the exit path. If a Job wrapper runs ssh, the job fails with 255 and the remote logs are the only useful source.

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"
SSH client

Fail fast and keep the transport error visible

Retries hide the first fatal error; verbose mode shows it once.

Host deploy
  HostName deploy.internal
  User deploy
  IdentityFile ~/.ssh/deploy_ed25519
  IdentitiesOnly yes
  BatchMode yes
  ConnectTimeout 5
  ServerAliveInterval 15
  ServerAliveCountMax 3

Frequently asked questions

Why does ssh return 255?

The ssh client uses 255 for its own fatal errors: connection failure, host key mismatch, authentication failure or a broken session. The remote command never ran. Run ssh -vvv, fix the first error in the transport log, and do not treat 255 as the remote program's exit status.

Why did a program that returns -1 show exit code 255?

POSIX exit statuses are 8 bits. -1 is 0xFF, which the parent observes as 255. The process did run and fail; this is different from ssh, where 255 means the client could not establish the session. Check whether the application logged an error before exiting.

How do I distinguish ssh failure from application failure?

If there are no remote logs and ssh's stderr shows a connection or authentication error, the transport failed. If the remote command logged normal output and then returned 255, the program chose that status. In CI, capture ssh's stderr separately from the remote command's stdout and stderr.

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