DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    DockerLesson 12 of 14
    Your progress · 0%
    On this page
    1. One pattern
    2. Images
    3. Containers
    4. Volumes and networks
    5. Cleanup
    6. Measure before you prune
    ↖ Course roadmap
    Lesson 12 · Docker CLI

    The Docker CLI day to day: inspect, filter, format and clean up

    The commands you use every day for images, containers, volumes and networks, how to filter and format their output for scripts, and how to win back disk space safely with docker system df and prune.

    25 min read · beginner · hands-on

    By the end of this lesson you'll be able to

    0 of 5 complete

    One pattern

    Every Docker command is docker <object> <verb>. Learn the verbs once and they work everywhere:

    Objectlsinspectrmprune
    imagedocker image ls (docker images)docker image inspectdocker image rm (rmi)docker image prune
    containerdocker container ls (docker ps)docker container inspectdocker container rm (rm)docker container prune
    volumedocker volume lsdocker volume inspectdocker volume rmdocker volume prune
    networkdocker network lsdocker network inspectdocker network rmdocker network prune

    docker <object> --help lists the rest.

    Images

    $ docker image ls --filter reference='price-api*' --format '{{.Repository}}:{{.Tag}} {{.Size}}'
    price-api:multi 14.6MB
    price-api:dev 14.6MB
    price-api:single 508MB
    $ docker image history price-api:multi            # layers and their sizes (lesson 7)
    $ docker image inspect price-api:multi --format '{{.Config.User}} {{json .Config.Entrypoint}}'
    nonroot ["/price-api"]
    $ docker image rm price-api:single                # removes the tag; layers go when nothing uses them
    $ docker pull --platform linux/arm64 alpine:3.22  # a specific architecture
    
    docker image ls --filter reference='price-api*' --format '{{.Repository}}:{{.Tag}} {{.Size}}'
    • dockerbuild and run containers
    • imagemanage images
    • lslist them
    • --filter reference='price-api*'show only matching items
    • --format '{{.Repository}}:{{.Tag}} {{.Size}}'print only these fields (Go template)
    docker image inspect price-api:multi --format '{{.Config.User}} {{json .Config.Entrypoint}}'
    • inspectshow details as JSON
    • --format '{{.Config.User}} {{json .Config.Entrypoint}}'print only these fields (Go template)
    docker image rm price-api:single
    • rmdelete one
    docker pull --platform linux/arm64 alpine:3.22
    • pulldownload an image
    • --platform linux/arm64which CPU architecture
    • alpine:3.22the image

    Containers

    $ docker ps -a --filter status=exited --format 'table {{.Names}}\t{{.Status}}'
    $ docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
    $ docker logs --since 10m --tail 50 web
    $ docker exec -it web sh
    $ docker cp web:/etc/nginx/nginx.conf ./nginx.conf       # copy out of a container
    $ docker cp ./index.html web:/usr/share/nginx/html/      # and into it
    $ docker stats --no-stream                               # CPU, memory, network, against limits
    $ docker top web                                         # processes, as the host sees them
    $ docker container rm -f $(docker ps -aq --filter label=env=test)   # remove a whole group
    
    docker ps -a --filter status=exited --format 'table {{.Names}}\t{{.Status}}'
    • dockerbuild and run containers
    • pslist running containers
    • -ainclude stopped ones too
    • --filter status=exitedshow only matching items
    • --format 'table {{.Names}}\t{{.Status}}'print only these fields (Go template)
    docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
    • inspectshow every detail as JSON
    • webthe container or image
    • --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'print only these fields (Go template)
    docker logs --since 10m --tail 50 web
    • logsshow a container’s output
    • --since 10monly output newer than this
    • --tail 50only the last N lines
    • webthe container
    docker exec -it web sh
    • execrun a command in a running container
    • -itinteractive, with a terminal
    • shthe command to run inside it
    docker cp web:/etc/nginx/nginx.conf ./nginx.conf
    • cpcopy files between a container and the host
    docker stats --no-stream
    • statslive CPU and memory use
    • --no-streamprint once instead of updating
    docker top web
    • topprocesses in a container
    docker container rm -f $(docker ps -aq --filter label=env=test)
    • containermanage containers
    • rmdelete one
    • -fdon’t ask to confirm
    • $(docker ps -aq --filter label=env=test)the output of another command

    --filter narrows lists (by status, name, label, ancestor, network…). --format takes a Go template: {{.Names}}, {{json .Config}}, table for aligned columns. docker inspect returns everything Docker knows about an object as JSON, and is the first place to look when something behaves oddly: the mounts, environment, restart count, health log and exit code are all there.

    Volumes and networks

    $ docker volume ls --filter dangling=true          # volumes no container uses
    $ docker volume inspect shop_pgdata --format '{{.Mountpoint}}'
    $ docker network ls
    $ docker network inspect appnet --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
    
    docker volume ls --filter dangling=true
    • dockerbuild and run containers
    • volumemanage volumes
    • lslist them
    • --filter dangling=trueshow only matching items
    docker volume inspect shop_pgdata --format '{{.Mountpoint}}'
    • inspectshow details as JSON
    • --format '{{.Mountpoint}}'print only these fields (Go template)
    docker network ls
    • networkmanage networks
    docker network inspect appnet --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
    • --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'print only these fields (Go template)

    Cleanup

    Docker never deletes anything on its own. Images from old builds, stopped containers, anonymous volumes and the build cache pile up until the disk is full, often on a CI runner at 2 a.m. See where the space goes:

    $ docker system df
    TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
    Images          21        0         5.446GB   479MB (8%)
    Containers      0         0         0B        0B
    Local Volumes   14        0         510.6MB   510.6MB (100%)
    Build Cache     74        0         1.226GB   447.7MB
    
    docker system df
    • dockerbuild and run containers
    • systemdisk use and cleanup
    • dfdisk use by type

    That's this machine after the course so far: 14 volumes nobody uses (mostly anonymous Postgres volumes from lesson 6) and half a gigabyte of build cache. Clean each kind on purpose:

    $ docker volume prune -f
    Total reclaimed space: 510.6MB
    $ docker builder prune -f
    Total:	404.6MB
    $ docker system df
    TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
    Images          21        0         5.045GB   77.44MB (1%)
    Containers      0         0         0B        0B
    Local Volumes   1         0         0B        0B
    Build Cache     37        0         820.9MB   43.11MB
    
    docker volume prune -f
    • dockerbuild and run containers
    • volumemanage volumes
    • prunedelete the unused ones
    • -fdon’t ask to confirm
    docker builder prune -f
    • buildermanage the build cache
    docker system df
    • systemdisk use and cleanup
    • dfdisk use by type
    CommandRemoves
    docker container prunestopped containers
    docker image prunedangling images (untagged leftovers); -a: every image no container uses
    docker volume pruneunused anonymous volumes; --all: unused named volumes too
    docker network prunenetworks no container uses
    docker builder prunebuild cache; --filter until=168h keeps the last week
    docker system prunecontainers, networks, dangling images and build cache; add --volumes or -a to go further
    Read before you prune

    Prune asks for confirmation and lists what it will delete: read that list. A volume prune deletes data with no undo, and docker system prune -a --volumes on a server can remove the database volume of a stack that is stopped for maintenance. Never put -a --volumes in a cron job.

    In production

    On CI runners and build servers, schedule docker builder prune --filter until=168h -f and docker image prune -a --filter until=168h -f. On application servers, keep a few previous images for rollback, and set a log size limit (--log-opt max-size=10m --log-opt max-file=3) so container logs can't fill the disk.

    Measure before you prune

    docker system df maps image, container and volume usage; prune narrowly after checking what is reclaimable. inspect --format gives stable fields for scripts, and docker context show confirms which daemon a command will affect. These are operational safeguards, not backups.

    $ docker system df
    $ docker ps --format '{{.Names}} {{.Image}} {{.Status}}'
    $ docker inspect api --format '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
    $ docker context show
    $ docker container prune --filter until=24h
    
    docker system df
    • dockerbuild and run containers
    • systemdisk use and cleanup
    • dfdisk use by type
    docker ps --format '{{.Names}} {{.Image}} {{.Status}}'
    • pslist running containers
    • --format '{{.Names}} {{.Image}} {{.Status}}'print only these fields (Go template)
    docker inspect api --format '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
    • inspectshow every detail as JSON
    • apithe container or image
    • --format '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'print only these fields (Go template)
    docker context show
    • contextchoose which daemon to talk to
    docker container prune --filter until=24h
    • containermanage containers
    • prunedelete the unused ones
    • --filter until=24hshow only matching items
    ⌘
    Mini mission

    Find and reclaim wasted space

    Measure Docker's disk use, create some junk on purpose (exited containers, anonymous volumes, a dangling image), find each kind with filters, and remove it with the matching prune without touching a named volume you want to keep.

    Input: DockerOutput: docker system df before and after, with the keep-me volume still present
    Reveal one safe solution
    $ docker system df
    $ docker volume create keep-me
    $ for i in 1 2 3; do docker run -v /data alpine:3.22 true; done        # 3 exited containers, 3 anonymous volumes
    $ docker ps -a --filter status=exited --filter ancestor=alpine:3.22 --format '{{.Names}} {{.Status}}'
    $ docker container prune -f
    $ docker volume ls --filter dangling=true        # now unused: the 3 anonymous ones, and keep-me
    $ docker volume prune -f
    $ docker volume ls --filter name=keep-me
    DRIVER    VOLUME NAME
    local     keep-me
    $ docker system df && docker volume rm keep-me
    
    docker system df
    • dockerbuild and run containers
    • systemdisk use and cleanup
    • dfdisk use by type
    docker volume create keep-me
    • volumemanage volumes
    • createcreate one
    for i in 1 2 3; do docker run -v /data alpine:3.22 true; done
    • forloop over a list
    • dostart of what the loop runs each time
    • runstart a new container
    • -v /datamount a volume or folder, source:path-inside
    • alpine:3.22the image to start
    • true;the command run inside, instead of the image’s default
    • doneend of the loop
    docker ps -a --filter status=exited --filter ancestor=alpine:3.22 --format '{{.Names}} {{.Status}}'
    • pslist running containers
    • -ainclude stopped ones too
    • --filter status=exitedshow only matching items
    • --filter ancestor=alpine:3.22show only matching items
    • --format '{{.Names}} {{.Status}}'print only these fields (Go template)
    docker container prune -f
    • containermanage containers
    • prunedelete the unused ones
    • -fdon’t ask to confirm
    docker volume ls --filter dangling=true
    • lslist them
    • --filter dangling=trueshow only matching items
    docker volume ls --filter name=keep-me
    • --filter name=keep-meshow only matching items
    docker system df && docker volume rm keep-me
    • &&run the next command only if this one worked
    • rmdelete one

    volume prune removed the anonymous volumes but kept keep-me, because named volumes need --all. Order matters too: volumes still attached to a stopped container aren't unused, so containers go first.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    docker system df shows Local Volumes at 510MB, 100% reclaimable. What does that mean?

    Go deeper

    Official reading for this lesson

    • DocsDocker Docs: Docker CLI referenceDocker Docs ↗
    • DocsDocker Docs: format command and log outputDocker Docs ↗
    • DocsDocker Docs: prune unused Docker objectsDocker Docs ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousContainer security: scanning, non-root, runtime limits and secretsNext lesson →Developing with Docker: hot reload, debugging, tests and CI