Skip to content

Instantly share code, notes, and snippets.

@webcoderz
Created February 7, 2026 20:16
Show Gist options
  • Select an option

  • Save webcoderz/4656b0ac3c78766684d082fd4c23a273 to your computer and use it in GitHub Desktop.

Select an option

Save webcoderz/4656b0ac3c78766684d082fd4c23a273 to your computer and use it in GitHub Desktop.
migrate volumes from inside wsl to docker desktop on windows..
#!/usr/bin/env bash
set -euo pipefail
SRC_VOLUMES_DIR="${SRC_VOLUMES_DIR:-/var/lib/docker/volumes}"
WORK_DIR="${WORK_DIR:-/tmp/volume-migrate}"
ALPINE_IMAGE="${ALPINE_IMAGE:-alpine:3.20}"
# Skip internal docker volume dirs and anything you don't want
SKIP_NAMES_REGEX='^(backingFsBlockDev|metadata\.db|tmp|builder|buildx|containerd|overlay2|aufs|btrfs|zfs|fuse-overlayfs)$'
log() { printf "\n[%s] %s\n" "$(date +'%F %T')" "$*"; }
need_root_for_src() {
if [[ $EUID -ne 0 ]]; then
log "ERROR: run as root (or via sudo) so we can read $SRC_VOLUMES_DIR"
exit 1
fi
}
ensure_docker_cli() {
if ! command -v docker >/dev/null 2>&1; then
log "ERROR: docker CLI not found in PATH."
exit 1
fi
# We want to be talking to Docker Desktop engine
local name
name="$(docker info --format '{{.Name}}' 2>/dev/null || true)"
if [[ -z "$name" ]]; then
log "ERROR: docker daemon not reachable from this shell."
exit 1
fi
log "docker engine: $name"
}
migrate_one() {
local vol="$1"
local src_data="${SRC_VOLUMES_DIR}/${vol}/_data"
local tarball="${WORK_DIR}/${vol}.tar"
if [[ ! -d "$src_data" ]]; then
log "SKIP (no _data): $vol"
return 0
fi
if [[ "$vol" =~ $SKIP_NAMES_REGEX ]]; then
log "SKIP (internal): $vol"
return 0
fi
log "Migrating: $vol"
# Create tarball from old data
rm -f "$tarball"
tar -C "$src_data" -cf "$tarball" .
# Create volume in Docker Desktop if it doesn't exist
if ! docker volume inspect "$vol" >/dev/null 2>&1; then
docker volume create "$vol" >/dev/null
fi
# Restore into Docker Desktop volume (wipe destination first to avoid duplicates)
docker run --rm \
-v "${vol}:/to" \
-v "${WORK_DIR}:/from:ro" \
"$ALPINE_IMAGE" sh -c "rm -rf /to/* /to/.[!.]* /to/..?* 2>/dev/null || true; cd /to && tar -xf /from/${vol}.tar"
log "Done: $vol"
}
main() {
need_root_for_src
ensure_docker_cli
mkdir -p "$WORK_DIR"
log "Source volumes dir: $SRC_VOLUMES_DIR"
log "Work dir: $WORK_DIR"
log "Enumerating volumes..."
local vols=()
while IFS= read -r name; do
vols+=("$name")
done < <(find "$SRC_VOLUMES_DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort)
log "Found ${#vols[@]} volume dirs. Starting migration..."
local ok=0
local fail=0
for v in "${vols[@]}"; do
if migrate_one "$v"; then
ok=$((ok+1))
else
log "FAILED: $v"
fail=$((fail+1))
fi
done
log "Migration finished. OK=$ok FAIL=$fail"
log "Tarballs left in: $WORK_DIR (delete when you're satisfied)."
log "Next: verify your compose stacks with Docker Desktop volumes, then you can delete $SRC_VOLUMES_DIR to reclaim space."
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment