feat: use host bind mounts for persistent data

This commit is contained in:
bisco
2026-06-25 10:12:41 +02:00
parent c66dd7e511
commit 0a590989bb
12 changed files with 232 additions and 42 deletions
+7 -5
View File
@@ -18,8 +18,9 @@ sanitized theme modifications, while a must-use plugin owns Shows and Gallery cu
post types so structured content is not lost when changing themes.
NGINX is the only public entry point. WP-CLI provides an idempotent opt-in bootstrap;
Certbot provides opt-in direct TLS. Docker networks isolate the database and functional
tests use separate volumes.
Certbot provides opt-in direct TLS. Docker networks isolate the database. Runtime state
uses host-based bind mounts with a preparation script for ownership/mode, while
functional tests use separate Docker volumes.
## Consequences
@@ -48,11 +49,12 @@ headers, and fail-closed TLS. Admin MFA and allowlisting remain external control
## Operational impact
Database and WordPress files are coordinated state and must be backed up together.
Host bind-mount paths must be permissioned for the image users before startup.
Operators must monitor upstream security releases and rebuild pinned images. SMTP and
off-host storage are not part of this minimal base.
## Rollback
Revert the implementation commit and rebuild. Preserve WordPress, database, and
certificate volumes unless data deletion is intentional. Restore data only from a
verified coordinated backup.
Revert the implementation commit and rebuild. Preserve the configured WordPress,
database, and certificate host directories unless data deletion is intentional. Restore
data only from a verified coordinated backup.
+4 -3
View File
@@ -16,8 +16,9 @@ activates the theme, configures the site, and creates realistic demo content. Ce
another optional service, enabled only for direct deployments. It shares challenge and
certificate volumes with NGINX but has no container-control access.
Persistent state lives in `db_data`, `wordpress_data`, and the certificate volumes.
Functional tests replace the first two with isolated test volumes and reach NGINX via
an internal `azionelab.org` network alias.
Persistent state lives in host-based bind mounts configured by `DB_DATA_PATH`,
`WORDPRESS_DATA_PATH`, `LETSENCRYPT_DATA_PATH`, and `CERTBOT_CHALLENGES_PATH`.
Functional tests replace the database and WordPress mounts with isolated Docker test
volumes and reach NGINX via an internal `azionelab.org` network alias.
See [ADR-0001](adr/0001-wordpress-single-page.md).
+18 -4
View File
@@ -12,6 +12,19 @@ docker compose --profile tools run --rm wp-cli /scripts/bootstrap.sh
NGINX binds to loopback ports 8080/8443. WordPress and MariaDB remain private. The
bootstrap is safe to rerun and does not duplicate demo records.
The default persistent paths are host-based bind mounts under `./runtime`. Before the
first start, run:
```bash
./scripts/prepare-host-volumes.sh
```
The script reads `.env`, creates the configured paths, and applies the expected
container ownership. For Linux hosts this avoids the common problem where Docker
creates missing bind-mount directories as `root:root` and WordPress or MariaDB later
cannot write to them. If a platform uses different image UIDs/GIDs, override
`MARIADB_VOLUME_UID/GID`, `WORDPRESS_VOLUME_UID/GID`, or `CERTBOT_VOLUME_UID/GID`.
## Production
Required controls:
@@ -35,7 +48,8 @@ behavior, removing only the staging certificate volume when necessary.
## State and rollback
Database and WordPress file volumes must be backed up together. Code rollback is a
container rebuild from a prior commit and does not require deleting volumes. Never use
`docker compose down --volumes` where content must survive. Test database restoration
in a disposable environment before any production restore.
Database and WordPress file directories must be backed up together. Code rollback is a
container rebuild from a prior commit and does not require deleting host data
directories. Never delete the paths configured by `DB_DATA_PATH`, `WORDPRESS_DATA_PATH`,
or `LETSENCRYPT_DATA_PATH` where content/certificates must survive. Test database
restoration in a disposable environment before any production restore.
+10 -4
View File
@@ -6,6 +6,7 @@
docker compose up --build -d
docker compose ps
docker compose logs -f proxy wordpress db certbot
./scripts/prepare-host-volumes.sh
docker compose --profile tools run --rm wp-cli -c 'wp core version'
docker compose down
```
@@ -24,9 +25,11 @@ image rebuilds are the update path.
## Backup and restore
Create database and `wordpress_data` backups in one maintenance window. Backups contain
credentials, accounts, contact information, and uploaded media; encrypt them, restrict
access, set retention, and store copies off-host.
Create database and WordPress file backups in one maintenance window. The default host
paths are `./runtime/db`, `./runtime/wordpress`, `./runtime/letsencrypt`, and
`./runtime/certbot/www`, unless overridden in `.env`. Backups contain credentials,
accounts, contact information, and uploaded media; encrypt them, restrict access, set
retention, and store copies off-host.
A restore is destructive. Validate it on isolated volumes, then stop WordPress, restore
the database and file volume together, restart, and verify the homepage, media,
@@ -34,7 +37,10 @@ the database and file volume together, restart, and verify the homepage, media,
## Known risks
- Local named volumes are not backups.
- Local host directories are not backups.
- A host-based volume may be unreadable by the application if created with the wrong
owner or mode; run `./scripts/prepare-host-volumes.sh` after changing paths or image
user IDs.
- SMTP is not configured; WordPress password-reset email needs an external mail service.
- Admin MFA and network allowlisting are deployment concerns and are not bundled.
- WordPress plugins expand the attack surface; install only reviewed, maintained,
+11 -3
View File
@@ -14,10 +14,17 @@
## Images or theme are missing
1. Confirm `wordpress_data` is mounted in both WordPress and WP-CLI.
1. Confirm `WORDPRESS_DATA_PATH` is mounted in both WordPress and WP-CLI.
2. Run `docker compose --profile tools run --rm wp-cli -c 'wp theme status azionelab'`.
3. Verify file ownership before changing permissions; never make the tree world-writable.
## A service cannot write to its volume
1. Stop the affected service.
2. Confirm the relevant `.env` path points to the intended host directory.
3. Run `./scripts/prepare-host-volumes.sh`.
4. Start the service and inspect logs without printing secret values.
## Certificate issuance fails
Verify public DNS, inbound port 80, the operator email, and staging mode. Request a
@@ -26,5 +33,6 @@ Avoid repeated production-CA retries while debugging.
## Rollback
Revert the deployment commit and rebuild while preserving all named volumes. Restore
database/files only for a data rollback and only from a verified coordinated backup.
Revert the deployment commit and rebuild while preserving all host data directories.
Restore database/files only for a data rollback and only from a verified coordinated
backup.
+3
View File
@@ -21,6 +21,9 @@
- Containers are not privileged and do not use host networking or the Docker socket.
WordPress/Apache retains the capabilities needed by the official image internally,
but no WordPress port is published.
- Persistent state uses host-based bind mounts. Keep those paths outside the public web
root, restrict host access, never make them world-writable, and run
`./scripts/prepare-host-volumes.sh` when paths or image user IDs change.
- WordPress/Apache access logs are disabled to avoid duplicate client metadata and
healthcheck noise; NGINX remains the single request log and PHP warnings/errors stay
visible.
+6 -5
View File
@@ -14,11 +14,12 @@ docker compose config --quiet
LETSENCRYPT_ENABLED=1 docker compose config --quiet
```
The override replaces normal database and WordPress volumes, maps `azionelab.org` to
the internal proxy, and never publishes an extra port. Browser tests cover content and
section order, contact actions, mobile overflow/navigation, semantic landmarks, image
alternatives, admin routing, security headers, blocked sensitive routes, and unknown
virtual hosts.
The normal stack uses host-based bind mounts under `./runtime` by default. The test
override replaces database and WordPress state with isolated Docker volumes, maps
`azionelab.org` to the internal proxy, and never publishes an extra port. Browser tests
cover content and section order, contact actions, mobile overflow/navigation, semantic
landmarks, image alternatives, admin routing, security headers, blocked sensitive
routes, and unknown virtual hosts.
Subjective visual review and a real-device accessibility audit remain manual release
checks.