In short
A sloppy Dockerfile gives you 1 GB images, 10-minute builds and a container running as root. A tidy one is small, fast to rebuild and safer, with the same app inside.
The instructions for building a Docker image — writing an efficient, secure Dockerfile involves minimizing layers, using specific base image versions, and not running as root by default.

Think of it like this
The caterer's recipe card
A good wedding caterer doesn't write 'add some masala'. The card says which brand, how many spoons, in what order. Anyone in the kitchen gets the same biryani. A Dockerfile is that card: exact versions, exact steps, same image every time.
Watch it happen
Same app, two Dockerfiles
Sloppy
- FROM node:latest, full Debian image
- COPY . . before npm install
- Runs as root, ships dev tools
Tidy
- FROM node:22-alpine, pinned and small
- Copy package files first, cache npm ci
- USER node, production deps only
Same code inside, but the tidy image is smaller, rebuilds in seconds after a code change and gives an attacker far less to work with.
The big ideas
What you need to know
Pin a small base
Use a specific tag like node:22-alpine or a slim image. Smaller bases mean faster pulls and fewer packages with known holes.
Order for the cache
Put things that rarely change (installing dependencies) above things that change often (your source code).
Use .dockerignore
Exclude node_modules, .git, .env and build output so they never enter the build context or the image.
Don't run as root
Switch to a normal user with USER. If the app is hacked, the attacker gets far fewer powers inside the container.
See it in code
Walk through the code
1FROM node:22-alpine2WORKDIR /app3 4# Dependencies first, so this layer is cached5COPY package.json package-lock.json ./6RUN npm ci --omit=dev7 8# App code changes often, so copy it last9COPY . .10 11# Don't run as root12USER node13EXPOSE 300014CMD ["node", "server.js"]1/4A pinned, small base image and a fixed working directory for every later step.
Pair this with a .dockerignore containing node_modules, .git and .env. The official node images already include a non-root user called node.
Save yourself the pain
Common mistakes
Oops
Baking secrets into the image with COPY .env or ENV API_KEY=....
Do this instead
Pass secrets at runtime through environment variables or a secrets manager. Anyone who pulls the image can read its layers.
Oops
Writing CMD npm start in shell form and wondering why the container takes 10 seconds to stop.
Do this instead
Use exec form, CMD ["node", "server.js"], so your app is PID 1 and gets the stop signal directly.
Your mission
Shrink an image
Quick check
Why copy package.json before the rest of the source code?
Go deeper
- 1Write a Dockerfile for a simple application and build an image from it.
- 2Learn why pinning a specific base image version (not `latest`) matters for reproducibility.
- 3Practice reducing image size by cleaning up unnecessary files in the same layer they were created.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.