Container Basics
This page is a one-page tour of the core objects you touch every day - images, containers, registries, and the orchestrator - and how they connect.
Every example is minimal and runnable so you can build the mental map quickly.
Prerequisites
- Docker Engine 29.6.1 with BuildKit (default) for building and running locally.
- Compose v2 (
docker compose, notdocker-compose) for multi-container dev. - kubectl matching your cluster (Kubernetes 1.36.2 here) for the orchestrator examples.
- A local cluster (kind, minikube, or k3d) if you want to run the pod examples.
Quick check that your toolchain is live:
docker version --format '{{.Server.Version}}'
kubectl version --client -o yaml | grep gitVersionBasic Examples
1. Build an image from a Dockerfile
An image is a read-only, layered filesystem plus config; a Dockerfile is the recipe.
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
CMD ["node", "server.js"]FROMpicks a base layer; each instruction adds a cache-friendly layer on top.COPY package*.jsonbefore the source letsnpm cistay cached when only app code changes.USER noderuns the app as a non-root user, a security default worth setting early.CMDis the default process; it becomes PID 1 in the container.
2. Build and tag it
Tags name an image so you and the registry can find it.
docker build -t myapp:1.0.0 .
docker images myapp-t name:tagsets a human tag; prefer semantic, immutable tags overlatest.- The build runs on BuildKit by default, which parallelizes and caches layers.
- The same content also has an immutable digest (
sha256:...) you can pin later.
3. Run a container
A container is a running instance of an image - an isolated process with that image's filesystem.
docker run --rm -p 8080:3000 --name web myapp:1.0.0-p 8080:3000maps host port 8080 to the container's port 3000.--rmdeletes the container on exit so you do not accumulate stopped ones.--name webgives it a stable name fordocker logs webanddocker exec.
4. Inspect and interact
You debug containers by looking at logs and stepping inside.
docker ps
docker logs web
docker exec -it web shdocker pslists running containers; add-ato see stopped ones.docker logsstreams stdout/stderr, which is where a good container writes everything.docker exec -it ... shopens a shell in the live container for inspection.
5. Push to a registry
A registry stores and distributes images so other machines and clusters can pull them.
docker tag myapp:1.0.0 registry.example.com/team/myapp:1.0.0
docker push registry.example.com/team/myapp:1.0.0- The tag's host prefix (
registry.example.com) tells Docker where to push. - Registries include Docker Hub, GHCR, ECR, GAR, and self-hosted Harbor.
- Nodes later pull this same reference; keep names consistent across environments.
6. Persist data with a volume
Container filesystems are ephemeral, so durable data lives in volumes.
docker volume create appdata
docker run --rm -v appdata:/var/lib/app myapp:1.0.0- A named volume outlives the container and survives restarts and re-creates.
- Anything written outside the volume vanishes when the container is removed.
- In Kubernetes the same idea appears as PersistentVolumes and PersistentVolumeClaims.
7. Wire up services with Compose
Compose v2 runs several containers together for local development.
services:
web:
build: .
ports:
- "8080:3000"
depends_on:
- db
db:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: devsecret
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:docker compose upbuilds and starts everything on a shared network.- Services reach each other by service name (
db) over that network. - Compose is a dev and CI tool; the orchestrator handles production.
Intermediate Examples
8. Run the image as a Kubernetes Pod
The orchestrator schedules containers onto nodes and keeps them running.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: registry.example.com/team/myapp:1.0.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
memory: "256Mi"- The kubelet pulls the image and asks containerd (via the CRI) to run it - not Docker.
requestsdrive scheduling;limitsbecome cgroup ceilings that cap the process.- A bare Pod is for learning; production uses a Deployment to manage replicas.
9. Manage replicas with a Deployment
A Deployment keeps a desired number of Pods running and handles rollouts.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.example.com/team/myapp:1.0.0
readinessProbe:
httpGet:
path: /healthz
port: 3000replicas: 3tells the controller to maintain three Pods at all times.- The
readinessProbegates traffic until the container reports healthy. - Updating the image triggers a rolling update, replacing Pods a few at a time.
10. Expose it with a Service
A Service gives a stable virtual IP and DNS name in front of changing Pods.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 3000- The
selectormatches Pod labels, so the Service tracks Pods as they come and go. - Other Pods reach it at
web(orweb.namespace.svc) via cluster DNS. - For external traffic, add an Ingress or a Gateway API route in front.
The connective tissue is one loop: a Dockerfile builds an image, a registry stores it, a runtime turns it into a container, and the orchestrator schedules and heals those containers across nodes.
Learn these five nouns well and the rest of the platform is composition on top of them.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).