Skip to main content

Docker Ep 4: Volumes & Data Persistence

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
docker - This article is part of a series.
Part 4: This Article
By default, all files created inside a container are stored on a writable container layer. When that container is deleted, the data is destroyed. Docker Volumes solve this by storing data outside the container filesystem.

TL;DR (Quick Summary)
#

  • Named Volumes: Managed by Docker in /var/lib/docker/volumes/. Best for production database persistence.
  • Bind Mounts: Mounts a specific host directory (e.g., ./src) directly into the container. Best for live development hot-reloading.
  • tmpfs Mounts: Stores data strictly in host RAM memory. Never writes to disk (great for sensitive secrets or temporary caches).

1. Storage Options Comparison
#

Storage MechanismManaged ByHost LocationTypical Use Case
Named VolumeDocker Engine/var/lib/docker/volumes/<name>/_dataDatabases (PostgreSQL, Redis), production assets.
Bind MountDeveloperAny arbitrary host directory (/home/user/app)Source code hot-reloading during development.
tmpfs MountLinux KernelHost System Memory (RAM)Session keys, temporary cache, high-speed ephemeral data.

2. Step-by-Step Lab: Preserving Database State
#

Let’s test data persistence across container removals using a Named Volume.

Step 1: Creating a Named Volume
#

Create a volume named postgres-data:

docker volume create postgres-data

Inspect volume metadata:

docker volume inspect postgres-data

Step 2: Launching Database with Volume Mount
#

Launch a PostgreSQL container mounted to postgres-data:

docker run -d \
  --name db-instance \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15-alpine

Step 3: Writing Sample Data
#

Create a sample table inside PostgreSQL:

docker exec -it db-instance psql -U postgres -c "CREATE TABLE users (id INT, name TEXT);"
docker exec -it db-instance psql -U postgres -c "INSERT INTO users VALUES (1, 'Rachmat');"

Step 4: Destroying the Container
#

Forcefully remove the database container:

docker rm -f db-instance

Step 5: Restoring Data in a New Container
#

Launch a brand new container using the exact same volume:

docker run -d \
  --name db-restored \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15-alpine

Verify that the table and records survived:

docker exec -it db-restored psql -U postgres -c "SELECT * FROM users;"

Expected Terminal Output:

 id |  name   
----+---------
  1 | Rachmat
(1 row)

3. Troubleshooting & Common Errors
#

Error 1: Permission denied on Bind Mounts
#

The Cause: File permissions mismatch between host user UID (e.g., 1000) and container user UID (e.g., 999). The Fix: Align UIDs when running the container: docker run -u $(id -u):$(id -g) ....


Summary & Next Steps
#

In this episode:

  • We analyzed Named Volumes, Bind Mounts, and tmpfs mounts.
  • We built a persistent database volume and verified data survival across container destruction.

Next, we move to Episode 5: Docker Compose for Local Microservices!

docker - This article is part of a series.
Part 4: This Article