DEV Community

Alessio Michelini
Alessio Michelini

Posted on

How to persist data with docker compose

One problem I see when people start to work with docker and docker compose is the problem of understanding what docker volumes are and what's their use.
And in many cases you don't really need them, for example if you have a container that runs in isolation and has no dependencies, and you are not going to store any data, or maybe it connects to an external database that it's on the cloud.
But in other cases you do need to persist data on a container, thus the need for docker volumes.
But let's try to explain how they work with a simple example.

You have a project which has two services, one is the application you are building, like some node.js or Python APIs, which connects to a the other service, a database, either Mongodb or MySQL (doesn't really matter for our example).
So perhaps your docker-compose.yml looks like this:

version: '3'
services:
  api:
    build: .
    ports:
      - 9000:9000
    command: npm run dev
    networks:
      - my-network
  mongo:
    image: mongo:4.4
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=pass
    ports:
      - '27017:27017'
    networks:
      - my-network
networks:
  my-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

As it is, once you start the containers, everything will work fine, you can save data on the database, you can read from it, etc...
But, the very moment you do docker-compose down, the containers will be removed by docker, and all the data will be gone!.
And that's probably fine with you as you don't need anything to persist, but in the other case, what you need to do is to use volumes.

Bind mounts vs Volumes

There are two ways where you can create a volume, bind mounts and volumes.
Whichever you choose, once you have set up a volume to the folder where the data is stored in the container, if you do a docker-compose down, and then a docker-compose up, your data will not be erased and it will become persistent.

Bind mounts

Bind Mounts are volumes that mount to a host path, and they can be modified by other processes outside docker.
Essentially it shares a folder with your host computer and the container, and it's defined with the syntax <host-folder>:<container-folder>.
With a bind mount our project will look like this:

mongo:
    image: mongo:4.4
    ...other settings
    volumes:
      - ./db:/data/db
Enter fullscreen mode Exit fullscreen mode

So what you will see that from your host machine, you will see a .db folder, which is going to contain all the data stored in the database.

Note: /data/db is where the MongoDB image store the databases data, this will be different on other databases like Redis, MariaDB, MySQL, etc...

The only problem with bind mounts, is that the folder is managed by the host computer, so if you change file permissions, or you delete a file for example, you will corrupt the data in the container, which could lead to errors or the container not working anymore.

Volumes

Volumes on the other hand live under the docker engine, it behaves more or less like a virtual hard drive, managed by docker and the files inside it are not accessible from the host computer.

Add a volume to docker-compose is rather simple, it's similar to bind mounts, but with the difference that you need to add the volume under the volumes array in the main docker-compose.yml, and in the service configuration you use the syntax <volume-name>:<container-path>, for example if we want to store the data in a container called mongodata, our docker-compose.yml is going to look something like this:

version: '3'
services:
  api:
    ...api configuration
  mongo:
    ...mongo configuration
    volumes:
      - mongodata:/data/db
networks:
  my-network:
    driver: bridge
volumes:
  mongodata:
Enter fullscreen mode Exit fullscreen mode

Note, the colon after mongodata is not an error.

Same as bind mounts, now when you stop the containers and restart them, the data will be there.

How I do remove a volume?

In case you need to remove a volume, for the bind mounts is just a matter to stop the containers, and delete the folder.
In the case of a volume you need to use a docker command to delete it, but it's rather easy:

docker volume rm mongodata
Enter fullscreen mode Exit fullscreen mode

That's it!

Bonus

A couple of common commands you can use to manage volumes with docker:

# create a volume
docker volume create myvolume

# list volums
docker volume ls

# delete a volume
docker volume rm myvolume
Enter fullscreen mode Exit fullscreen mode

Discussion (0)