--- draft: false title: "How to find that one volume you're pretty sure you didn't lose" date: "2024-06-25" series: [] author: "Nick Dumas" cover: "" tags: ["bash", "docker"] summary: "Docker volumes can be opaque, so I wrote a small bash script to help you troubleshoot." showFullContent: false --- ## What I expect you to know This article is only relevant if you know about and use Docker volumes and have some fluency in bash. I'll explain the code as I go, if it helps. ## The Problem Over the lifetime of a Docker host machine, it's like that orphaned volumes ( and other detritus ) will accumulate over time. You might also find yourself fumbling a configuration and orphaning a volume yourself. However we got here, we have a bunch of volumes and we need to know if any of them are important. In a perfect world, they'll have decent names. We don't live in a perfect world. ## Make a list Luckily, we have tools at our disposal to handle this. My thought process almost always starts with "Can I turn a list of the things I care about into a newline separated list?" If I can do that, I can start automating my troubleshooting. Let's start with `docker volume ls`. This is how we list volumes, but the default output isn't quite what I'm looking for: ``` docker volume ls DRIVER VOLUME NAME local d35fce052fbce42b94b2f9b2957be0f77090fa006b1a192030eff07db3675af2 local grafana-storage local plausible_db-data local plausible_event-data ``` This is human readable, and we could even do some slicing with `cut` or `awk`, but Docker gives us a flag that will take us exactly where we need to go: `--format`. Generally, Docker uses Go's `text/template` library to back this feature, and more specifically, individual flags (usually) [document](https://docs.docker.com/reference/cli/docker/volume/ls/#format) the template verbs available. Here, we want `Name`. ``` docker volume ls --format "{{.Name}}" d35fce052fbce42b94b2f9b2957be0f77090fa006b1a192030eff07db3675af2 grafana-storage plausible_db-data plausible_event-data ``` And now we have a newline separated list of volume names. ## Process of elimination The next part is fairly straightforward. We loop over this list and ask Docker to create a temporary container based on alpine, with a single volume mounted at `/test/`. ``` #! /bin/bash # Newline separated list of volume names volumes=$(docker volume ls --format="{{.Name}}") for volume in $volumes; do # Help the user keep track of which volume they're exploring echo "Mounting $volume in a temporary image." docker run --rm -v $volume:/test/ -it alpine /bin/sh done ``` Running this script should do something like this: ``` ./cycle-volumes.sh Mounting d35fce052fbce42b94b2f9b2957be0f77090fa006b1a192030eff07db3675af2 in a temporary image. / # ls /test/ clickhouse-server.err.log clickhouse-server.log.1.gz clickhouse-server.log.4.gz clickhouse-server.log.7.gz clickhouse-server.log clickhouse-server.log.2.gz clickhouse-server.log.5.gz clickhouse-server.log.8.gz clickhouse-server.log.0.gz clickhouse-server.log.3.gz clickhouse-server.log.6.gz / # Mounting grafana-storage in a temporary image. / # ls /test alerting csv file-collections grafana.db plugins png / # exit Mounting plausible_db-data in a temporary image. / # exit Mounting plausible_event-data in a temporary image. / # exit ``` You can use bash to explore the volume and identify its contents, make note of which ones are which, and proceed accordingly.