Samuel Papin

Why does Docker not make editing volumes easy?

I am migrating a small Discord bot I made to Docker (and eventually Kubernetes). This bot’s architecture is roughly like this:

discord_app_schema

This setup has been running for a while. Everything is working fine. I am now migrating it to run with a Docker volume representing the database.

This is fairly straightforward. I can create my volume with this command:

docker volume create persistent_db

Then… how do I put files there?

What's missing?

It feels like a tool is missing—the equivalent of the usual system manipulation commands provided by an OS:

I would like to be able to do something like:

# copy sqlite.db to the persistent_db volume

cp sqlite.db persistent_db:sqlite.db

What docker recommends

The normal pattern in Docker is to only access volume files from a container. Volumes are meant to be opaque types that the administrator doesn’t handle manually.

The proper recipe is:

docker run -rm -v persistent_db:/data -v $PWD:/src busybox cp src/sqlite.db data/sqlite.db

In my case, though — as I am developing and migrating to the new environment — I would like to be able to manually edit my containers.

Shell script

You can use this script:

#!/usr/bin/env bash
set -e

COMMAND="$1"
SRC_ARG="$2"
DST_ARG="${3:-}"

# Parse source volume and path (format: volume:path or just path)
if echo "$SRC_ARG" | grep -q ":"; then
    SRC_MOUNT="${SRC_ARG%%:*}"  # everything before ':'
    SRC_PATH="${SRC_ARG#*:}"    # everything after ':'
else
    SRC_MOUNT="$PWD"            # current directory
    SRC_PATH="$SRC_ARG"         # whole argument
fi

# Parse destination volume and path (if provided)
if echo "$DST_ARG" | grep -q ":"; then
    DST_MOUNT="${DST_ARG%%:*}"
    DST_PATH="${DST_ARG#*:}"
else
    DST_MOUNT="$PWD"
    DST_PATH="$DST_ARG"
fi

case "$COMMAND" in
  cp|mv)
    docker run --rm -v "$SRC_MOUNT":/src -v "$DST_MOUNT":/dst busybox \
      "$COMMAND" "/src/$SRC_PATH" "/dst/$DST_PATH"
    ;;
  rm)
    docker run --rm -v "$SRC_MOUNT":/src busybox rm -rf "/src/$SRC_PATH"
    ;;
  ls)
    docker run --rm -v "$SRC_MOUNT":/src busybox ls -l "/src/$SRC_PATH"
    ;;
  *)
    echo "Unsupported command: $COMMAND"
    echo "Available commands: cp, mv, rm, ls"
    exit 1
    ;;
esac

You can then use the command as you normally would:

dv cp sqlite.db persitent_db:sqlite.db 
dv ls persistent_db
etc...

I know this isn’t the way Docker intends it to be used, but as I am working on my project, I find it extremely convenient.