22

On my Ubuntu EC2 I host an application using docker containers. db data and upload data is being stored in volumes CaseBook-data-db and CaseBook-data-uploads which are being created with this commands:

docker volume create --name=CaseBook-data-db
docker volume create --name=CaseBook-data-uploads

Volumes being attached through docker-compose file:

version: '2'
services:
    mongo:
        container_name: "CaseBook-db"
        restart: always
        image: mongo:3.2.7
        ports:
            - "27017"
        volumes:
            - data_db:/data/db
        labels:
            - "ENVIRONMENT_TYPE=meteor"

    app:
        container_name: "CaseBook-app"
        restart: always
        image: "meteor/casebook"
        build: .
        depends_on:
            - mongo
        environment:
            - MONGO_URL=mongodb://mongo:27017/CaseBook
        ports:
            - "80:3000"
        volumes:
            - data_uploads:/Meteor-CaseBook-Container/.uploads
        labels:
            - "ENVIRONMENT_TYPE=meteor"
volumes:
     data_db:
        external:
            name: CaseBook-data-db
     data_uploads:
        external:
            name: CaseBook-data-uploads

I need to store those docker volumes in different folder(for example /home/ubuntu/data/) of the host machine. How to change docker storage folder for volumes? Or there is a better way in doing this? Thank you in advance.

DimonVersace
  • 325
  • 1
  • 2
  • 7

2 Answers2

29

Named volumes will be stored inside docker's folder (/var/lib/docker). If you want to create a volume in a specific host folder, use a host volume with the following syntax:

docker run -v /home/ubuntu/data/app-data:/app-data my-image

Or from your compose file:

version: '2'
services:
    mongo:
        container_name: "CaseBook-db"
        restart: always
        image: mongo:3.2.7
        ports:
            - "27017"
        volumes:
            - /home/ubuntu/data/db:/data/db
        labels:
            - "ENVIRONMENT_TYPE=meteor"

    app:
        container_name: "CaseBook-app"
        restart: always
        image: "meteor/casebook"
        build: .
        depends_on:
            - mongo
        environment:
            - MONGO_URL=mongodb://mongo:27017/CaseBook
        ports:
            - "80:3000"
        volumes:
            - /home/ubuntu/data/uploads:/Meteor-CaseBook-Container/.uploads
        labels:
            - "ENVIRONMENT_TYPE=meteor"

With host volumes, any contents of the volume inside the image will be overlaid with the exact contents of the host folder, including UID's of the host folder. An empty host folder is not initialized from the image the way an empty named volume is. UID mappings tend to be the most difficult part of using a host volume.


Edit: from the comments below, if you need a named volume that acts as a host volume, there is a local persist volume plugin that's listed on docker's plugin list. After installing the plugin, you can create volumes that point to host folders, with the feature that even after removing the named volume, the host directory is left behind. Sample usage from the plugin includes:

docker volume create -d local-persist -o mountpoint=/data/images --name=images
docker run -d -v images:/path/to/images/on/one/ one
docker run -d -v images:/path/to/images/on/two/ two

They also include a v2 compose file with the following volume example:

volumes:
  data:
    driver: local-persist
    driver_opts:
      mountpoint: /data/local-persist/data

One additional option that I've been made aware of in the past month is to use the local volume driver's mount options to manually create a bind mount. This is similar to a host volume in docker with the following differences:

  • If the directory doesn't exist, trying to start a container with a named volume pointing to a bind mount will fail. With host volumes, docker will initialize it to an empty directory owned by root.
  • If the directory is empty, a named volume will initialize the bind mount with the contents of the image at the mount location, including file and directory ownership/permissions. With a host volume, there is no initialization of the host directory contents.

To create a named volume as a bind mount, you can create it in advance with:

docker volume create --driver local \
  --opt type=none \
  --opt device=/home/user/test \
  --opt o=bind \
  test_vol

From a docker run command, this can be done with --mount:

docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

Or in a compose file, you can create the named volume with:

volumes:
  data:
    driver: local
    driver_opts:
      type: none
      o: bind 
      device: /home/user/test 

My preference would be to use the named volume with the local driver instead of the local-persist 3rd party driver if you need the named volume features.

BMitch
  • 231,797
  • 42
  • 475
  • 450
  • 1
    But is there a way to use named volumes, but with same logic? – DimonVersace Jul 15 '16 at 13:33
  • 2
    Not with the default named volumes, at least not to my knowledge. You can add other volume drivers like [local persist](https://github.com/CWSpear/local-persist) that would support this. – BMitch Jul 15 '16 at 14:02
  • But the first option above the line does not mount a volume, this is the way to create `bind mounts`. Because the first argument is the path on the host machine: [docs](https://docs.docker.com/engine/admin/volumes/bind-mounts/) – Ivan Talalaev Dec 08 '17 at 12:22
  • 1
    @ivantalalaev host volume is another name for a bind mount in docker terminology. https://success.docker.com/KBase/Different_Types_of_Volumes – BMitch Dec 08 '17 at 12:25
  • What "--opt type=none" does? (Third part of your answer). – VinceLomba Sep 21 '19 at 20:24
  • 1
    @Vinciuz it gets passed to the mount syscall. Type indicates the filesystem type, e.g. ext4 or nfs, but with a bind mount, there is no type. – BMitch Sep 22 '19 at 00:58
  • @BMitch I have followed your method 3, i.e. first create the volumes on an external drive using "docker volume create" and the "device=... " option - then load them in docker-compose file with the external:true flag. However, although the application correctly puts its data into the new volumes (on the external drive), I see that there appears to be an exact copy of the data being made simultaneously in the default volume location (at /var/lib/docker/volumes/). Do you know what is going on here? Do we expect two copies of the data (and therefore twice as much disk space being used) ? Thanks. – teeeeee Jan 23 '23 at 23:35
  • 1
    @teeeeee that's a bind mount (you'll see the directory mounted in `mount`). It's not a copy of the data, it's a second filesystem path to the same directory, there's no additional disk space used other than a bit of json metadata tracking the volume definition. – BMitch Jan 24 '23 at 01:32
  • @BMitch okay thanks, that's more reassuring now. When I run the disk available command (df), I can see the local disk (where /var/lib/docker resides) is increasing as well as external disk. You are saying this is expected due to it being a bind mount. It's a little confusing to see this going up, so do you know how we can check the actual true disk space used by the local drive then? Thank you – teeeeee Jan 24 '23 at 08:45
  • I'll let someone else with my Linux CLI experience answer that one. You may also get more responses by posting it as a question rather than a comment on an old answer. – BMitch Jan 24 '23 at 14:12
4

Another way with build-in driver local:

docker volume create --opt type=none --opt device=/home/ubuntu/data/ --opt o=bind data_db

(This use DimonVersace example with: data_db declared as external named volume in docker-compose and /home/ubuntu/data/ as the folder on the host machine)

user1707414
  • 3,833
  • 2
  • 18
  • 19
  • 1
    Why is this solution not more famous? I've used this for years and don't see any downsides of it. – Roemer Jul 03 '22 at 18:58