126 lines
3.3 KiB
Markdown
126 lines
3.3 KiB
Markdown
---
|
|
title: Persistent Volumes
|
|
description: Storage patterns for keeping container data durable across restarts and upgrades
|
|
tags:
|
|
- containers
|
|
- docker
|
|
- storage
|
|
category: containers
|
|
created: 2026-03-14
|
|
updated: 2026-03-14
|
|
---
|
|
|
|
# Persistent Volumes
|
|
|
|
## Introduction
|
|
|
|
Containers are disposable, but application data usually is not. Persistent volumes provide storage that survives container restarts, recreation, and image upgrades.
|
|
|
|
## Purpose
|
|
|
|
Use persistent volumes to:
|
|
|
|
- Preserve databases, uploads, and application state
|
|
- Separate data lifecycle from container lifecycle
|
|
- Simplify backup and restore workflows
|
|
- Reduce accidental data loss during redeployments
|
|
|
|
## Architecture Overview
|
|
|
|
Docker storage typically falls into three categories:
|
|
|
|
- Named volumes: managed by Docker and usually the best default for persistent app data
|
|
- Bind mounts: direct host paths mounted into a container
|
|
- Tmpfs mounts: memory-backed storage for temporary data
|
|
|
|
## Storage Patterns
|
|
|
|
### Named volumes
|
|
|
|
Named volumes are portable within a host and reduce the chance of coupling to host directory layouts.
|
|
|
|
```bash
|
|
docker volume create postgres-data
|
|
docker run -d \
|
|
--name db \
|
|
-v postgres-data:/var/lib/postgresql/data \
|
|
postgres:16
|
|
```
|
|
|
|
### Bind mounts
|
|
|
|
Bind mounts are useful when:
|
|
|
|
- The application expects editable configuration files
|
|
- You need direct host visibility into files
|
|
- Backups are based on host file paths
|
|
|
|
Example:
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name caddy \
|
|
-v /srv/caddy/Caddyfile:/etc/caddy/Caddyfile:ro \
|
|
-v /srv/caddy/data:/data \
|
|
caddy:2
|
|
```
|
|
|
|
### Permissions and ownership
|
|
|
|
Many container storage issues come from mismatched UID and GID values between the host and containerized process. Check the image documentation and align ownership before assuming the application is broken.
|
|
|
|
## Configuration Example
|
|
|
|
Compose example with named volumes:
|
|
|
|
```yaml
|
|
services:
|
|
app:
|
|
image: ghcr.io/example/app:1.2.3
|
|
volumes:
|
|
- app-data:/var/lib/app
|
|
|
|
db:
|
|
image: postgres:16
|
|
volumes:
|
|
- db-data:/var/lib/postgresql/data
|
|
|
|
volumes:
|
|
app-data:
|
|
db-data:
|
|
```
|
|
|
|
## Troubleshooting Tips
|
|
|
|
### Data disappears after updating a container
|
|
|
|
- Verify the service is writing to the mounted path
|
|
- Check whether a bind mount accidentally hides expected image content
|
|
- Inspect mounts with `docker inspect <container>`
|
|
|
|
### Permission denied errors
|
|
|
|
- Check ownership and mode bits on bind-mounted directories
|
|
- Match container user expectations to host permissions
|
|
- Avoid mounting sensitive directories with broad write access
|
|
|
|
### Backups restore but the app still fails
|
|
|
|
- Confirm the restored data matches the application version
|
|
- Restore metadata such as permissions and database WAL files if applicable
|
|
- Test restores on a separate host before using them in production
|
|
|
|
## Best Practices
|
|
|
|
- Use named volumes for most stateful container data
|
|
- Use bind mounts deliberately for human-managed configuration
|
|
- Keep backups separate from the production host
|
|
- Record where every service stores its critical state
|
|
- Test restore procedures, not only backup creation
|
|
|
|
## References
|
|
|
|
- [Docker: Volumes](https://docs.docker.com/engine/storage/volumes/)
|
|
- [Docker: Bind mounts](https://docs.docker.com/engine/storage/bind-mounts/)
|
|
- [Docker: Tmpfs mounts](https://docs.docker.com/engine/storage/tmpfs/)
|