Introduction

Entrypoint scripts are a fundamental Docker concept that allows containers to perform initialization tasks before running the main application. They handle setup operations like configuration generation, permission adjustments, waiting for dependencies, and environment-specific initialization. When an entrypoint script fails, the container never reaches the application startup phase, and the entire container exits immediately.

The "exec permission denied" error is one of the most common entrypoint failures. While the error message suggests a simple permissions issue, the root cause can be more nuanced. The script might lack executable permissions in the image, have incorrect line endings from Windows editing, reference an interpreter that doesn't exist in the image, or be overridden by a bind mount during development. Understanding how to systematically diagnose and fix these issues is essential for building reliable container images.

Docker containers exit with code 126 when the container runtime cannot execute the specified command due to permission issues. This is distinct from exit code 127 (command not found) or exit code 1 (command executed but failed).

Symptoms

When an entrypoint script has permission issues, you will observe these symptoms:

  • Container exits immediately with exit code 126 or 127
  • Docker logs show exec /entrypoint.sh: permission denied
  • The container shows "No such file or directory" for scripts with CRLF line endings
  • The same script works when run on the host system but fails inside the container
  • The issue appeared after moving files between Windows and Linux platforms
  • CI/CD builds fail while local builds succeed (or vice versa)
  • Development containers with bind mounts fail while production containers work

Common error messages from Docker:

bash
docker: Error response from daemon: failed to create shim: OCI runtime create failed:
container_linux.go:380: starting container process caused: exec: "/entrypoint.sh": permission denied: unknown.

Or in container logs:

bash
/bin/bash: /entrypoint.sh: Permission denied
exec /entrypoint.sh: permission denied
standard_init_linux.go:228: exec user process caused: permission denied

With incorrect line endings (CRLF):

bash
exec /entrypoint.sh: no such file or directory
/bin/bash: /entrypoint.sh: /bin/sh^M: bad interpreter: No such file or directory

Common Causes

