Docker
Dockerfile
ENV instruction
PWD variable
container environment

PWD is not set in ENV instruction in a Dockerfile

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

PWD often behaves the way developers expect inside an interactive shell, so it is confusing when ENV CURRENT_DIR=$PWD in a Dockerfile does not work. The reason is that ENV is not executed by a shell in the way RUN is, and Dockerfile variable expansion only knows about values that already exist in the Docker build context for that instruction. In practice, WORKDIR, ARG, and runtime shell evaluation solve different parts of this problem.

Why PWD Is Not Available the Way You Expect

A Dockerfile has several instruction types, and they do not all evaluate variables the same way.

  • 'RUN executes a command in a shell-like environment, depending on the form you use.'
  • 'ENV sets image environment variables.'
  • 'WORKDIR sets the current working directory for later build steps and at container start.'

This often surprises people:

dockerfile
FROM alpine:3.20
ENV CURRENT_DIR=$PWD

Here, Docker is not asking a shell to expand the current directory the way your terminal would. Unless PWD is already defined as an environment variable available to that Dockerfile instruction, the expansion will not give you the shell current-directory value you had in mind.

Use WORKDIR for Directory Intent

If what you really want is "this image should work from /app," use WORKDIR.

dockerfile
1FROM alpine:3.20
2WORKDIR /app
3COPY . .
4CMD ["sh", "-c", "echo Working directory is: $PWD && ls"]

WORKDIR is the correct tool because it changes where later instructions run and what the container uses as its current directory by default.

If you build and run this image, the shell in CMD prints /app because the container process starts there.

If You Need a Variable, Set It Explicitly

If you want an environment variable representing a known path, declare that path directly:

dockerfile
1FROM alpine:3.20
2WORKDIR /app
3ENV APP_DIR=/app
4CMD ["sh", "-c", "echo APP_DIR=$APP_DIR; echo PWD=$PWD"]

This is clearer than trying to infer the path indirectly through PWD.

At runtime:

  • 'APP_DIR is an image environment variable you defined.'
  • 'PWD is provided by the shell process that starts in /app.'

Those are related, but they come from different mechanisms.

Passing Host-Side Paths Is a Separate Problem

Sometimes the real goal is not the container working directory, but the host directory from which docker build was invoked. Docker does not automatically expose your host shell PWD into the image build as a magical default.

If you truly need that value, pass it in with a build argument:

dockerfile
1FROM alpine:3.20
2ARG HOST_PWD
3ENV HOST_PWD=$HOST_PWD
4CMD ["sh", "-c", "echo Host build directory was: $HOST_PWD"]

Build command:

bash
docker build --build-arg HOST_PWD="$PWD" -t demo-image .

That works because you explicitly injected the host value. Docker did not infer it for you.

RUN pwd Versus ENV CURRENT_DIR=$PWD

This distinction helps:

dockerfile
FROM alpine:3.20
WORKDIR /app
RUN pwd

RUN pwd prints the current working directory during that build step because it runs as a command.

By contrast, this instruction is just variable assignment using Dockerfile expansion rules:

dockerfile
ENV CURRENT_DIR=$PWD

Those are not equivalent. One executes a program. The other records an environment variable in image metadata.

Prefer Explicit Paths Over Implicit Shell State

Images are more maintainable when their important paths are explicit. WORKDIR /app communicates intent directly. ENV APP_DIR=/app communicates it again for application code that wants a variable. PWD is useful inside running shells, but it is not a strong design anchor for Dockerfile structure.

Common Pitfalls

  • Assuming every Dockerfile instruction expands variables the same way a shell does.
  • Using ENV when the real need is WORKDIR.
  • Expecting the host terminal PWD value to appear automatically inside the image build.
  • Hiding important directory choices behind implicit shell state instead of explicit paths.
  • Confusing build-time behavior with runtime shell behavior inside a started container.

Summary

  • 'ENV does not magically evaluate shell PWD the way an interactive terminal does.'
  • Use WORKDIR to set the image and container working directory.
  • Set path-like environment variables explicitly if your application needs them.
  • Pass host-side directory values with ARG when that information is genuinely required.
  • Prefer explicit Dockerfile paths over implicit assumptions about shell state.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.