Introduction: Why Data Persistence Matters

Welcome back! In the last lesson, you learned how to build your own Docker image for a simple web application, run it as a container, and access it from your browser. You also saw how easy it is to start, stop, and remove containers. However, there is an important detail about containers that you need to understand as you move forward: containers are ephemeral. This means that any files created or changed inside a container will disappear as soon as the container is removed.

Imagine you are running a web app that writes logs, stores user uploads, or saves configuration files. If you remove the container, all that data is lost. In real-world applications, you almost always need to keep some data between runs. For example, you might want to keep server logs for debugging, or you might need to store user-generated content. This is where data persistence comes in. In this lesson, you will learn how to make sure important files created by your app are saved on your computer, even after the container is gone.

Bind Mounts 101: Options and What We'll Use

Docker provides a few ways to persist data. The two most common are bind mounts and named volumes. A bind mount lets you map a specific directory or file from your host machine (your computer) into the container. This means anything the container writes to that directory will actually be saved on your host, and you can access it even after the container is removed. A named volume is managed by Docker and is stored in a special location on your system. Both methods are useful, but for this lesson, you will use a bind mount. This approach is simple and makes it easy to see the files your app creates right in your project folder.

You will mount a logs directory from your host into the container. This way, when your app writes log files, they will be saved on your computer and will not disappear when the container is removed.

App Update: Write Logs to a File

To see data persistence in action, you will update your web app so that it writes a log entry every time someone visits the home page. The app will create a logs directory (if it does not already exist) and append a line to logs/access.log with the current date and time.

Here is the updated app.py:

This code checks if the logs directory exists and creates it if needed. Every time the root URL is accessed, it appends a line to logs/access.log with the current timestamp. The log file path is relative to the app's working directory, which keeps things simple and predictable.

You will also need a requirements.txt file to specify your dependencies with pinned versions:

Rebuild the Image with Logging

Since you have changed your application code, you need to rebuild your Docker image. This is a good reminder that any time you change your app's code or dependencies, you must rebuild the image for those changes to take effect in new containers.

Your Dockerfile for this app looks like this:

Notice that the Dockerfile sets the working directory to /app, copies in your requirements.txt first, installs the dependencies with pip install --no-cache-dir -r requirements.txt, then copies in your app.py.

The Dockerfile also exposes port 3000 and sets the default command to run your app. To build the new image using the Docker CLI, open your terminal and make sure you are in the docker-app directory where your Dockerfile, requirements.txt, and app.py are located. Then run:

The docker build command reads your Dockerfile and creates a new image. The option tags your image with a name and version, making it easy to identify. The at the end tells Docker to use the current directory as the build context, meaning it will look for the and any files to copy from this location.

Run the Container with a Bind Mount

Now you are ready to run your container and mount a directory from your host into the container. First, create a logs directory on your host machine. This is where the log files will be saved. Open your terminal and run:

The -p flag ensures that the command succeeds even if the directory already exists.

Next, you will use the docker run command with the -v flag to create a bind mount. The -v flag takes the format host-path:container-path, which maps a directory on your host to a directory inside the container. On macOS or Linux, you can use $(pwd)/logs to refer to the full path of the logs directory in your current working directory. On Windows (using Command Prompt), you would use %cd%\logs, or in PowerShell, ${PWD}\logs.

Here is the command for macOS/Linux:

Let's break down what each part does:

  • -d Runs the container in detached mode (in the background).
  • --name my-logging-app Gives your container a friendly name.
Verify Persistence Across Container Removal

To test that everything is working, open your browser and visit http://localhost:3000. Each time you refresh the page, the app will write a new line to the log file. You can check the contents of the log file on your host with:

You should see output like this:

Now, stop and remove the container using the Docker CLI. First, stop the running container:

The docker stop command sends a signal to the container to shut down gracefully. You should see the container name echoed back, confirming it has stopped.

Next, remove the container:

The docker rm command deletes the stopped container. Again, you should see the container name echoed back.

Now check the log file again:

You will see that the log file still exists and contains all the entries you made. This proves that the data persisted on your host, even after the container was removed. This is the power of bind mounts — your data lives on your host machine, independent of the container's lifecycle.

Common Pitfalls and Quick Fixes

When working with bind mounts, there are a few common issues you might run into. If you use the wrong path for the host directory, Docker might create a new directory in an unexpected place, or the mount might fail. Always double-check your paths and use absolute paths if you are unsure. If the logs folder does not exist on the host, Docker will usually create it, but it is a good habit to create it yourself first.

Permissions can also cause problems. If your user does not have write access to the host directory, the container might not be able to write logs. On some systems, like macOS with Docker Desktop, you may need to give Docker permission to access certain folders. On Linux, SELinux can sometimes block access to mounted directories. If you see permission errors, check your Docker settings and folder permissions.

Another thing to remember is that if you change your app code, you need to rebuild the image before running a new container. If you forget, you might be running an old version of your app.

Summary & Next Steps

In this lesson, you learned why data persistence is important when working with Docker containers and how to use bind mounts to save data from your containers onto your host machine. You updated your web app to write logs, rebuilt your Docker image using the Docker CLI, and ran a container with a bind mount to ensure log files persisted even after the container was removed. You also learned about common pitfalls with bind mounts and how to avoid them.

Next, you'll get hands-on practice using Docker CLI commands to work with bind mounts and persist data, reinforcing these concepts and preparing you for real-world Docker projects.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal