Metadata-Version: 2.4
Name: docker-volume-toolkit
Version: 1.2.4
Summary: Docker volume toolkit - copy/migrate volumes across a name-prefix change (CLI + interactive TUI)
Project-URL: Homepage, https://github.com/stellarshenson/docker-volume-toolkit
Project-URL: Repository, https://github.com/stellarshenson/docker-volume-toolkit
Project-URL: Issues, https://github.com/stellarshenson/docker-volume-toolkit/issues
Author-email: stellarshenson <konrad.jelen@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cli,devops,docker,migration,rsync,toolkit,tui,volume
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: rich>=13
Requires-Dist: textual>=0.80
Description-Content-Type: text/markdown

# docker-volume-toolkit

[![PyPI version](https://img.shields.io/pypi/v/docker-volume-toolkit.svg)](https://pypi.org/project/docker-volume-toolkit/)
[![Total PyPI downloads](https://static.pepy.tech/badge/docker-volume-toolkit)](https://pepy.tech/project/docker-volume-toolkit)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

A small toolkit for Docker volumes. Its first command copies volumes from one name prefix
to another - it matches every volume named `{from_prefix}{tail}` and copies it to
`{to_prefix}{tail}`, preserving the tail (`_home`, `_workspace`, `_certs`, a
per-user suffix, anything that follows the prefix). Run it from the host that
owns the Docker volumes; it copies rather than renames, so the originals stay in
place until you have verified the result.

Run with no arguments for the interactive TUI - designer, plan, execution:

![Designer](.resources/1-designer.png)

*Set the FROM and TO prefixes, an optional whole-name filter, the worker count, and the overwrite / remove-source toggles; a live counter shows how many discovered volumes match (`5 of 20`) and the BEFORE / AFTER panes preview the exact source and destination names.*

![Migration plan](.resources/2-plan.png)

*Review each matched volume and its `source → destination` mapping; toggle rows with Space (a = all, n = none) and press Enter to run only the selected copies.*

![Execution](.resources/3-execution.png)

*Live progress during the copy - an overall bar plus a per-volume bar for each parallel worker, moving through discovery and transfer.*

## When you need it

Docker namespaces volumes by `COMPOSE_PROJECT_NAME` (for example `myproject_data`,
`myproject_shared`), and many stacks add a per-entity prefix of their own
(`jupyterlab-<user>`). Whenever that prefix changes you would otherwise lose
access to the existing data:

- renaming a deployment (`COMPOSE_PROJECT_NAME` change) renames every
  `<old-project>_*` volume
- an upstream platform reworking its volume names across an upgrade

The migrator moves the data onto the new names so nothing is lost across the
rename.

## Usage

Run with no arguments for the interactive TUI (designer → plan → execution):

```bash
./docker_volume_toolkit.py
```

Or drive it entirely from the command line:

```bash
# preview the mapping without copying
./docker_volume_toolkit.py --from myproject_ --to mynewproject_ --dry-run

# copy, skipping the prompt
./docker_volume_toolkit.py --from myproject_ --to mynewproject_ --yes

# only the cert volumes, four parallel workers
./docker_volume_toolkit.py --from myproject_ --to mynewproject_ --filter '_certs$' --workers 4
```

## Options

- `--from PREFIX` source volume name prefix (e.g. `jupyterlab-`)
- `--to PREFIX` replacement destination prefix
- `--filter REGEX` regex applied to the full source volume name (empty = all matches)
- `--workers N` parallel copy containers (default 3)
- `--dry-run` mount both volumes and verify access, copy nothing
- `--overwrite` clean and replace a destination volume that already exists (default: error out and abort)
- `--remove-source` delete each source volume after its successful copy (default: keep sources)
- `--yes` skip the interactive plan and run from the CLI arguments

## How it works

- each copy runs `rsync -aAX --delete` inside a disposable `alpine` container - source mounted read-only, destination read-write; all metadata preserved
- destinations are never recreated - with `--overwrite` the existing volume is kept and its contents mirrored from the source (`--delete` clears stale files)
- sources are left intact by default; after the run the tool prints the `docker volume rm` commands for every volume it copied so you can clean up once verified
- the `--filter` regex matches the whole source name; note Docker encodes `.` in volume names as `-2e` (e.g. `alice.smith` appears as `alice-2esmith`)

## Install

Needs Docker (the tool shells out to `docker volume` and `docker run`) and Python 3.10+;
`rich>=13` and `textual>=0.80` come with it.

Install from PyPI and run the CLI:

```bash
pip install docker-volume-toolkit
docker-volume-toolkit            # interactive TUI
docker-volume-toolkit --help     # CLI flags
```

Or skip installation entirely - the script carries an inline dependency block and a
`uv run --script` shebang, so it auto-installs its own dependencies on first run:

```bash
./docker_volume_toolkit.py
```

Without `uv`, install the dependencies once and run with any Python:

```bash
pip install rich textual
python docker_volume_toolkit.py --help
```

---

*It copies volumes from one prefix to another, and then it has no further reason to exist. You will run it twice and forget it. The volumes never say thank you.*
