U principle §6: bind mounts only, never named volumes
Promote 'don't use named volumes' from a mysql/redis-specific explanation to a hard rule that applies to anything needing persistence (ssh host keys, uploads, queue state, etc). Lead with the 'docker compose down -v wipes named volumes' rationale since that's the keystroke this rule is really protecting against. Add a counter-pattern example.
This commit is contained in:
parent
1d1a3b32f9
commit
2589d4baf4
|
|
@ -393,30 +393,64 @@ that's why each one starts with the app's slug.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Persistent data: `./docker-data/` in the repo, gitignored
|
## 6. Persistent data: `./docker-data/` bind mounts, never named volumes
|
||||||
|
|
||||||
Bind-mount mysql and redis storage to a `docker-data/` folder right next
|
**Rule: any service that needs to keep state between container restarts
|
||||||
to the source:
|
gets a bind mount under `./docker-data/<service>/`. Never a named docker
|
||||||
|
volume.** This applies to mysql, redis, ssh host keys, app uploads,
|
||||||
|
queue state, every "this dir needs to survive" case — same shape, same
|
||||||
|
location, no exceptions.
|
||||||
|
|
||||||
```
|
```
|
||||||
docker-data/
|
docker-data/
|
||||||
mysql/ # mysql:8.0 datadir
|
mysql/ # mysql:8.0 datadir
|
||||||
redis/ # redis dump.rdb
|
redis/ # redis dump.rdb
|
||||||
|
… # whatever else needs persistence
|
||||||
```
|
```
|
||||||
|
|
||||||
The folder is gitignored (`docker-data/` in `.gitignore`). It survives
|
The folder is gitignored (`docker-data/` in `.gitignore`). The deploy
|
||||||
`docker compose down`. The deploy script `mkdir -p`s it on first run so
|
script `mkdir -p`s it on first run so fresh boxes Just Work.
|
||||||
fresh boxes Just Work.
|
|
||||||
|
|
||||||
### Why not named docker volumes?
|
### Why bind mounts, not named volumes
|
||||||
|
|
||||||
|
The headline reason: **`docker compose down -v` wipes named volumes**.
|
||||||
|
That command is in too many people's muscle memory ("nuke the stack and
|
||||||
|
start clean") for the production datastore to be one accidental keystroke
|
||||||
|
away from gone. Bind mounts under the repo are immune — `down -v`
|
||||||
|
doesn't touch them.
|
||||||
|
|
||||||
|
The rest of the rationale:
|
||||||
|
|
||||||
- Trivially backed up — `tar -caf data.tar.xz docker-data/` from the
|
- Trivially backed up — `tar -caf data.tar.xz docker-data/` from the
|
||||||
repo root is the entire prod state.
|
repo root is the entire prod state.
|
||||||
- Survives container/image churn including accidental
|
- Survives container/image churn including accidental
|
||||||
`docker volume prune -af`.
|
`docker volume prune -af` and `docker system prune --volumes`.
|
||||||
- Discoverable — anyone with the repo can see where the data lives.
|
- Discoverable — anyone with the repo can see where the data lives.
|
||||||
- The repo path identifies which app owns the data when you have ten
|
- The repo path identifies which app owns the data when you have ten
|
||||||
apps on one host.
|
apps on one host.
|
||||||
|
- Restoring on a new host is `git clone && rsync docker-data/` — no
|
||||||
|
per-volume `docker volume create && docker run --rm -v ... tar` dance.
|
||||||
|
|
||||||
|
### Counter-pattern (do not do this)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# WRONG — named volume; `docker compose down -v` deletes the data.
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# RIGHT — bind mount; survives `down -v`.
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
volumes:
|
||||||
|
- ./docker-data/mysql:/var/lib/mysql
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue