DockerfileImage → running Container. Images follow OCI format: portable across Docker, Podman, containerd, Kubernetes.

Dockerfile: key instructions

FROM node:18-alpine          # base image (alpine = small)
WORKDIR /app                 # sets working dir for subsequent cmds
COPY package*.json ./        # copy dependency files first (cache layer)
RUN npm ci --only=production # install deps
COPY . .                     # copy source code
EXPOSE 3000                  # document port (does NOT publish it)
ENV NODE_ENV=production      # set env var at build + runtime
ARG BUILD_VERSION=1.0        # build-time variable only (not in final image)
ENTRYPOINT ["node"]          # fixed executable: cannot be overridden by args
CMD ["server.js"]            # default args: overridable at runtime

Multi-stage build

# Stage 1: build
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Stage 2: minimal runtime image
FROM gcr.io/distroless/base
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
# Final image has NO shell, no build tools: very small & secure

.dockerignore

.git
node_modules
*.md
Dockerfile

Build and tag

docker build -t myapp:1.0 .
docker build -f docker/Dockerfile.prod -t myapp:1.0 .
docker build --build-arg VERSION=2.0 -t myapp:2.0 .
docker build -t myapp:latest -t myapp:v1.0 -t registry.local/myapp:v1.0 .

Retag existing images

docker tag does not copy image data: new name pointing at same image ID.

docker tag myapp:1.0 registry.io/user/myapp:1.0
docker tag nginx:1.25 myregistry.local/nginx:prod
docker images    # same IMAGE ID, different REPOSITORY:TAG

Registry: pull and push

docker pull nginx:1.25
docker push registry.io/user/myapp:1.0
docker tag myapp:1.0 myregistry.local:5000/myapp:1.0
docker push myregistry.local:5000/myapp:1.0

Save and load (OCI image tar)

docker save exports image layers + metadata. docker load restores it.

docker save nginx:1.25 -o nginx.tar
docker save -o bundle.tar nginx:1.25 busybox:1.36

docker load -i nginx.tar
docker images | grep nginx

Typical exam workflow:

  1. docker pull or docker build
  2. docker save -o image.tar <name>:<tag>
  3. docker load -i image.tar
  4. Deploy with imagePullPolicy: Never
spec:
  containers:
  - name: app
    image: myapp:1.0
    imagePullPolicy: Never

save/load vs export/import

CommandSourceOutputRestores
docker saveImage (layers + metadata)OCI image tardocker load → full image with history
docker loadImage tar from saveLocal image:
docker exportRunning container filesystemFlat filesystem tarNo layer history
docker importContainer export tarNew image (no Dockerfile history):

Inspect and debug images

docker images
docker history myapp:1.0               # layers and sizes
docker inspect myapp:1.0               # full metadata (ENTRYPOINT, ENV, etc.)
docker rmi myapp:1.0
docker image prune -f                  # remove dangling images

Run containers (local testing)

docker run -d -p 8080:80 nginx:1.25
docker run --rm -it alpine ping -c 3 8.8.8.8
docker ps / docker ps -a
docker logs <id> / docker logs -f <id>
docker exec -it <id> /bin/sh
docker stop <id> && docker rm <id>

Podman / nerdctl equivalents

Taskdockerpodmannerdctl
Builddocker build -t app:1.0 .podman build -t app:1.0 .nerdctl build -t app:1.0 .
Savedocker save -o app.tar app:1.0podman save -o app.tar app:1.0nerdctl save -o app.tar app:1.0
Loaddocker load -i app.tarpodman load -i app.tarnerdctl load -i app.tar
Tagdocker tag src dstpodman tag src dstnerdctl tag src dst
Pushdocker push dstpodman push dstnerdctl push dst

Run which docker podman nerdctl at the start of any image task.

CMD vs ENTRYPOINT

OverrideRuntime args
CMDAny docker run arg replaces CMDReplace entirely
ENTRYPOINT--entrypoint onlyAppended to ENTRYPOINT
Both--entrypoint + argsArgs override CMD default

Kubernetes mapping

DockerK8s Pod field
ENTRYPOINTcommand
CMDargs

Override in Pod spec

spec:
  containers:
  - name: app
    image: myapp:1.0
    command: ["python"]        # overrides ENTRYPOINT
    args: ["-u", "server.py"]  # overrides CMD
kubectl run mypod --image=busybox:1.36.1 -o yaml --dry-run=client \
  -- /bin/sh -c "while true; do date; sleep 10; done"
# everything after -- → args

imagePullPolicy

PolicyBehaviour
IfNotPresentPull only if not already cached
AlwaysPull on every Pod start
NeverUse only local image; fail if not present

Defaults: :latest (or omitted) → Always; any other tag → IfNotPresent

spec:
  containers:
  - name: app
    image: myapp:1.0
    imagePullPolicy: IfNotPresent
  • ImagePullBackOff → wrong tag, missing registry auth, or Never with no local image

Exam gotchas

  • command = ENTRYPOINT, args = CMD
  • :latestimagePullPolicy: Always by default; pin a version for IfNotPresent
  • "Save image to tar" = docker save, not docker export
  • After docker load, set imagePullPolicy: Never (or IfNotPresent)
  • docker tag only adds a name: to change an image, edit Dockerfile and rebuild
  • Check which CLI is installed: which docker podman nerdctl
  • Layer caching: fix Dockerfile errors and rebuild; cached layers before the failure are reused