How to fix OCI runtime exec failed: exec failed: unable to start container process: exec: "curl": executable file not found in $PATH in Docker

DockerINTERMEDIATEMEDIUMUpdated Dec 6, 2025· 238 views

This error occurs when a Docker container's HEALTHCHECK command references an executable (like curl or wget) that doesn't exist in the container image. The fix involves using an alternative tool that's already installed, installing the required tool, or building a custom healthcheck binary for minimal images.

Quick fix

Fixes OCI runtime exec failed: exec failed: unable to start container process: exec: "curl": executable file not found in $PATH

# View container health status and recent errors
docker inspect --format='{{json .State.Health}}' <container_name> | jq

# Check the Dockerfile HEALTHCHECK instruction
grep -i healthcheck Dockerfile

# Check docker-compose.yml health check configuration
grep -A 5 healthcheck docker-compose.yml
  1. Identify which command is missing
  2. Use wget instead of curl (Alpine images)
  3. Use netcat for TCP port checks (Alpine images)
  4. Install curl in your Dockerfile
docker — error
# View container health status and recent errors
docker inspect --format='{{json .State.Health}}' <container_name> | jq
# Check the Dockerfile HEALTHCHECK instruction
grep -i healthcheck Dockerfile
# Check docker-compose.yml health check configuration
grep -A 5 healthcheck docker-compose.yml
OCI runtime exec failed: exec failed: unable to start container process: exec: "curl": executable file not found in $PATH

What this error means

The "OCI runtime exec failed: executable file not found in $PATH" error occurs when Docker tries to run a health check command but cannot find the specified executable inside the container. This is extremely common with minimal container images like Alpine, distroless, or scratch-based images that don't include common utilities like curl or wget. Docker's health check mechanism runs commands inside your container to verify the service is working correctly. When the health check specifies a command like `curl -f http://localhost:8080/health`, Docker attempts to execute curl from within the container's filesystem. If curl isn't installed, the OCI runtime (runc) fails with this error. This error is particularly prevalent because many developers copy health check configurations from tutorials or documentation without realizing their base image doesn't include the required tools. Alpine images, for example, don't include curl by default - only wget is available. Distroless and scratch images contain no shell utilities at all.

Typical symptoms

  • Container shows '(unhealthy)' status in docker ps output
  • docker inspect shows health check errors with 'executable file not found'
  • Health check works locally but fails in production with different base image
  • Container runs fine but health checks consistently fail
  • docker-compose services fail to start when using depends_on with service_healthy condition
  • Error message contains 'OCI runtime exec failed' followed by the missing command name
  • Kubernetes liveness/readiness probes fail with 'command not found' in events

Common causes

  1. Using curl in health check but the container image doesn't have curl installed (common with Alpine)
  2. Base image is minimal/distroless and contains no shell utilities
  3. Multi-stage build didn't copy the health check tool to the final image
  4. Health check command copied from documentation without verifying tool availability
  5. Using scratch base image which contains no executables except your application
  6. Package was installed in a different stage but not in the runtime stage
  7. Image was slimmed/optimized and utility tools were removed

How to fix "OCI runtime exec failed: exec failed: unable to start container process: exec: "curl": executable file not found in $PATH"

1Identify which command is missing

First, check the health check configuration and error details:

bash
# View container health status and recent errors
docker inspect --format='{{json .State.Health}}' <container_name> | jq

# Check the Dockerfile HEALTHCHECK instruction
grep -i healthcheck Dockerfile

# Check docker-compose.yml health check configuration
grep -A 5 healthcheck docker-compose.yml

The error message will tell you exactly which executable is missing. Common ones include:
- curl - Not installed by default in Alpine or minimal images
- wget - Available in Alpine but not in distroless/scratch
- nc (netcat) - Available in Alpine but not in all images

2Use wget instead of curl (Alpine images)

Alpine Linux includes wget by default, so you can use it without installing anything:

In Dockerfile:

dockerfile
# Instead of curl
HEALTHCHECK CMD wget -qO- http://localhost:8080/health || exit 1

# Spider mode (doesn't download content, just checks URL exists)
HEALTHCHECK CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

In docker-compose.yml:

yaml
healthcheck:
  test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

wget options explained:
- -q: Quiet mode (no progress output)
- -O-: Output to stdout (useful for piping)
- --no-verbose: Less output without being completely quiet
- --tries=1: Don't retry on failure
- --spider: Don't download, just check if URL exists

3Use netcat for TCP port checks (Alpine images)

If you only need to verify a port is open (not a full HTTP check), netcat is available in Alpine:

In Dockerfile:

dockerfile
HEALTHCHECK CMD nc -z localhost 8080 || exit 1

In docker-compose.yml:

yaml
healthcheck:
  test: ["CMD", "nc", "-z", "localhost", "8080"]
  interval: 10s
  timeout: 5s
  retries: 3

This is simpler and faster than HTTP checks when you just need to verify the service is listening on the expected port. Note that this doesn't verify the application is responding correctly - only that the port is open.

4Install curl in your Dockerfile

If you need curl specifically (for features wget doesn't have), install it in your Dockerfile:

For Alpine-based images:

dockerfile
FROM node:20-alpine

# Install curl
RUN apk add --no-cache curl

# Your application setup...
COPY . .

HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

For Debian/Ubuntu-based images:

dockerfile
FROM node:20

# Install curl (often already included, but just in case)
RUN apt-get update && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*

HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

Note: Adding curl increases image size by ~2-5 MB depending on the base image. Consider using wget if it's already available.

5Use your application runtime for health checks

Instead of installing external tools, use the runtime your application already has:

Node.js example:

dockerfile
HEALTHCHECK CMD node -e "require('http').get('http://localhost:8080/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"

Python example:

dockerfile
HEALTHCHECK CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1

PHP example:

dockerfile
HEALTHCHECK CMD php -r "file_get_contents('http://localhost:8080/health');" || exit 1

This approach has zero additional dependencies since your runtime is already in the image.

6Build a custom healthcheck binary (for distroless/scratch)

For minimal images without any shell or utilities, compile a small static binary:

Go healthcheck binary (healthcheck.go):

go
package main

import (
    "net/http"
    "os"
    "time"
)

func main() {
    client := &http.Client{Timeout: 5 * time.Second}
    url := "http://localhost:8080/health"
    if len(os.Args) > 1 {
        url = os.Args[1]
    }
    resp, err := client.Get(url)
    if err != nil || resp.StatusCode != 200 {
        os.Exit(1)
    }
    os.Exit(0)
}

Build and use in Dockerfile:

dockerfile
# Build stage
FROM golang:1.21 AS healthcheck-builder
WORKDIR /build
COPY healthcheck.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o healthcheck healthcheck.go

# Final stage (distroless)
FROM gcr.io/distroless/static:nonroot
COPY --from=healthcheck-builder /build/healthcheck /healthcheck
COPY --from=builder /app/myapp /app/myapp
HEALTHCHECK CMD ["/healthcheck"]

Alternatively, use a pre-built tool like microcheck:

dockerfile
COPY --from=ghcr.io/tarampampam/microcheck:1 /bin/httpcheck /healthcheck
HEALTHCHECK CMD ["/healthcheck", "http://localhost:8080/health"]
7Verify the fix works

After updating your health check, rebuild and test:

bash
# Rebuild the image
docker build -t myapp:latest .

# Run with health check enabled
docker run -d --name test-healthcheck myapp:latest

# Wait for health check to run and check status
sleep 45
docker ps --format "table {{.Names}}\t{{.Status}}"

# View detailed health check results
docker inspect --format='{{json .State.Health}}' test-healthcheck | jq

# If unhealthy, check the logs for errors
docker inspect test-healthcheck | jq '.State.Health.Log[-3:]'

Expected output shows "healthy" status:

json
{
  "Status": "healthy",
  "FailingStreak": 0,
  "Log": [...]
}

Advanced notes

Why minimal images don't have curl/wget: Distroless and scratch images are designed to contain only your application and its runtime dependencies. They intentionally exclude package managers, shells, and utility programs to reduce attack surface and image size. This is a security best practice, but it means traditional health checks won't work.

Comparing health check options for minimal images:

| Approach | Image Size Impact | Complexity | Works in Distroless |
|----------|------------------|------------|---------------------|
| wget (Alpine) | 0 (built-in) | Low | No |
| curl (install) | +2-5 MB | Low | No |
| Runtime healthcheck | 0 | Medium | Yes (if runtime exists) |
| Custom Go binary | +1-2 MB | High | Yes |
| microcheck | +500 KB | Low | Yes |

Kubernetes alternative: If you're deploying to Kubernetes, consider using HTTP liveness/readiness probes instead of Docker HEALTHCHECK. Kubernetes probes run from outside the container, so they don't need any tools inside:

yaml
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

ECS and Fargate: AWS ECS health checks can also be configured at the load balancer level, avoiding the need for in-container tools:

json
{
  "healthCheck": {
    "command": ["CMD-SHELL", "exit 0"],
    "interval": 30,
    "timeout": 5,
    "retries": 3
  }
}

Then configure the ALB/NLB target group health check to hit your health endpoint.

Security consideration: While it's tempting to install curl "just for health checks," each additional binary increases your attack surface. If your container is compromised, tools like curl and wget can be used to exfiltrate data or download malware. Consider whether a port check with netcat or a custom binary is sufficient.

Frequently asked questions

What does "OCI runtime exec failed: exec failed: unable to start container proces" mean?
The "OCI runtime exec failed: executable file not found in $PATH" error occurs when Docker tries to run a health check command but cannot find the specified executable inside the container. This is extremely common with minimal container images like Alpine, distroless, or scratch-based images that don't include common utilities like curl or wget. Docker's health check mechanism runs commands inside your container to verify the service is working correctly.
How do I fix "OCI runtime exec failed: exec failed: unable to start container proces"?
This error occurs when a Docker container's HEALTHCHECK command references an executable (like curl or wget) that doesn't exist in the container image. The fix involves using an alternative tool that's already installed, installing the required tool, or building a custom healthcheck binary for minimal images.
What causes "OCI runtime exec failed: exec failed: unable to start container proces"?
Using curl in health check but the container image doesn't have curl installed (common with Alpine) Base image is minimal/distroless and contains no shell utilities Multi-stage build didn't copy the health check tool to the final image
Is the "OCI runtime exec failed: exec failed: unable to start container proces" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading