The WORKDIR error occurs when Docker cannot change to the specified directory during a container run or build. This typically happens with multi-stage builds, incorrect base images, or when running containers with a WORKDIR that was not created. Verify your Dockerfile structure or use absolute paths.
The "WORKDIR: cannot change directory to /app: no such file or directory" error indicates that Docker is trying to set the working directory to a path that doesn't exist inside the container. This error can occur in two main scenarios: 1. **During container runtime**: When you run a container (with `docker run`) and the image specifies a WORKDIR that doesn't exist in the final image layers. 2. **During image build**: When Docker processes a WORKDIR instruction in a Dockerfile and the directory cannot be created (rare, as WORKDIR normally creates the directory automatically). The most common cause is multi-stage builds where the WORKDIR exists in a build stage but not in the final runtime stage. Docker's WORKDIR instruction should automatically create the directory if it doesn't exist, so this error typically indicates a more fundamental issue with the image structure or how the container is being run.
First, check if the directory actually exists in your Docker image:
# Run a shell in the image to inspect its filesystem
docker run --rm -it --entrypoint /bin/sh your-image -c "ls -la /"
# Or if the image has no shell (minimal/distroless)
docker run --rm -it alpine sh -c "docker save your-image | tar -tvf - | grep app"
# Check the image layers for the directory
docker history your-imageIf the directory doesn't exist, you need to ensure it's created in your Dockerfile.
In multi-stage builds, each stage starts fresh. The WORKDIR from a build stage doesn't carry over:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Runtime stage - WORKDIR must be declared again!
FROM node:18-alpine AS runtime
WORKDIR /app # This creates /app in the final image
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]The key insight is that WORKDIR /app in the runtime stage creates the directory - it doesn't inherit from the builder stage.
Always use absolute paths for WORKDIR and place it early in your Dockerfile:
# Good practice - absolute path, declared early
FROM alpine:latest
WORKDIR /app
COPY . .
RUN chmod +x ./entrypoint.sh
# Bad practice - relative path, may inherit unknown base WORKDIR
FROM some-base-image
WORKDIR app # Relative to whatever WORKDIR the base image set
COPY . .Docker recommends always using absolute paths to avoid unexpected behavior from base images:
# Explicitly set WORKDIR even if you think the base sets it
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY public/ .When using FROM scratch or distroless images, you need to handle directory creation differently:
# For scratch images, create directory structure in builder
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /app/myapp
FROM scratch
# Copy the binary - WORKDIR won't work without a shell
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]For distroless images that support WORKDIR:
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o /app/server
FROM gcr.io/distroless/static-debian11
WORKDIR /app
COPY --from=builder /app/server /app/server
CMD ["/app/server"]If your image works on macOS but fails on Linux, check for case sensitivity issues:
# macOS is case-insensitive by default
# These refer to the same directory on Mac:
# /App, /APP, /app
# Linux is case-sensitive
# /App, /APP, /app are three different directoriesEnsure your Dockerfile and code use consistent casing:
# Ensure exact case match throughout
WORKDIR /app # lowercase
COPY src/ ./src/ # lowercase
RUN cd /app && ./build.sh # lowercase - must match WORKDIRCheck your local filesystem for mismatched cases:
# Find files with unusual casing
find . -name "[A-Z]*" -type fIf you're getting this error when using docker run -w, ensure the directory exists:
# This may fail if /nonexistent doesn't exist in the image
docker run -w /nonexistent alpine pwd
# Standard Docker creates the directory, but some container runtimes don't
# Check your container runtime behavior
# Safe approach: use an image WORKDIR or create the directory
docker run alpine sh -c "mkdir -p /myworkdir && cd /myworkdir && pwd"Note: Standard Docker should create the directory specified by -w, but some alternative container runtimes (like containerd with certain configurations) may not.
If you're still having issues, do a clean rebuild:
# Remove cached layers and rebuild
docker build --no-cache -t your-image:latest .
# Inspect the resulting image
docker inspect your-image:latest | jq '.[0].Config.WorkingDir'
# Run with verbose output
docker run --rm your-image:latest pwd
# If that works, check what your actual entrypoint does
docker run --rm --entrypoint /bin/sh your-image:latest -c "pwd && ls -la"Also verify your .dockerignore isn't excluding necessary files:
# Check what gets sent to the build context
cat .dockerignore### Multi-Stage Build WORKDIR Behavior
Each FROM instruction in a Dockerfile starts a completely new build stage with its own filesystem. WORKDIR, ENV, and other settings from previous stages do not carry over:
FROM alpine AS stage1
WORKDIR /build
ENV MY_VAR=hello
RUN mkdir -p /output
FROM alpine AS stage2
# /build doesn't exist here
# MY_VAR is not set here
# /output doesn't exist here
WORKDIR /app # Creates fresh /app directoryTo transfer files between stages, use COPY --from=stage1.
### WORKDIR vs RUN mkdir
WORKDIR has special behavior that differs from manually creating directories:
1. WORKDIR creates all parent directories - WORKDIR /a/b/c/d creates the entire path
2. WORKDIR sets ownership to root - unless you've set USER before it
3. WORKDIR persists across RUN commands - unlike cd which only affects one RUN
# These are NOT equivalent:
WORKDIR /app/subdir # Creates /app and /app/subdir owned by root
# vs
RUN mkdir -p /app/subdir && cd /app/subdir
# Next RUN starts back at the previous WORKDIR, not /app/subdir### USER and WORKDIR Interaction
When using non-root users, the order of USER and WORKDIR matters:
# WORKDIR before USER - directory owned by root
WORKDIR /app
USER node
RUN touch test.txt # May fail - can't write to root-owned /app
# Better approach
RUN mkdir -p /app && chown node:node /app
USER node
WORKDIR /app
RUN touch test.txt # Works - /app is owned by node### BuildKit and WORKDIR
With Docker BuildKit (default in Docker 23+), WORKDIR behavior is consistent, but older Docker versions or alternative builders like Buildah may have subtle differences. If you encounter issues:
# Force BuildKit
DOCKER_BUILDKIT=1 docker build -t myimage .
# Or use buildx explicitly
docker buildx build -t myimage .### Container Runtime Differences
While standard Docker (moby/runc) creates WORKDIR directories automatically, alternative runtimes may behave differently:
- containerd (nerdctl): Generally follows Docker behavior
- Podman: Compatible behavior, creates directories
- Garden/Diego (Cloud Foundry): May not create non-existent WORKDIR (historical issue)
- Kubernetes with containerd: Depends on configuration
If deploying to environments with alternative runtimes, always ensure WORKDIR directories exist explicitly.
dockerfile parse error line 5: unknown instruction: RRUN
How to fix 'unknown instruction' Dockerfile parse error in Docker
Error response from daemon: manifest for nginx:nonexistent not found: manifest unknown: manifest unknown
How to fix 'manifest for image:tag not found' in Docker
Error response from daemon: invalid reference format: repository name must be lowercase
How to fix 'repository name must be lowercase' in Docker
Error response from daemon: No such image
How to fix 'No such image' in Docker
Error response from daemon: Container is not running
How to fix 'Container is not running' when using docker exec