mahonya
mahonya

Reputation: 10045

How can I add a volume to an existing Docker container?

I have a Docker container that I've created simply by installing Docker on Ubuntu and doing:

sudo docker run -i -t ubuntu /bin/bash

I immediately started installing Java and some other tools, spent some time with it, and stopped the container by

exit

Then I wanted to add a volume and realised that this is not as straightforward as I thought it would be. If I use sudo docker -v /somedir run ... then I end up with a fresh new container, so I'd have to install Java and do what I've already done before just to arrive at a container with a mounted volume.

All the documentation about mounting a folder from the host seems to imply that mounting a volume is something that can be done when creating a container. So the only option I have to avoid reconfiguring a new container from scratch is to commit the existing container to a repository and use that as the basis of a new one whilst mounting the volume.

Is this indeed the only way to add a volume to an existing container?

Upvotes: 592

Views: 558879

Answers (11)

johannes_lalala
johannes_lalala

Reputation: 595

If you're under linux and have enough privileges, there's a way to attach a bind mount to a running container. See e.g. https://brauner.io/2023/02/28/mounting-into-mount-namespaces.html for more info.

Here's a working example following above blog post. The program takes 3 arguments: host directory, container directory and any pid of some program currently running in the container. To find the latter, do ps x |grep whatever_runs_inyour_container. It's easiest to run the program with sudo.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sched.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/mount.h>

#include <errno.h>
#include <sys/syscall.h>

int main(int argc, char **argv)
{
    if (argc < 3) {
        printf("usage: ./a.out pid src_dir dst_dir\n");
        return 0;
    }

    char *pid = argv[1];
    char *src_dir = argv[2];
    char *dst_dir = argv[3];

    int fd_mnt;
    fd_mnt = syscall(__NR_open_tree, 
        -EBADF, src_dir, OPEN_TREE_CLONE);

    if (fd_mnt < 0) {
        perror("open_tree failed");
        return 1;
    }

    char mount_namespace[1000];
    snprintf(mount_namespace, 1000, "/proc/%s/ns/mnt", pid);
    int fd_mntns = open(mount_namespace, O_RDONLY);

    if (fd_mntns < 0) {
        printf("open /proc/%s/ns/mnt failed: %s", pid, strerror(errno));
        return 1;
    }

    setns(fd_mntns, 0);

    int ret = syscall(__NR_move_mount, 
        fd_mnt, "", -EBADF, dst_dir, MOVE_MOUNT_F_EMPTY_PATH );

    if (ret < 0) {
        perror("move_mount failed");
        return 1;
    }

    printf("mounted %s to %s of namespace %s,", src_dir, dst_dir, mount_namespace);
    return 0;
}

Upvotes: 4

BigLeo
BigLeo

Reputation: 7789

You can commit your existing container (that creates a new image from container’s changes) and then run it with your new mounts.

Example:

$ docker ps  -a

CONTAINER ID        IMAGE                 COMMAND                  CREATED              STATUS                          PORTS               NAMES
5a8f89adeead        ubuntu:14.04          "/bin/bash"              About a minute ago   Exited (0) About a minute ago                       agitated_newton

$ docker commit 5a8f89adeead newimagename
$ docker run -ti -v "$PWD/somedir":/somedir newimagename /bin/bash

If it's all OK, stop your old container, and use this new one.

You can also commit a container using its name, for example:

docker commit agitated_newton newimagename

That's it :)

Upvotes: 730

Aditya Bhuyan
Aditya Bhuyan

Reputation: 448

You can stop your container, add the volume and restart it. How to do it, follow the steps.

docker volume create ubuntu-volume
docker stop <container-name>
sudo docker run -i -t --mount source=ubuntu-volume,target=<target-path-in-container> ubuntu /bin/bash 

Upvotes: -1

droebi
droebi

Reputation: 946

A note for using Docker Windows containers

Conditions:

  • Windows 10
  • Docker Desktop (latest version)
  • using Docker Windows Container for image microsoft/mssql-server-windows-developer

Problem:

  • I wanted to mount a host dictionary into my windows container.

Solution as partially described here:

  • Create docker container

docker run -d -p 1433:1433 -e sa_password=<STRONG_PASSWORD> -e ACCEPT_EULA=Y microsoft/mssql-server-windows-developer

  • Go to command shell in container

docker exec -it <CONTAINERID> cmd.exe

  • Create DIR

mkdir DirForMount

  • Stop container

docker container stop <CONTAINERID>

  • Commit container

docker commit <CONTAINERID> <NEWIMAGENAME>

  • Delete old container

docker container rm <CONTAINERID>

  • Create new container with new image and volume mounting

docker run -d -p 1433:1433 -e sa_password=<STRONG_PASSWORD> -e ACCEPT_EULA=Y -v C:\DirToMount:C:\DirForMount <NEWIMAGENAME>

After this I solved this problem on docker windows containers.

Upvotes: 8

Roman
Roman

Reputation: 20246

Jérôme Petazzoni has a pretty interesting blog post on how to Attach a volume to a container while it is running. This isn't something that's built into Docker out of the box, but possible to accomplish.

As he also points out

This will not work on filesystems which are not based on block devices.

It will only work if /proc/mounts correctly lists the block device node (which, as we saw above, is not necessarily true).

Also, I only tested this on my local environment; I didn’t even try on a cloud instance or anything like that

Upvotes: 35

Franck Theeten
Franck Theeten

Reputation: 140

You can stop and remove the container, append the existing volume in a startup script, and restart from the image. If the already existing existing partitions do keep the data, you shouldn't experience any loss of information. This should also work the same way with Dockerfile and Docker composer.

eg (solr image). (initial script)

#!/bin/sh
docker pull solr:8.5
docker stop my_solr
docker rm solr:8.5

docker create \
  --name my_solr \
  -v "/XXXX/docker/solr/solrdata":/var/solr \
  -p 8983:8983 \
  --restart unless-stopped \
  --user 1000:1000 \
  -e SOLR_HEAP=1g \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  solr:8.5
docker cp /home/XXXX/docker/solr/XXXXXXXX.jar my_solr:/opt/solr/contrib/dataimporthandler-extras/lib
docker start my_solr

file with the second volume

#!/bin/sh
docker pull solr:8.5
docker stop my_solr
docker rm solr:8.5

docker create \
  --name my_solr \
  -v "/XXXX/docker/solr/solrdata":/var/solr \
  -v "/XXXX/backups/solr_snapshot_folder":/var/solr_snapshots \
  -p 8983:8983 \
  --restart unless-stopped \
  --user 1000:1000 \
  -e SOLR_HEAP=1g \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  solr:8.5
docker cp /home/XXXX/docker/solr/XXXXXXXX.jar my_solr:/opt/solr/contrib/dataimporthandler-extras/lib
docker start my_solr

Upvotes: 0

Darshan Bhanushali
Darshan Bhanushali

Reputation: 9

Use symlink to the already mounted drive:

ln -s Source_path targer_path_which_is_already_mounted_on_the_running_docker

Upvotes: -2

user128364
user128364

Reputation: 4925

We don't have any way to add volume in running container, but to achieve this objective you may use the below commands:

Copy files/folders between a container and the local filesystem:

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH

docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

For reference see:

https://docs.docker.com/engine/reference/commandline/cp/

Upvotes: 200

Igor Bendrup
Igor Bendrup

Reputation: 2837

I've successfully mount /home/<user-name> folder of my host to the /mnt folder of the existing (not running) container. You can do it in the following way:

  1. Open configuration file corresponding to the stopped container, which can be found at /var/lib/docker/containers/99d...1fb/config.v2.json (may be config.json for older versions of docker).

  2. Find MountPoints section, which was empty in my case: "MountPoints":{}. Next replace the contents with something like this (you can copy proper contents from another container with proper settings):

"MountPoints":{"/mnt":{"Source":"/home/<user-name>","Destination":"/mnt","RW":true,"Name":"","Driver":"","Type":"bind","Propagation":"rprivate","Spec":{"Type":"bind","Source":"/home/<user-name>","Target":"/mnt"},"SkipMountpointCreation":false}}

or the same (formatted):

  "MountPoints": {
    "/mnt": {
      "Source": "/home/<user-name>",
      "Destination": "/mnt",
      "RW": true,
      "Name": "",
      "Driver": "",
      "Type": "bind",
      "Propagation": "rprivate",
      "Spec": {
        "Type": "bind",
        "Source": "/home/<user-name>",
        "Target": "/mnt"
      },
      "SkipMountpointCreation": false
    }
  }
  1. Restart the docker service: service docker restart

This works for me with Ubuntu 18.04.1 and Docker 18.09.0

Upvotes: 124

Vatsal Mishra
Vatsal Mishra

Reputation: 11

The best way is to copy all the files and folders inside a directory on your local file system by: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH

SRC_PATH is on container DEST_PATH is on localhost

Then do docker-compose down attach a volume to the same DEST_PATH and run Docker containers by using docker-compose up -d

Add volume by following in docker-compose.yml

volumes:
 - DEST_PATH:SRC_PATH

Upvotes: -6

Adel Helal
Adel Helal

Reputation: 670

Unfortunately the switch option to mount a volume is only found in the run command.

docker run --help

-v, --volume list Bind mount a volume (default [])

There is a way you can work around this though so you won't have to reinstall the applications you've already set up on your container.

  1. Export your container docker container export -o ./myimage.docker mycontainer
  2. Import as an image docker import ./myimage.docker myimage
  3. Then docker run -i -t -v /somedir --name mycontainer myimage /bin/bash

Upvotes: 14

Related Questions