Docker on the CLI

It is always useful to be able to do your work from the command line.

For some time now, Docker Desktop have been dulling the collective common knowledge about how to use docker on the command line.

Let us address this knowledge gap - while avoiding the basics - with a Housekeeping section before we dive into Tools.

Housekeeping

For general housekeeping we need to check which images and containers exist, monitor their resource usage, and clean up any unnecessary or unused items.

A Good Tip

As quick aside I need to mention that a lot of housekeeping can be avoided with the option --rm for instance when running an image:

% docker run --rm hello-world

The option --rm effectively cleans up after itself.

Automatically remove the container and its associated anonymous volumes when it exits

Listing

% docker ps # alias for `docker container ls`
CONTAINER ID   IMAGE               COMMAND                  CREATED      STATUS      PORTS                      NAMES
90217dfd7c27   hugomods/hugo:git   "docker-entrypoint.s…"   7 days ago   Up 7 days   127.0.0.1:1313->1313/tcp   codereapergithubio-render-run-d1f5fc2bea43

% docker images # alias for `docker image ls`
REPOSITORY                TAG          IMAGE ID       CREATED         SIZE
hugomods/hugo             git          cd0d7c15f208   3 weeks ago     99.2MB
hello-world               latest       f1f77a0f96b7   5 weeks ago     5.2kB

Resource Usage

% docker stats --no-stream
CONTAINER ID   NAME                                         CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O   PIDS
90217dfd7c27   codereapergithubio-render-run-d1f5fc2bea43   1.33%     47.91MiB / 1.914GiB   2.45%     2.05MB / 26.2MB   0B / 0B     40

% docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          4         1         192.9MB   101.9MB (52%)
Containers      1         1         0B        0B
Local Volumes   0         0         0B        0B
Build Cache     5         0         199B      199B

The option --no-stream can be omitted to use the stats subcommand as a top command.

Disable streaming stats and only pull the first result

Cleaning Up

Note that following command will remove everything not currently in use without confirmation.

% docker system prune -af --volumes
Deleted Containers:
... skipped ...

Deleted Images:
... skipped ...

Deleted build cache objects:
... skipped ...

Total reclaimed space: 93.68MB

Tools

We will not be installing any tools, but instead we are going to run the tools using docker run commands. That way we only need to maintain an alias file or some similar.

CVE Scanning

It can be good to have options for scanning your images, so let us take a look at two scanning tool options.

Grype

% grype python:alpine
NAME    INSTALLED  FIXED-IN  TYPE    VULNERABILITY  SEVERITY
python  3.13.2     3.14.0    binary  CVE-2024-3220  Unknown
Show alias
alias grype='
mkdir -p /tmp/grype && \
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/grype:/tmp \
-e GRYPE_DB_CACHE_DIR=/tmp/grype \
anchore/grype $@'

Trivy

% trivy python:alpine
2025-03-30T15:50:42Z	INFO	[vuln] Vulnerability scanning is enabled
2025-03-30T15:50:42Z	INFO	[secret] Secret scanning is enabled
2025-03-30T15:50:42Z	INFO	[secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-03-30T15:50:42Z	INFO	[secret] Please see also https://trivy.dev/v0.61/docs/scanner/secret#recommendation for faster secret detection
2025-03-30T15:50:42Z	INFO	Detected OS	family="alpine" version="3.21.3"
2025-03-30T15:50:42Z	INFO	[alpine] Detecting vulnerabilities...	os_version="3.21" repository="3.21" pkg_num=28
2025-03-30T15:50:42Z	INFO	Number of language-specific files	num=1
2025-03-30T15:50:42Z	INFO	[python-pkg] Detecting vulnerabilities...

Report Summary

┌──────────────────────────────────────────────────────────────────────┬────────────┬─────────────────┬─────────┐
│                                Target                                │    Type    │ Vulnerabilities │ Secrets │
├──────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼─────────┤
│ python:alpine (alpine 3.21.3)                                        │   alpine   │        0        │    -    │
├──────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼─────────┤
│ usr/local/lib/python3.13/site-packages/pip-24.3.1.dist-info/METADATA │ python-pkg │        0        │    -    │
└──────────────────────────────────────────────────────────────────────┴────────────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)
Show alias
alias trivy='
mkdir -p /tmp/trivy && \
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/trivy:/root/.cache/ \
aquasec/trivy image $@'

Explore Image Contents

Dive

Allows you to view each layers command, directories and files which can be very helpful in a debugging situation. This is an interactive tool, so it is hard to show the output from dive therefore I am borrowing their own introduction gif:

Animation showing the functions in dive

Show alias
alias dive='
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/wagoodman/dive $@'

Note you cannot view the contents of a file (yet - see #336).

SBOMs with Syft

Syft is an easy-to-use tool for generating Software Bill of Materials for container images (and filesystems).

% syft alpine
NAME                    VERSION      TYPE
alpine-baselayout       3.6.8-r1     apk
alpine-baselayout-data  3.6.8-r1     apk
alpine-keys             2.5-r0       apk
alpine-release          3.21.3-r0    apk
apk-tools               2.14.6-r3    apk
busybox                 1.37.0-r12   apk
busybox-binsh           1.37.0-r12   apk
ca-certificates-bundle  20241121-r1  apk
libcrypto3              3.3.3-r0     apk
libssl3                 3.3.3-r0     apk
musl                    1.2.5-r9     apk
musl-utils              1.2.5-r9     apk
scanelf                 1.3.8-r1     apk
ssl_client              1.37.0-r12   apk
zlib                    1.3.1-r2
Show alias
alias syft='docker run --rm ghcr.io/anchore/syft $@'

Reverse-engineer a Dockerfile with dfimage

Take a peak at how others have made their Dockerfile.

% dfimage aquasec/trivy
FROM alpine:latest
RUN /bin/sh -c apk --no-cache add ca-certificates git # buildkit
COPY trivy /usr/local/bin/trivy # buildkit
COPY contrib/*.tpl contrib/ # buildkit
ENTRYPOINT ["trivy"]
Show alias
alias dfimage='
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/laniksj/dfimage $@'

Missing Out

You might be wondering, what are we missing out on by not using Docker Desktop and you are missing three things:

Docker Scout

Scout can present you with an assessment score of an images vulnerabilities. This score is meaningless if you ignoring the “high number, bad” and “low number, good” arguments. You still need evaluate the risks of your application, its use-cases and risk profile.

You have tools above to generate a list of software and versions used in an image or a list of vulnerabilities to evaluate.

Docker Cloud

Docker Build Cloud and Testcontainers Cloud are both … ridiculous. They are marketed as time-saving tools and they are both the ability to do docker build and docker run, but in the cloud.

Your builds and tests should not take long, and the solution is not to send your code to a third-party and have them run it.

If - and there should not be a need for it - you really need to offload building and testing from the developers machine, use your own build server, or a free one from GitHub.

Docker Debug

This is the ability to have a shell on a distroless image. This could be really useful.

The dive tool mentioned above should be enough, especially once we have the ability to view the contents of a file.

Setting Docker free »