So I've been playing around with Docker containers for a while now and really like it. In fact this blog is running in a Docker container right now. But Docker sure leaves a mess behind on disk.
One of the things it tends to leave behind is volumes, that is unless you specify the --volumes option when you rm a container which I always forget. So I wrote a little script to delete all the orphaned docker volumes in /var/lib/docker/volumes and /var/lib/docker/vfs/dir:

WARNING: volumes have changed in Docker 1.7.0, the script below is outdated and will delete all volumes on Docker 1.7.0, goto GitHub for the latest version of the script that works with Docker 1.7.0

#! /bin/bash

set -eo pipefail

#usage: sudo ./docker-cleanup-volumes.sh [--dry-run]

dockerdir=/var/lib/docker
volumesdir=${dockerdir}/volumes
vfsdir=${dockerdir}/vfs/dir
allvolumes=()
dryrun=false

function delete_volumes() {
  targetdir=$1
  echo
  if [[ ! -d ${targetdir} ]]; then
        echo "Directory ${targetdir} does not exist, skipping."
        return
  fi
  echo "Delete unused volume directories from $targetdir"
  for dir in $(ls -d ${targetdir}/* 2>/dev/null)
  do
        dir=$(basename $dir)
        if [[ "${dir}" =~ [0-9a-f]{64} ]]; then
                if [[ ${allvolumes[@]} =~ "${dir}" ]]; then
                        echo In use ${dir}
                else
                        if [ "${dryrun}" = false ]; then
                                echo Deleting ${dir}
                                rm -rf ${targetdir}/${dir}
                        else
                                echo Would have deleted ${dir}
                        fi
                fi
        else
                echo Not a volume ${dir}
        fi
  done
}

if [ $UID != 0 ]; then
    echo "You need to be root to use this script."
    exit 1
fi

docker_bin=$(which docker.io || which docker)
if [ -z "$docker_bin" ] ; then
    echo "Please install docker. You can install docker by running \"wget -qO- https://get.docker.io/ | sh\"."
    exit 1
fi

if [ "$1" = "--dry-run" ]; then
        dryrun=true
else if [ -n "$1" ]; then
        echo "Cleanup docker volumes: remove unused volumes."
        echo "Usage: ${0##*/} [--dry-run]"
        echo "   --dry-run: dry run: display what would get removed."
        exit 1
fi
fi

# Make sure that we can talk to docker daemon. If we cannot, we fail here.
docker info >/dev/null

#All volumes from all containers
for container in `${docker_bin} ps -a -q --no-trunc`; do
        #add container id to list of volumes, don't think these
        #ever exists in the volumesdir but just to be safe
        allvolumes+=${container}
        #add all volumes from this container to the list of volumes
        for vid in `${docker_bin} inspect --format='{{range $vol, $path := .Volumes}}{{$path}}{{"\n"}}{{end}}' ${container}`; do
            if [[ ${vid} == ${vfsdir}* && "${vid##*/}" =~ [0-9a-f]{64} ]]; then
                    allvolumes+=("${vid##*/}")
            else
                    #check if it's a bindmount, these have a config.json file in the ${volumesdir} but no files in ${vfsdir}
                    for bmv in `grep --include config.json -Rl "\"IsBindMount\":true" ${volumesdir} | xargs -i grep -l "\"Path\":\"${vid}\"" {}`; do
                            bmv="$(basename "$(dirname "${bmv}")")"
                            allvolumes+=("${bmv}")
                            #there should be only one config for the bindmount, delete any duplicate for the same bindmount.
                            break
                    done
            fi
            done
done

delete_volumes ${volumesdir}
delete_volumes ${vfsdir}

View on GitHub or View on Docker registry