Docker basics — images, containers, ports, volumes, and debugging

Docker runs an application in an isolated process environment

Docker packages an application with its runtime files and starts it as an isolated process called a container. Containers share the host kernel, so they are lighter than full virtual machines, but they still receive their own filesystem view, process namespace, network interfaces, and resource limits.

The Docker CLI is a client. On Linux it normally sends API requests to the Docker daemon through a Unix socket; Docker Desktop provides a managed Linux environment on macOS and Windows. When a command fails, first decide whether the problem is client-to-daemon communication, image creation, or the process running inside a container.

An image is a template; a container is one execution of it

An image is an immutable stack of filesystem layers plus configuration such as the default command and environment. A container adds a writable layer and runtime state to an image. Removing a container does not remove its image, and rebuilding an image does not update containers that were already created from an older image.

Use explicit tags rather than relying on latest. A tag is a movable name, while an image digest identifies exact content. For repeatable deployment, record the tag or digest that was tested.

docker image ls
docker pull nginx:1.27-alpine
docker run --name web nginx:1.27-alpine
docker inspect web --format '{{.Image}}'

A Dockerfile creates image layers in a defined order

A Dockerfile starts from a base image and applies instructions such as WORKDIR, COPY, and RUN. Put stable dependency-installation steps before frequently changing source files so Docker can reuse cached layers. Keep secrets out of build arguments and copied files because image layers can preserve their contents.

Use a .dockerignore file to keep version-control data, local dependencies, credentials, and build output out of the build context. A smaller context improves build speed and reduces accidental data exposure.

docker build --tag my-app:dev .
docker image inspect my-app:dev
docker history my-app:dev

The container stops when its main process exits

The image entrypoint and command create process ID 1 inside the container. A server must remain in the foreground; if it daemonizes or crashes, the container stops. A stopped container still exists and can be inspected or restarted until it is removed.

Names make repeated inspection easier. Add --rm for disposable commands, but omit it while debugging so the stopped state and logs remain available.

docker run --name my-app -d my-app:dev
docker ps
docker logs my-app
docker inspect my-app --format 'status={{.State.Status}} exit={{.State.ExitCode}}'
docker stop my-app
docker rm my-app

Publishing a port connects a host port to a container port

A process listening inside a container is not automatically reachable from the host. -p 8080:3000 publishes host port 8080 to container port 3000. The application must listen on 0.0.0.0 inside the container rather than only 127.0.0.1.

Containers on the same user-defined network can reach each other by container or service name. Inside one container, localhost refers to that container itself, not the host and not another service.

docker network create app-net
docker run -d --name api --network app-net my-api:dev
docker run -d --name web --network app-net -p 8080:3000 my-web:dev
docker port web

Use volumes or bind mounts for data that must outlive a container

Changes in a container writable layer disappear when that container is removed. A named volume is managed by Docker and is a good default for database or application state. A bind mount maps an explicit host path and is useful for source code and local configuration during development.

Mounts can hide files that were present at the same path in the image. If an application suddenly cannot find built-in files, inspect its mounts before rebuilding the image.

docker volume create app-data
docker run -d --name db -v app-data:/var/lib/postgresql/data postgres:17
docker inspect db --format '{{json .Mounts}}'

Inspect the layer that failed before applying a cleanup command

Start with docker version when the daemon may be unreachable. For build failures, read the first failing Dockerfile step and rebuild with plain progress output. For runtime failures, preserve the container and inspect its state, logs, command, environment, and mounts. Entering a running container is useful only after its main process stays alive.

List resources before pruning them

Docker can accumulate stopped containers, unused images, build cache, and volumes. Prune commands are convenient but broad. Inspect disk usage and use a dry, resource-specific cleanup approach first. Volumes can contain the only copy of application data, so never include them in cleanup without a verified backup.

docker system df
docker ps -a
docker image ls
docker volume ls
docker container prune
docker image prune