Irbisa · Cheatsheet

Docker & Containers

Images, layers and the build cache, multi-stage Dockerfiles for Go, scratch and distroless, Compose for local development, volumes and networking, signals and PID 1, image security, multi-arch builds

Middle Developer · 18 questions

  1. 01

    What is a container, how does it differ from a virtual machine, and what do namespaces and cgroups give it?

    A container is an ordinary Linux process that the kernel isolates: namespaces give it its own view of the system, and cgroups limit how much CPU and memory it may use.

  2. 02

    What is the difference between an image, a container and a registry, and what is an image layer?

    An image is a read-only stack of filesystem layers plus a config (command, environment, user); a container is a running instance of an image with a thin writable layer on top; a registry stores images by name and tag.

    # one layer per build step that changed files; ENTRYPOINT only sets config$ docker image history --format 'table {{.CreatedBy}}\t{{.Size}}' registry.example.com/api:1.4.0CREATED BY                            SIZEENTRYPOINT ["/api"]                   0BCOPY /out/api /api # buildkit         10.7MBbazel build //common:cacerts_deb...   319kB…
  3. 03

    What do RUN, CMD and ENTRYPOINT do in a Dockerfile, and why does the exec form ["/api"] matter?

    RUN executes at build time and saves a layer; ENTRYPOINT (the program) and CMD (its default arguments, replaced by whatever follows the image name in docker run) act when the container starts.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./# RUN: executed at build time; its file changes are saved as a layerRUN go mod download…
  4. 04

    What is the difference between EXPOSE and -p 8080:8080, and why is a Go server listening on 127.0.0.1:8080 unreachable through a published port?

    EXPOSE only documents the port the app listens on; it opens nothing.

    package main import (	"log"	"net/http")…
  5. 05

    What is the difference between a named volume and a bind mount, where should Postgres keep its data in Compose, and what does docker compose down -v delete?

    Both keep data outside the container's writable layer, so it survives docker rm.

    services:  postgres:    image: postgres:17-alpine    environment: {POSTGRES_USER: app, POSTGRES_PASSWORD: app, POSTGRES_DB: app}    volumes:      # named volume: managed by Docker, survives down, removed by down -v…
  6. 06

    How do you write a production Dockerfile for a Go service, and what breaks when the binary runs on scratch?

    Use a multi-stage build: compile a static binary with CGO_ENABLED=0 in a golang image, then copy only that binary into distroless/static or scratch.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download                 # cached until go.mod/go.sum changeCOPY . .…
  7. 07

    Find the bug: after a one-line change in a handler, docker build downloads every module again and recompiles the whole dependency tree.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY . .RUN go mod downloadRUN CGO_ENABLED=0 go build -o /out/api ./cmd/api FROM gcr.io/distroless/static-debian12:nonrootCOPY --from=build /out/api /apiENTRYPOINT ["/api"]

    COPY . . comes before go mod download, so a change to any file invalidates that layer and every layer after it, the download included.

  8. 08

    What is the Docker build context, and what goes wrong when a Go repository has no .dockerignore?

    The build context is the directory passed to docker build (the final .), and the only host files COPY can read.

    # .dockerignore at the root of the build context, allowlist style:# exclude everything...*# ...then re-include only what the Go build reads!go.mod!go.sum…
  9. 09

    An image built on an Apple Silicon Mac fails on an amd64 server with exec /api: exec format error. Why, and how do you build one image for both architectures?

    A Go binary is machine code for one CPU architecture, and on an M-series Mac docker build produces a linux/arm64 image by default, which an amd64 kernel cannot run.

    # syntax=docker/dockerfile:1# The build stage runs natively on the builder's own platform: no emulation.FROM --platform=$BUILDPLATFORM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download…
  10. 10

    Why is deploying :latest a bad idea, and what is the difference between an image tag and a digest?

    A tag is a movable name in the registry, and latest is just the default tag, not "the newest": what it points to can change between two pulls.

    # syntax=docker/dockerfile:1# wrong: a moving tag, a different image after every Go patch or Debian update#   FROM golang:1.25 AS build# right: the tag is for humans; the digest decides what is pulledFROM golang:1.25.14@sha256:699337d620559a59b4a2bb298ad59611e535d2ee755a34cf2d2a98f37578dc80 AS buildWORKDIR /src…
  11. 11

    The Alpine-based container starts via entrypoint.sh, which reads a secret and runs /app/server. Pods now take the full 30 s to stop and never log a shutdown. Why?

    The script is PID 1 and the Go binary is its child, so the kubelet's SIGTERM goes to sh, which neither forwards it nor, as PID 1 without a handler, even dies of it.

    #!/bin/sh# entrypoint.shset -euDB_PASSWORD="$(cat /run/secrets/db-password)"export DB_PASSWORD…
  12. 12

    Why should a Go container run as a non-root user, and how do you set that up on scratch or distroless?

    Without user namespaces, UID 0 in a container is UID 0 on the host, held back only by dropped capabilities and seccomp, so a compromised root process is one kernel or runtime bug from the node.

    # Dockerfile: USER 65532:65532 (numeric, so runAsNonRoot can verify it)spec:  securityContext:    runAsNonRoot: true    runAsUser: 65532    runAsGroup: 65532…
  13. 13

    Why must secrets never be baked into a container image, and how should a Go service get them instead, at build time and at runtime?

    Every layer of an image is readable by anyone who can pull it, and deleting a file or overriding a variable in a later layer does not remove it from the earlier one.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcENV GOPRIVATE=github.com/acme/*COPY go.mod go.sum ./# wrong: ARG GITHUB_TOKEN, then RUN git config ... $GITHUB_TOKEN…
  14. 14

    How do you set up local development for a Go service with Postgres and Redis in Docker Compose, and how do you make the service wait until the database is ready?

    One compose.yaml: the service built from its Dockerfile, Postgres and Redis from official images, a named volume for the data.

    services:  api:    build: .    ports: ["8080:8080"]    environment:      DATABASE_URL: postgres://app:app@postgres:5432/app?sslmode=disable…
  15. 15

    Find the bug: the API reaches Postgres when started with go run, but in Compose it exits with dial tcp 127.0.0.1:5432: connect: connection refused.

    services:  api:    build: .    ports:      - "8080:8080"    environment:      DATABASE_URL: postgres://app:app@localhost:5432/app?sslmode=disable    depends_on:      postgres:        condition: service_healthy  postgres:    image: postgres:17-alpine    environment: {POSTGRES_USER: app, POSTGRES_PASSWORD: app, POSTGRES_DB: app}    ports:      - "5432:5432"    healthcheck:      test: ["CMD-SHELL", "pg_isready -U app -d app"]      interval: 2s

    Inside a container localhost is the container itself, and nothing in the api container listens on 5432.

  16. 16

    How do you secure a Go service's container image beyond running as non-root: scanning, SBOMs, signing and runtime restrictions?

    Ship as little as possible, know what is inside, keep it patched, prove where it came from, and limit what the running container may do.

    # 1. scan: OS packages plus the Go modules and stdlib recorded in the binary$ trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 \    registry.example.com/api:1.4.0 # 2. Go-aware triage: which vulnerable functions are linked into the binary#    (needs symbols: scan a build made without -ldflags=-s)…
  17. 17

    The service runs on distroless, so docker exec -it api sh fails with executable file not found. How do you debug the running container?

    Bring the tools from outside instead of shipping them in the image: a debug container that joins the target's process and network namespaces.

    $ docker exec -it api shOCI runtime exec failed: exec failed: unable to start container process:exec: "sh": executable file not found in $PATH # Docker: a toolbox container in the same PID and network namespaces$ docker run --rm -it --pid=container:api --network=container:api \…
  18. 18

    What is the difference between Docker, containerd, runc and Podman, and what changed for your images when Kubernetes removed dockershim in 1.24?

    They are layers of one stack: dockerd builds and manages, containerd pulls images and runs containers, runc creates the process with namespaces and cgroups.