Home TutorialsDocker Using Bind Mounts in Docker Containers

Using Bind Mounts in Docker Containers

by Atif Azad

In the previous part of this Docker tutorial series, we dockerised a PHP app with PostgreSQL database. We noted, however, that upon removal of Postgres container, the data was lost which should not be the case. We also noted for the app container that the image must be built again for the updated code to be synced with the code running in container. Therefore that Docker setup was also not sufficient for local development. In this tutorial, we’ll solve both of those issues by using bind mounts in both Docker containers.

Understanding the database persistence problem

If you are here as a result of Google search then probably you know the data persistence problem. But if you haven’t faced/realised it yet do the following steps for understanding. Please refer to Run a PostgreSQL Container section of the previous part if you don’t understand any of the steps.

  • Run a Postgres container with following command.
> docker container run -p 5433:5432 --network dock-net --name pg-container -e POSTGRES_PASSWORD=1234 postgres:12.2
  • Connect to this database host using any database client application like PGAdmin.
  • Create a database testdb.
  • Stop and remove the container with following commands.
> docker container stop pg-container
> docker container rm pg-container

The container gets removed.

  • Now run Postgres container again with same command and connect to DB host in your database client app.
> docker container run -p 5433:5432 --network dock-net --name pg-container -e POSTGRES_PASSWORD=1234 postgres:12.2
  • You’ll see that testdb that you created above is no more available. The reason is that the database storage directory was owned by the container. Therefore when the container was removed, that data directory was also removed.

This is not an ideal situation if you are using Docker setup in production or even in development. You’d not want to setup your DB again, if you had to remove the container for any reason.

Solution: Data persistence with bind mount volume

The solution is quite easy and simple.

  • Run following command in Terminal. (If a container with name pg-container exists, remove that container first docker container rm pg-container --force)
> docker container run \
-p 5433:5432 \
--network dock-net \
--name pg-container \
-e POSTGRES_PASSWORD=1234 \
--mount type=bind,target=/var/lib/postgresql/data,source=/path/to/folder/on/host \
postgres:12.2

This command is same as before except that we have used --mount option and that we have split the command over multiple lines. --mount option has several components but we need to use only three here.

For all other options used in above docker container run command you can see explanation in the previous tutorial. Below is the explanation of parameter used with --mount option.

source=/path/to/folder/on/host

Create/use a folder on your host machine where you want to persist your databases. source should have the absolute path to that folder.

target=/var/lib/postgresql/data

This is the path to default data directory of Postgres. As Postgres is running inside Docker container, this path is not host path but container’s.

type=bind

type can have value bind, volume or tmpfs. For bind mount we must use type=bind. With this, the source folder will be mounted into the container and will bind to the target path. This means that the source folder will be accessible to Postgres on container’s internal path specified as target. The source folder is managed by host and removing the container will not remove it.

The following image from official Docker docs shows how bind mount works compared to other types.

Bind mount compared to volume and tmpfs mount - from Docker Official Docs
  • In your Postgres client app, connect to this DB host.
  • Create a database testdb.
  • stop and remove (rm) the container.
> docker container stop pg-container
> docker container rm pg-container
  • Now, create the container again with the same command with bind mount.
> docker container run \
-p 5433:5432 \
--network dock-net \
--name pg-container \
-e POSTGRES_PASSWORD=1234 \
--mount type=bind,target=/var/lib/postgresql/data,source=/path/to/folder/on/host \
postgres:12.2
  • Connect to DB host again in your Postgres client app (eg. PGAdmin), you’ll see that the database testdb is still available this time.

Now you can safely use Docker and even remove the Postgres container without worrying about data loss!

Problem: Need to docker build on every code update

In previous tutorial we Dockerised a Python app which runs perfectly fine with the code provided at build time. However, if we have to make changes to code, for example, to add a new API endpoint. In such case, we have to run docker build command again to see the impact of new changes. This is not a feasible approach if you are developing app in dockerised environment.

To understand and experience this problem, complete the application setup as done in previous tutorial: Dockerise a Python app with PostgreSQL. Once you have verified that the app is running, go ahead and make some changes in api.py. For example, add a new function as below:

@app.route('/api/welcome', methods=['GET'])
def index():
    return 'welcome!'

If you hit in your browser or Postman this URL: http://127.0.0.1:8000/api/welcome, you’ll get 404 / Not Found error because the changes in code were made on host but the container doesn’t know about those changes.

Solution: Bind mount the application directory

To solve this problem, the solution is same as we did for database persistence. Therefore execute the following command (remove flask-api-exp container, if it already exists, using command: docker container rm flask-api-exp --force and then execute…)

docker container run \
-p 8000:5000 \
--network dock-net \
--name flask-api-exp \
--mount type=bind,target=/dockerised-example,source=/host/path/to/flask-api-example \
flask-docker-exp:latest

The command is using --mount exactly the same way as explained above. With type=bind, it bind-mounts the source folder from host to the target folder path in container. This means that target path is actually referencing source path. Therefore any changes made in the content of source are also reflected in target right away.

Now the container is running! To verify that the problem is solved, please add following code again in api.py.

@app.route('/api/welcome', methods=['GET'])
def index():
    return 'welcome!'

Now hit http://127.0.0.1:8000/api/welcome in your browser or Postman. You will see Welcome! in response which means that the bind mount is working fine 🙂

What’s next!

The next exciting step is to manage the Docker containers using docker-compose which is really helpful tool in setting up development environment using Docker.

0 0 votes
Article Rating

You may also like

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

Privacy & Cookies Policy
0
Would love your thoughts, please comment.x
()
x