# Using Volumes in Docker

Docker volumes are a persistent storage mechanism that decouples your data from the container lifecycle, letting containers retain data across restarts and removals and allowing multiple containers to share a common data source. Unlike bind mounts, Docker manages volumes internally and stores them under `/var/lib/docker/volumes/` on Linux systems. This guide shows how to create and inspect Docker volumes, interact with their contents, use them in Docker Compose workflows, back up and restore volume data, and reference volumes with the `VOLUME` instruction in a Dockerfile. By the end, you'll have a container with a named, persistent volume that you can inspect, back up, restore, and manage through both the CLI and Docker Compose, following Docker storage management practices documented in [**Vultr Docs**](https://docs.vultr.com/how-to-use-volumes-in-docker).

---

## Quick Reference

The following commands demonstrate how to manage persistent data using volumes:

```console
docker volume create web_content
docker container run -d -v web_content:/app/data --name volume_demo httpd
docker volume ls
docker volume inspect web_content
docker volume rm web_content
docker volume prune
```

## When to Use Docker Volumes

Use Docker volumes instead of bind mounts when:

- You want data to persist across container restarts or removals.
- You need consistent storage behavior across Linux, macOS, and Windows.
- You prefer Docker-managed storage lifecycle and internal volume handling.
- You plan to back up or restore application data regularly.
- You need to share persistent data between multiple containers.

Bind mounts are more suitable for development environments where code changes on the host need to reflect immediately in the container. For production environments, Docker volumes are the preferred choice due to their portability, safety, and lifecycle integration.

## Manually Create and Link Volumes

**1\. Create a Docker volume:**

```console
docker volume create web_content
```

This creates a Docker-managed volume named `web_content`. Docker stores it internally and tracks its metadata.

Output:

```plaintext
web_content
```

**2\. Run a container and mount the volume:**

```console
docker container run -d -v web_content:/usr/local/apache2/htdocs --name apache_with_volume httpd
```

This launches a container with the `httpd` image and mounts the named volume at the web server's root. `-d` runs the container in detached mode, `-v web_content:/usr/local/apache2/htdocs` mounts the volume to Apache's web root, and `--name apache_with_volume` assigns the container a custom name.

Output:

```plaintext
03d0120e51dd822148d4c4c0464473cdad8a916e22ba5685ec495e47573dc722
```

**3\. List all volumes:**

```console
docker volume ls
```

Output:

```plaintext
DRIVER    VOLUME NAME
local     web_content
```

**4\. Inspect the volume:**

```console
docker volume inspect web_content
```

This confirms the volume's internal mount path and attributes, useful for debugging and manual access.

Output:

```plaintext
[
   {
       "CreatedAt": "2025-06-06T17:38:52Z",
       "Driver": "local",
       "Labels": null,
       "Mountpoint": "/var/lib/docker/volumes/web_content/_data",
       "Name": "web_content",
       "Options": null,
       "Scope": "local"
   }
]
```

**5\. List all containers:**

```console
docker container ls -a
```

Output:

```plaintext
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS          PORTS     NAMES
03d0120e51dd   httpd     "httpd-foreground"   46 seconds ago   Up 45 seconds   80/tcp    apache_with_volume
```

**6\. Check volume usage by container:**

```console
docker container inspect --format={{.Mounts}} apache_with_volume
```

This shows where the volume is mounted inside the container, validating its usage path.

Output:

```plaintext
[{volume web_content /var/lib/docker/volumes/web_content/_data /usr/local/apache2/htdocs local z true }]
```

**7\. Stop the container:**

```console
docker container stop apache_with_volume
```

This halts the container without removing it, preserving the volume and data.

Output:

```plaintext
apache_with_volume
```

**8\. Remove the container:**

```console
docker container rm apache_with_volume
```

This removes the container while leaving the named volume intact for reuse.

Output:

```plaintext
apache_with_volume
```

**9\. Remove the volume:**

```console
docker volume rm web_content
```

This permanently removes the volume and its contents.

Output:

```plaintext
web_content
```

> **Warning:** Removing a volume deletes all stored data. Ensure backups exist before running this command in production environments.

## Use Volumes in Dockerfiles

While Dockerfiles can't create named volumes, they can define mount points expected at runtime using the `VOLUME` instruction. Docker automatically creates an anonymous volume for such paths unless you explicitly mount a named one when running the container.

**1\. Navigate to the user's home directory and create a** `docker` **directory:**

```console
cd ~
mkdir docker
```

**2\. Navigate into the new directory:**

```console
cd docker/
```

**3\. Create a Dockerfile:**

```console
nano Dockerfile
```

**4\. Add the following content to the Dockerfile:**

```dockerfile
FROM httpd
VOLUME ["/usr/local/apache2/htdocs"]
CMD ["httpd-foreground"]
```

Save and close the file. This Dockerfile builds an Apache-based image that treats `/usr/local/apache2/htdocs` as a volume path.

**5\. Build the image:**

```console
docker image build -t apache_with_volume .
```

This builds the Dockerfile and creates a new image named `apache_with_volume`.

Output:

```plaintext
[+] Building 0.1s (5/5) FINISHED                            docker:default
 => [internal] load build definition from Dockerfile        0.0s
 => => transferring dockerfile: 110B                        0.0s
 => [internal] load metadata for docker.io/library/httpd    0.0s
 => => writing image sha256:c886f948f696b...                0.0s
 => => naming to docker.io/library/apache_with_volume       0.0s
```

**6\. Run the container:**

```console
docker container run -d --rm apache_with_volume
```

`-d` runs the container in detached mode, and `--rm` automatically removes the container when it exits. Docker automatically creates an anonymous volume and mounts it at `/usr/local/apache2/htdocs`, as defined in the Dockerfile.

To use a named volume instead of an anonymous one, pass the `-v` flag explicitly:

```console
docker container run -d --rm -v web_content:/usr/local/apache2/htdocs apache_with_volume
```

This lets you control the volume lifecycle while still benefiting from Dockerfile-defined mount points.

## Interact with Docker Volumes

Docker does not let you directly mount volumes on the host system, but you can access and modify volume contents from within a running container.

**1\. Start a container with the volume mounted:**

```console
docker container run -d --name apache_with_volume -v web_content:/usr/local/apache2/htdocs httpd
```

Output:

```plaintext
03d0120e51dd8221...
```

**2\. Create an HTML file on your host machine:**

```console
echo "<h1>Hello Apache from docker volume</h1>" > index.html
```

**3\. Copy the file into the container:**

```console
docker cp index.html apache_with_volume:/usr/local/apache2/htdocs/
```

Output:

```plaintext
Successfully copied 2.05kB to apache_with_volume:/usr/local/apache2/htdocs/
```

**4\. View the file contents inside the container:**

```console
docker exec apache_with_volume \
   cat /usr/local/apache2/htdocs/index.html
```

Output:

```plaintext
<h1>Hello Apache from docker volume</h1>
```

**5\. Back up the volume from inside the container:**

```console
docker exec apache_with_volume \
   tar czf /usr/local/apache2/htdocs/httpd_backup.tar.gz -C /usr/local/apache2/htdocs .
```

This creates a compressed `.tar.gz` archive of the volume's contents inside the container.

**6\. Copy the backup file from the container to the host:**

```console
docker cp apache_with_volume:/usr/local/apache2/htdocs/httpd_backup.tar.gz .
```

Output:

```plaintext
Successfully copied 2.05kB to /home/john/docker/.
```

**7\. Restore the volume by copying the archive back and extracting it:**

```console
docker cp httpd_backup.tar.gz apache_with_volume:/usr/local/apache2/htdocs/
```

```console
docker exec apache_with_volume \
   tar xzf /usr/local/apache2/htdocs/httpd_backup.tar.gz -C /usr/local/apache2/htdocs
```

The first command copies the backup archive into the container, and the second extracts its contents into the volume directory.

## Use Volumes with Docker Compose

Docker Compose supports named volumes natively — they're automatically created and managed alongside the service lifecycle.

**1\. Create a** `docker-compose` **directory in your home path:**

```console
cd ~
mkdir docker-compose
```

**2\. Navigate to the new directory:**

```console
cd docker-compose/
```

**3\. Create a** `docker-compose.yml` **manifest:**

```console
nano docker-compose.yml
```

**4\. Add the following YAML content to the file:**

```yaml
services:
   web:
       image: httpd
       volumes:
           - web_content:/usr/local/apache2/htdocs

volumes:
   web_content:
```

This tells Docker Compose to create a volume named `web_content` and mount it to the container at `/usr/local/apache2/htdocs`.

**5\. Start the service:**

```console
docker compose up -d
```

This builds and runs the container while automatically creating the declared volume and network.

Output:

```plaintext
[+] Running 3/3
✔ Network docker-compose_default       Created                    0.2s 
✔ Volume "docker-compose_web_content"  Created                    0.0s 
✔ Container docker-compose-web-1       Started                    0.6s
```

**6\. Inspect the volume created by Docker Compose:**

```console
docker volume inspect docker-compose_web_content
```

Replace `docker-compose_web_content` with the name of your Docker Compose volume. This confirms that the named volume declared in the Compose file was created and is mounted as expected.

Output:

```plaintext
[
    {
        "Name": "docker-compose_web_content",
        "Mountpoint": "/var/lib/docker/volumes/docker-compose_web_content/_data",
        ...
    }
]
```

**7\. Shut down the service and remove the volume:**

```console
docker compose down -v
```

`down` stops and removes containers, networks, and volumes, and `-v` ensures that named volumes declared in the Compose file are also deleted.

Output:

```plaintext
[+] Running 3/3
✔ Container docker-compose-web-1     Removed                      1.3s 
✔ Network docker-compose_default     Removed                      0.2s 
✔ Volume docker-compose_web_content  Removed                      0.0s
```

## Next Steps

- Move your own stateful services (databases, CMS uploads, app data) from bind mounts to named volumes for safer, more portable persistence.
- Script the backup/restore workflow (`tar` inside the container + `docker cp`) into a reusable shell script you can run on a schedule.
- Add volume definitions to an existing Docker Compose project instead of managing volumes manually with the CLI.
- Review `docker volume prune` to clean up unused volumes once you're comfortable with which ones are safe to remove.

For the full guide with additional tips, visit the original article on [**Vultr Docs**](https://docs.vultr.com/how-to-use-volumes-in-docker).