Several factors cause entrypoint script permission failures:

  1. 1.Missing execute permission: The script file doesn't have the executable bit set (+x) in the container image. This often happens when files are added with COPY without subsequent chmod, or when Git doesn't preserve executable permissions across different operating systems.
  2. 2.CRLF line endings: Scripts edited on Windows often have CRLF (\r\n) line endings instead of LF (\n). The shebang line becomes /bin/sh^M instead of /bin/sh, causing the interpreter to not be found.
  3. 3.Invalid or missing shebang: The script lacks a shebang (#!/bin/sh or similar) at the top, or the shebang references an interpreter that doesn't exist in the base image (e.g., #!/bin/bash in an Alpine image without bash installed).
  4. 4.Bind mount override: During development, a bind mount replaces the executable script in the image with a local file that lacks execute permissions.
  5. 5.Incorrect file ownership: The script is owned by a user other than root or the container user, and that user doesn't have execute permissions.
  6. 6.COPY without preserving permissions: Using COPY without explicit permission handling, especially when the host file system differs from the container environment.
  7. 7.Script encoding issues: The script has a BOM (Byte Order Mark) or is saved with an encoding that the shell doesn't recognize.

Step-by-Step Fix

Follow these steps to diagnose and resolve entrypoint permission issues:

Step 1: Verify the error is indeed a permission issue

Check the container exit status and logs:

```bash # Run the container and capture the exit code docker run --rm my-image echo $? # 126 = permission denied, 127 = not found

# Check container logs docker logs $(docker ps -lq) 2>&1

# Try running the container interactively to debug docker run --rm -it --entrypoint /bin/sh my-image

# Inside the container, check the script ls -la /entrypoint.sh file /entrypoint.sh cat /entrypoint.sh | head -1 ```

Step 2: Set executable permission during the image build

The most reliable fix is to explicitly set permissions in the Dockerfile:

```dockerfile # Method 1: COPY then chmod COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]

# Method 2: COPY --chmod (Docker 17.09+) COPY --chmod=755 entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]

# Method 3: Use a multi-stage build for cleaner permissions FROM alpine as builder COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh

FROM alpine COPY --from=builder /entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ```

Step 3: Check and fix line endings

Ensure scripts use Unix line endings (LF):

```bash # Check current line endings file entrypoint.sh # Output should show: ASCII text, with LF line terminators

# Convert CRLF to LF using dos2unix dos2unix entrypoint.sh

# Or using sed sed -i 's/\r$//' entrypoint.sh

# Or using tr tr -d '\r' < entrypoint.sh > entrypoint_fixed.sh && mv entrypoint_fixed.sh entrypoint.sh ```

Prevent CRLF issues in Git:

```bash # Configure Git to use LF line endings git config --global core.autocrlf false

# Add .gitattributes to enforce LF for shell scripts echo "*.sh text eol=lf" >> .gitattributes git add .gitattributes ```

Step 4: Verify the shebang line

Check that the shebang references an available interpreter:

```bash # Check the shebang line head -1 entrypoint.sh # Should be something like: #!/bin/sh or #!/bin/bash

# Verify the interpreter exists in the base image docker run --rm -it base-image-name ls -la /bin/sh /bin/bash

# For Alpine-based images, install bash if needed # Dockerfile: FROM alpine:latest RUN apk add --no-cache bash COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ```

Common shebang patterns:

bash
#!/bin/sh         # POSIX shell (most portable)
#!/bin/bash       # Bash (requires bash in image)
#!/usr/bin/env bash  # More portable bash shebang
#!/usr/bin/env python3  # For Python scripts

Step 5: Handle development bind mount overrides

If using bind mounts for development, ensure local files have correct permissions:

```bash # Set executable permission on host chmod +x entrypoint.sh

# Or use a development-specific entrypoint approach # docker-compose.yml: services: app: volumes: - .:/app # Override entrypoint for development entrypoint: ["/bin/sh", "-c", "chmod +x /app/entrypoint.sh && /app/entrypoint.sh"] ```

Alternative: Use a separate development Dockerfile:

dockerfile
# Dockerfile.dev
FROM my-base-image
# Don't bind mount scripts; copy them with correct permissions
COPY --chmod=755 entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

Step 6: Debug by running the script manually

If issues persist, debug interactively:

```bash # Start container with shell instead of entrypoint docker run --rm -it --entrypoint /bin/sh my-image

# Inside container: # Check file exists and permissions ls -la /entrypoint.sh

# Check file type and encoding file /entrypoint.sh

# Try running manually /bin/sh /entrypoint.sh

# Check for hidden characters od -c /entrypoint.sh | head

# Verify interpreter exists which sh which bash ```

Step 7: Rebuild and verify

After fixes, rebuild and test:

```bash # Rebuild without cache to ensure fresh build docker build --no-cache -t my-image .

# Test the container starts successfully docker run --rm my-image echo "Container started successfully"

# Verify entrypoint runs docker run --rm my-image ```

Verification

After applying fixes, verify the entrypoint works:

```bash # Build the image docker build -t my-image .

# Run container and check exit code docker run --rm my-image echo $? # Should be 0 (success)

# Check entrypoint permissions in image docker run --rm my-image ls -la /entrypoint.sh # Should show: -rwxr-xr-x (755 permissions)

# Check line endings docker run --rm my-image file /entrypoint.sh # Should show: POSIX shell script, ASCII text executable

# Test in different environments docker run --rm -it my-image /entrypoint.sh --help ```

Prevention

To prevent entrypoint permission issues:

  1. 1.Always set permissions explicitly in Dockerfile: Never rely on host filesystem permissions being preserved. Add RUN chmod +x or use COPY --chmod=755.

```dockerfile # Best practice: Use COPY --chmod COPY --chmod=755 scripts/*.sh /scripts/

# Or explicit chmod COPY scripts/*.sh /scripts/ RUN chmod +x /scripts/*.sh ```

  1. 1.Use .gitattributes for consistent line endings: Enforce LF line endings for all shell scripts in version control.
gitattributes
# .gitattributes
*.sh text eol=lf
*.py text eol=lf
  1. 1.Validate scripts during CI: Add a linting step that checks script validity.
yaml
# GitHub Actions example
- name: Check shell scripts
  run: |
    find . -name "*.sh" -exec shellcheck {} \;
    find . -name "*.sh" -exec bash -n {} \;
  1. 1.Test builds on Linux: Ensure CI builds run on Linux hosts to catch line ending and permission issues early.
  2. 2.Use hadolint for Dockerfile linting: Catch permission issues in Dockerfile before building.
bash
hadolint Dockerfile
  1. 1.Document entrypoint requirements: Clearly document that entrypoint scripts must have LF line endings and require executable permissions.
  • [Fix docker build cache invalidated unnecessary layers Issue in Docker-Errors](docker-build-cache-invalidated-unnecessary-layers)
  • [Fix Docker Build Cache Invalidation Optimization Issue in Docker](docker-build-cache-invalidation-optimization)
  • [Fix docker build context slow large files Issue in Docker-Errors](docker-build-context-slow-large-files)
  • [Fix docker build multi stage copy from not found Issue in Docker-Errors](docker-build-multi-stage-copy-from-not-found)
  • [Fix docker buildkit export local tar layer missing Issue in Docker-Errors](docker-buildkit-export-local-tar-layer-missing)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Docker Entrypoint Script Failed With exec Permission Denied", "description": "Resolve Docker entrypoint permission denied errors by setting executable permissions, checking line endings, and validating the shebang and copy behavior in the image build.", "url": "https://www.fixwikihub.com/docker-entrypoint-script-exec-permission-denied", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-23T10:32:32.381Z", "dateModified": "2026-01-23T10:32:32.381Z" } </script>