---
title: Mastering Docker Commands: Build, Debug, Secure, and Deploy Containers
description: Four inspections before you delete a failing container: ps, logs, inspect, and port. Docker client 29.8.2 and Compose v5.5.1 on 11 October 2026.
url: https://www.factualminds.com/blog/mastering-docker-commands/
datePublished: 2026-10-11T00:00:00.000Z
dateModified: 2026-10-11T00:00:00.000Z
author: palaniappan-p
category: DevOps & CI/CD
tags: docker, containers, devops
---

# Mastering Docker Commands: Build, Debug, Secure, and Deploy Containers

> Four inspections before you delete a failing container: ps, logs, inspect, and port. Docker client 29.8.2 and Compose v5.5.1 on 11 October 2026.

On 11 October 2026 the lab script printed `client=29.8.2`, `compose=Docker Compose version v5.5.1`, and `daemon=down`. Client and Compose versions were observed. Commands that need a running daemon (`ps`, `build`, `run`) were **not** executed here. Their flags are from the [Docker CLI reference](https://docs.docker.com/reference/cli/docker/) and the [Compose reference](https://docs.docker.com/reference/cli/docker/compose/), checked the same day. Confirm them with `docker COMMAND --help` on an engine you are allowed to use.

Four inspections before you delete a failing container: `docker ps -a`, `docker logs`, `docker inspect`, and `docker port`. Deleting the container deletes the easiest copy of that evidence.

> **What broke** — `docker compose version` on this host still printed v5.5.1 while `docker info` could not open the socket. A Compose file that "works on my machine" was not tested, because there was no daemon. The failure mode is treating a client version check as proof the app starts.

> **Reproduce this** — Run `bash examples/architecture-blog-2026/mastering-developer-tools/mastering-docker-commands/check-client.sh`. Expected: a `client=` line, a `compose=` or `compose=unavailable` line, `daemon=up` or `daemon=down`, and `lab=ok`. The script does not prune. Published copy: [/examples/architecture-blog-2026/mastering-developer-tools/mastering-docker-commands/check-client.sh](/examples/architecture-blog-2026/mastering-developer-tools/mastering-docker-commands/check-client.sh).

We recommend a non-root user in the image and a pinned digest in the deploy manifest. The trade-off: some vendor images still run as root, and you then confine them with a read-only root filesystem and dropped capabilities instead of pretending `USER` is optional. Runtime policy on EKS is discussed in [container runtime security](/blog/container-runtime-security-seccomp-apparmor-eks-fargate/).

## Why Docker commands still matter

An agent can write a Dockerfile that builds locally and fails in staging because the port, the env file, or the health check differs. `inspect` shows the config the process actually received. `logs` shows why it exited. `image pull` of a tag without a digest shows you a moving target.

## Client, context, daemon

Context: Docker 29.8.2 client. This section's version commands were run. The rest of the article's daemon commands were not.

```bash
docker version
docker context ls
docker info
```

`docker version` prints client and, when the daemon answers, server. A client-only result means the socket is down or you lack permission. `docker context ls` shows which endpoint `docker` talks to. A context pointed at a remote engine is **remote mutation** for every later command. Read the active context before `run` or `prune`.

`docker --help` lists commands. `docker run --help` lists flags for one command. That help text is the source of truth for the engine you have. This page is not a replacement for it.

Install Docker Engine or Docker Desktop from Docker's docs for your OS. Do not copy a one-line install from a chat transcript. On Linux the daemon is a service. On Docker Desktop the daemon runs in a utility VM, which is why `df` inside a container is not your laptop's disk.

## Images and registries

| Task | Command | Risk |
| --- | --- | --- |
| Pull | `docker pull IMAGE` | **Local change**. Network. |
| List | `docker image ls` | **Read-only** |
| Inspect | `docker image inspect IMAGE` | **Read-only** |
| History | `docker image history IMAGE` | **Read-only**. Shows layers and often ENV. |
| Tag | `docker tag SOURCE TARGET` | **Local change** |
| Push | `docker push IMAGE` | **Remote mutation** |
| Login | `docker login REGISTRY` | Writes credentials to the Docker config file. |

`docker image inspect --format '{{.RepoDigests}}'` shows digests after a pull. Prefer deploying `image@sha256:DIGEST` when the orchestrator supports it. A tag such as `latest` or `staging` does not freeze the bytes.

`docker history` and `inspect` leak secrets that were baked into ENV. If you see a key there, rotate it and rebuild without that ENV. Build arguments have the same problem. BuildKit secrets (`docker build --secret`) are the mechanism Docker documents for build-time secrets. They are not the same as `ARG`. See [Dockerfile best practices](https://docs.docker.com/build/building/best-practices/).

ECR login is an AWS CLI step. The token is a secret. See [AWS CLI](/blog/mastering-aws-cli/).

## Container lifecycle

| Task | Command | Risk |
| --- | --- | --- |
| Run | `docker run` | **Local change**. Can publish ports. |
| List running | `docker ps` | **Read-only** |
| List all | `docker ps -a` | **Read-only** |
| Logs | `docker logs --tail 100 CONTAINER` | **Read-only** |
| Inspect | `docker inspect CONTAINER` | **Read-only** |
| Processes | `docker top CONTAINER` | **Read-only** |
| Stats | `docker stats --no-stream` | **Read-only** |
| Exec | `docker exec CONTAINER COMMAND` | Runs a command inside. Can be a mutation. |
| Port map | `docker port CONTAINER` | **Read-only** |
| Stop | `docker stop CONTAINER` | Sends SIGTERM, then SIGKILL. |
| Kill | `docker kill CONTAINER` | SIGKILL unless you set `--signal`. |
| Remove | `docker rm CONTAINER` | **Potentially destructive** for that container's writable layer. |
| Remove image | `docker image rm IMAGE` | **Local change** |

`docker ps -a` shows status and the exit code in the status column when the container has stopped. Exit code 0 is "the process ended because it finished." Exit code 137 often means SIGKILL (out of memory killer or `docker kill`). Exit code 1 is an application error. Read the logs before you interpret the code as proof.

`docker logs` shows the process stdout and stderr Docker captured. It does not show a file the app wrote to a volume unless you `exec` and read that file. A catalog API that logs to a file and not to stdout will look silent.

`docker inspect` is JSON. Useful fields: `State.ExitCode`, `State.Error`, `Config.Env`, `HostConfig.PortBindings`, `Mounts`, `State.Health`. Env values may be secrets. Redact before you share the JSON.

`docker exec` is a shell on that container. It is as powerful as the user inside the container. Prefer `exec` with a named command (`wget -q -O - http://127.0.0.1:8080/health`) over an interactive root shell.

Publishing a port (`-p 8080:8080`) binds a host port. **Local change** with network exposure. Check `docker port` and the host firewall. Do not publish a database port on a shared staging host to "test quickly."

## Build

`docker build` uses BuildKit on current Docker. `docker buildx build` is the explicit Buildx path for multi-platform images and for exporters.

```bash
docker buildx build --check -f Dockerfile .
docker buildx build --progress=plain -t catalog-api:lab .
```

`--check` runs Dockerfile checks and is the read you want before a long build. Confirmed as a Buildx feature in current Docker build docs. Run `docker buildx build --help` on your client because build flags move.

`.dockerignore` decides what the context sends. A context that includes `.git` or a data export makes slow, leaky images.

Multi-stage builds copy a built artifact into a smaller runtime image. That is how you drop compilers from production. The runtime stage still needs the CA certificates and libraries the binary actually uses. A missing shared library shows up as an immediate exit. `logs` and `inspect` show it. Rebuilding with `--no-cache` is a last resort after you know which layer is wrong, because it spends time and registry bandwidth.

`--platform linux/amd64,linux/arm64` builds more than one architecture. **Potential cost impact** on CI minutes. Do it when the cluster is mixed, not by default on a laptop.

## Networks, volumes, health

`docker network ls` and `docker network inspect NETWORK` are **read-only**. Compose project networks are the usual reason two services cannot resolve each other. The DNS name is the Compose service name on that network, not `localhost`, once the process is in another container.

`docker volume ls` and `docker volume inspect VOLUME` are **read-only**. `docker volume rm` deletes the volume if no container uses it. **Potentially destructive.** Data in that volume goes away.

A health check in the image or Compose file turns `State.Health` into something you can read. Without one, `docker ps` shows `Up` while the app is wedged. Add a check that hits the real endpoint, with a start period long enough for a JVM or a catalog warm-up.

Resource limits (`--memory`, `--cpus`) are how you keep a staging container from taking the laptop or the node. They are also how you reproduce an out-of-memory kill. Exit 137 plus a limit in `inspect` is a lead. It is not the only cause of 137.

## Compose

Current Compose is `docker compose` (a plugin), not the old `docker-compose` binary, unless you still have the standalone installed. This host's plugin reported v5.5.1.

| Task | Command | Risk |
| --- | --- | --- |
| Validate | `docker compose config` | **Read-only**. Prints the merged file, including env values. |
| Start | `docker compose up -d` | **Local change**. Can build. |
| Status | `docker compose ps` | **Read-only** |
| Logs | `docker compose logs --tail 100 SERVICE` | **Read-only** |
| Exec | `docker compose exec SERVICE COMMAND` | Runs in the container. |
| Stop | `docker compose stop` | Stops containers. Keeps them. |
| Remove | `docker compose down` | Removes containers and the project network. |
| Remove volumes | `docker compose down -v` | **Potentially destructive** |
| Profiles | `docker compose --profile TOOLS up` | Starts the services in that profile. |

`docker compose config` is the pre-flight. It expands variables. If a secret appears, fix the file before `up`. Multiple files: `docker compose -f compose.yml -f compose.staging.yml config`.

`down` without `-v` leaves volumes. `down -v` deletes named volumes declared in the file. Read `docker volume ls` first if the database lives in a volume.

## Cleanup

`docker system df` shows image, container, and volume disk use. **Read-only.** Run it before any prune.

| Command | What it can remove | Risk |
| --- | --- | --- |
| `docker container prune` | Stopped containers | **Potentially destructive** |
| `docker image prune` | Dangling images | **Local change** |
| `docker image prune -a` | Images not used by a container | **Potentially destructive** if you have not pushed them |
| `docker volume prune` | Volumes not referenced by a container | **Potentially destructive** |
| `docker system prune -a` | Unused images, stopped containers, unused networks | **Potentially destructive** |
| `docker system prune -a --volumes` | The above plus unused volumes | **Potentially destructive** |

There is a prompt unless you pass `--force`. An agent that adds `--force` skips the prompt. Read `docker system df` and `docker volume ls` yourself.

## Scenario: it works locally and fails in staging

Do this on the staging engine, after you confirm `docker context ls` is the staging context and not production.

1. `docker compose ps` or `docker ps -a`. Note status and exit code.
2. `docker logs --tail 200` for the API container and for the database or queue it calls.
3. `docker inspect` the API. Compare `Config.Env`, mounts, and port bindings with the local inspect. Redact secrets.
4. `docker port` and a `curl` from the host or from a sibling container. `localhost` inside the container is the container, not the host.
5. `docker top` if the process is up but slow. Pair with host `df` from the [Linux](/blog/mastering-linux-commands/) article if the node is short on disk.
6. Rebuild only after the diff between local and staging config is written down.

If staging is ECS or EKS, the Docker inspect is a local analog. The running copy might be a task or a pod. Continue in [AWS CLI](/blog/mastering-aws-cli/) or [Kubernetes](/blog/mastering-kubernetes-commands/).

## Security habits

- Run as a non-root `USER` when the image allows it.
- Minimal runtime base. Multi-stage so the compiler stays in the build stage.
- Do not put secrets in `ARG` or `ENV`.
- Publish only the ports the load balancer needs.
- Pin digests for anything you did not just build.
- Scan images with the scanner your registry already runs. A scan is evidence. A green check from an unscanned `latest` tag is not.
- Set memory and CPU limits before a load test.

## Working with a coding agent

Ask the agent to run `docker compose config` and `docker ps -a` and to quote the exit code. Refuse `system prune`, `volume prune`, `down -v`, and a published database port until you have seen `docker system df` and the context name. After a rebuild, `docker inspect` the new image id. "Rebuilt" is not a digest.

## Five labs

Use a daemon you are allowed to throw away. The version script does not need a daemon.

1. Run `check-client.sh`. Record client, Compose, and daemon lines.
2. With a daemon: `docker run --rm public.ecr.aws/docker/library/hello-world` or another image you trust. Expected: the hello message and no leftover container because of `--rm`. If you skip this, you have still finished lab 1.
3. `docker ps -a` and `docker system df` before you prune anything. Write down the reclaimable column. Do not prune on this pass.
4. `docker compose config` on a Compose file in a scratch directory with a public image and a health check. Expected: merged YAML and no secret values.
5. Start that project, `curl` the published port, read `docker logs`, then `docker compose down` without `-v` first. Run `down -v` only if the volume was created for this lab.

Progression: client and context, then logs and inspect, then build, then prune only after `docker system df`.

## What this post does not cover

Kubernetes scheduling, ECS task definitions in full, and image signing policy. Rootless mode and BuildKit cache backends are named in Docker's docs. Confirm the flags on your engine before you standardize on them.

## What to do this week

1. Run the client check on the laptop and on CI. Pin the major version you mean to support.
2. Add `docker compose config` to the review of any agent-written Compose file.
3. Find one service still deployed by tag alone and record whether a digest is available.
4. Delete nothing with `prune` until `docker system df` has been read once this week.

## Quick reference

| I need to | Command | Risk |
| --- | --- | --- |
| See if the daemon is up | `docker info` | **Read-only** |
| See stopped containers | `docker ps -a` | **Read-only** |
| Read why it exited | `docker logs` and `docker inspect` | **Read-only** |
| Preview Compose | `docker compose config` | **Read-only**, may print env |
| See disk use | `docker system df` | **Read-only** |
| Delete unused data | `docker system prune -a` | **Potentially destructive** |

You should be able to name the active context, read an exit code, and explain what `prune -a` and `down -v` remove.

## Further reading

- [Docker CLI](https://docs.docker.com/reference/cli/docker/)
- [Docker Compose CLI](https://docs.docker.com/reference/cli/docker/compose/)
- [Build best practices](https://docs.docker.com/build/building/best-practices/)
- Series: [Git](/blog/mastering-git-commands/), [Linux](/blog/mastering-linux-commands/), [AWS CLI](/blog/mastering-aws-cli/), [Kubernetes](/blog/mastering-kubernetes-commands/), [Bedrock CLIs](/blog/mastering-bedrock-cli/), [AI agent tools](/blog/mastering-ai-agent-tools/)

[Contact us](/contact-us/) or see [DevOps pipeline setup](/services/devops-pipeline-setup/) if staging and local images are drifting and nobody owns the digest.

## FAQ

### When should you not run docker system prune -a?
When you have not run docker system df, and when the machine holds images or volumes you cannot rebuild. prune -a removes unused images, not only dangling ones. Volume prune removes unused volumes and the data in them.


### Are Dockerfile build args a safe place for secrets?
No. Build args and ENV values are visible in image history and in inspect output. Use BuildKit secret mounts for build-time secrets, and inject runtime secrets from a manager the process reads, not from the image.


### What does a tag prove compared with a digest?
A tag is a mutable name. A digest (sha256) identifies the bytes you pulled. Production deploys should record the digest. A tag that worked yesterday can point at a different image today.


### The container exits immediately. What is the first command?
docker ps -a to see the status and exit code, then docker logs, then docker inspect for the entrypoint, env, and mounts. Do not docker rm until you have copied the evidence you need.


### Does this page list every Docker flag?
No. The complete reference is docs.docker.com/reference/cli/docker. This page is the daily set plus the commands that delete data.


---

*Source: https://www.factualminds.com/blog/mastering-docker-commands/*
