Docker’s value is less about virtualisation and more about writing an environment down as code. Half a day of local setup for a new teammate becomes one line: docker compose up.

Install

  brew install --cask docker          # Docker Desktop
brew install --cask orbstack        # a lighter alternative on macOS
  

On Linux, follow your distribution’s official docker-ce instructions.

Three concepts

ConceptMeaning
ImageA read-only snapshot of an environment
ContainerA running instance of an image
VolumeStorage that survives the container

Basic commands

  docker run -d --name pg -p 5432:5432 \
  -e POSTGRES_PASSWORD=secret postgres:16

docker ps                    # running containers
docker ps -a                 # including stopped ones
docker logs -f pg            # follow the logs
docker exec -it pg psql -U postgres    # run a command inside
docker stop pg && docker rm pg
  
FlagMeaning
-dRun in the background
-p host:containerPublish a port
-e KEY=VALUEEnvironment variable
-v hostpath:containerpathMount a volume
--rmDelete on exit
-itInteractive terminal

Writing a Dockerfile

  # stage 1: build
FROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# stage 2: run, without the build tooling
FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
  

Two things carry most of the benefit:

  • Layer caching. Copy what changes rarely (dependencies) first and what changes often (source) last. Reordering alone can make builds several times faster.
  • Multi-stage builds. Build tools never reach the final image, which shrinks both the size and the attack surface.

Don’t forget .dockerignore:

  node_modules
.git
dist
*.log
.env
  

Build and run

  docker build -t myapp:dev .
docker run --rm -p 3000:3000 --env-file .env myapp:dev
  

Cleaning up

Containers and images quietly eat disk.

  docker system df           # check usage
docker container prune     # remove stopped containers
docker image prune -a      # remove unused images
docker system prune -a --volumes    # everything, volumes included (careful)
  

Frequent problems

SymptomCause and fix
Port conflictSomething already holds it. Check with lsof -i :5432
Edits don’t show upThe source was copied into the image. Mount it in dev: -v $(pwd):/app
Fails to run on Apple SiliconSpecify --platform linux/amd64
Container exits immediatelyNo foreground process. Check docker logs

Next

To bring several services up at once → Docker Compose

Last updated 19 Aug 2026, 00:00 UTC. history