Skip to main content
  1. Projects/

Cleaning Up a Bloated Docker vDisk on Unraid

Status Stable
Cost $0
Time Spent ~20 minutes
Outcome

Reclaimed 22GB of vDisk space, reducing usage from 89% to 31%

Stack
Docker Unraid

My Unraid Docker vDisk crept up to 89% capacity. Stopped containers, dangling images, and orphaned volumes had accumulated from months of tinkering. This post covers how I recovered 22GB and got back down to 31% without touching anything I actually needed.

The Problem
#

The Docker vDisk on Unraid has a fixed size. When it fills up, containers can start behaving unpredictably and new pulls fail. At 89% I was close enough to the limit that it needed to be dealt with before something broke.

The tricky part is that docker system df shows a reclaimable number that is often misleading. It only counts truly dangling images, not images that are still referenced by stopped containers. The real opportunity was hidden behind months of test containers that had been stopped and forgotten.

The Goal
#

Recover enough space to get the vDisk back to a comfortable level without removing anything actively in use. No nuclear options, no wiping everything and starting fresh.

The Build
#

The Investigation
#

SSH into Unraid and get a clear picture of what is consuming space:

docker system df

My output:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          32        30        32.24GB   3.442GB (10%)
Containers      31        12        904MB     103.4MB (11%)
Local Volumes   16        2         1.045GB   1.045GB (100%)
Build Cache     0         0         0B        0B

32 images at 32GB with only 3.4GB showing as reclaimable. 31 containers but only 12 active. 16 volumes with 14 of them completely unused. The low reclaimable number on images was the key clue. Stopped containers were still referencing images, preventing them from being counted as reclaimable.

To see which images were taking the most space:

docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | sort -k3 -h

The top offenders:

ghcr.io/blakeblackshear/frigate    stable-tensorrt    6.63GB
lscr.io/linuxserver/orcaslicer     latest             4.33GB
gitlab/gitlab-ce                   latest             3.96GB
lscr.io/linuxserver/webtop         ubuntu-xfce        2.91GB
agregarr/agregarr                  latest             1.98GB
ghcr.io/harborguard/harborguard    latest             2.05GB

Frigate, OrcaSlicer, Webtop, agregarr, and harborguard were all test containers spun up and forgotten. GitLab is legitimately in use so that stayed. Those five alone represented around 17GB.

The Investigation: Why the Reclaimable Number Was Wrong
#

The order of operations matters here. Images cannot be removed while a container, even a stopped one, still references them. The stopped containers needed to go first before the images would show as reclaimable.

Listing all containers including stopped ones confirmed several that had been dead for 8 weeks:

docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"

The Fix
#

Step 1: Remove stopped containers

docker rm adguardhome-sync cronmaster cadvisor doku diun dozzle \
           mkvtoolnix makemkv handbrake orcaslicer frigate webtop

After this, the reclaimable image count jumped from 3.4GB to 10.93GB.

Step 2: Remove unused images

docker rmi ghcr.io/blakeblackshear/frigate:stable-tensorrt
docker rmi lscr.io/linuxserver/webtop:ubuntu-xfce
docker rmi agregarr/agregarr:latest
docker rmi ghcr.io/harborguard/harborguard:latest

If a removal fails because a stopped container is still referencing the image, check the error for the container ID and force remove it first:

docker rm -f <container-id>
docker rmi <image>

Then clean up remaining dangling images:

docker image prune -f

Step 3: Remove orphaned volumes

All 14 unused volumes had hash-based names, meaning they were created automatically by containers that no longer exist. Most cleared with:

docker volume prune -f

A handful of named volumes from a Fleet install were also orphaned:

docker volume rm fleet_data fleet_logs fleet_mysql fleet_redis fleet_vulndb

Results
#

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          12        12        9.853GB   78.13MB (0%)
Containers      12        12        800.8MB   0B (0%)
Local Volumes   1         1         0B        0B
Build Cache     0         0         0B        0B

Images went from 32.24GB down to 9.85GB. Containers went from 31 total down to 12, all active. Volumes went from 16 down to 1. Docker vDisk dropped from 89% to 31%. The whole cleanup took about 20 minutes and most of that was identifying what was safe to remove.

What I Would Do Differently
#

I would not let it get to 89% in the first place. A periodic docker system df check, even just monthly, would catch drift long before it becomes urgent. The bigger lesson is that test containers should be removed immediately after testing rather than left stopped indefinitely. A stopped container feels harmless but it is silently holding onto its image and preventing cleanup.

What Is Next
#

Going forward I added log rotation to all Docker Compose services to prevent container logs from silently growing:

logging:
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

I also added docker system df to my monthly maintenance checklist so this does not creep up again unnoticed.

Quick Reference
#

# See space usage breakdown
docker system df

# List all containers including stopped
docker ps -a

# List images sorted by size
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | sort -k3 -h

# Remove specific stopped containers
docker rm container1 container2

# Remove dangling images only
docker image prune -f

# Remove all unused images
docker image prune -a -f

# Remove orphaned volumes
docker volume prune -f

# Remove everything unused in one shot
docker system prune -a -f --volumes

The last command removes everything not attached to a running container. Use it only if you are confident nothing stopped is worth keeping.


Disclaimer: Content on this site is provided as-is with no guarantees of accuracy, completeness, or fitness for any particular purpose. Always test in a safe, non-production environment before applying anything documented here to systems you rely on. I am not responsible for data loss, damage, or security issues resulting from following these guides. Software and services change over time and steps that worked when this was written may not work in the future.