wordpress: switch to dedicated MariaDB per site (was shared)

Reconsidered after the shared-MariaDB design's real cost became clear:
Kopia's generic backup (services/backup.sh) stops a service's
container to snapshot it, so a shared MariaDB instance would back up
-- and would have to be restored -- as one unit covering every site at
once. Restoring just one site's database to an earlier point meant
restoring the whole shared snapshot to a temporary location first and
manually extracting that site's data back out, not a direct restore.

Each site now gets its own dedicated MariaDB container embedded in its
own docker-compose.yml (same pattern as services/nextcloud.sh) instead
of registering a database on a shared instance:
- Removed _wordpress_ensure_shared_db() and the wordpress-db/
  wordpress_net shared resources entirely.
- Each site's compose file gets a `db` service (container
  <site>-db) on an explicitly-named per-site default network
  (<site>_net), so wp-cli's one-off container reliably joins the
  right network without depending on Docker Compose's implicit
  naming convention.
- DB creation goes through the mariadb image's own MYSQL_DATABASE/
  MYSQL_USER/MYSQL_PASSWORD env vars on first boot (same as
  nextcloud.sh) instead of an imperative `docker exec mysql -e
  "CREATE DATABASE..."` against a shared container.
- Root and site DB passwords are both reused across reruns (read from
  the existing .env), verified via a real update-mode rerun.

Tradeoff, stated in both the script's header comment and the generated
per-site README: more RAM per site (~100-150MB for a full MariaDB
container instead of a slice of one shared instance) in exchange for
independent backup/restore. Data was already fully isolated either way
(separate database + user, always required since WordPress's schema
uses generic table names) -- the shared-vs-dedicated choice was only
ever about the container/process, not the data.

Re-verified end-to-end against the fake docker shim: distinct ports,
distinct dedicated DB containers/networks per site, correct compose/
.env structure, credentials preserved across an update-mode rerun.

docs/vps-sizing-recommendations.md: updated to match -- WordPress
capacity recomputed for dedicated-per-site MariaDB (~580MB headroom at
4 sites, ~976MB at 2, vs. the shared design's ~700MB/~950MB).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBtExJcqxnokyZZKmphdug
This commit is contained in:
Claude
2026-08-09 21:23:24 +00:00
parent e96a257d8f
commit a5d57050b3
3 changed files with 109 additions and 147 deletions
+28 -16
View File
@@ -140,14 +140,23 @@ again; it just isn't part of the current baseline.
**WordPress — confirmed, 2-4 sites, light traffic, ecommerce-capable.**
`services/wordpress.sh` (new): multi-site from the start, every site named,
one shared MariaDB container instead of a dedicated database container per
site (same resource-sharing idea as `coturn`, scoped to WordPress's own
sites). Each site gets its own database + user within that shared
instance — required, not just tidy: WordPress's schema uses generic table
names (`wp_posts`, `wp_options`, etc.), so two installs sharing one database
with the same table prefix would collide. wp-cli automates the initial
install (title, admin account) so there's no per-site browser setup wizard,
and PHP limits are pre-tuned (256M memory, 64M uploads) for WooCommerce
each with its own **dedicated** MariaDB container (same pattern as
`services/nextcloud.sh`) — not a shared instance. Started as a shared-MariaDB
design (same resource-sharing idea as `coturn`) but switched to dedicated
per-site after weighing it against backup/restore: Kopia's generic backup
(`services/backup.sh`) stops a service's container to snapshot it, so a
shared instance would back up — and would have to be restored — as one unit
covering every site at once, not one site independently. Dedicated per-site
costs more RAM (a full MariaDB container each, ~100-150MB, instead of one
instance amortized across sites) in exchange for real isolation: each
site's database backs up and restores completely independently. Separate
databases were always required regardless of which model — WordPress's
schema uses generic table names (`wp_posts`, `wp_options`, etc.), so two
installs sharing one database with the same table prefix would collide —
the shared-vs-dedicated choice was only ever about the container/process,
never about the data being mixed. wp-cli automates the initial install
(title, admin account) so there's no per-site browser setup wizard, and PHP
limits are pre-tuned (256M memory, 64M uploads) for WooCommerce
specifically since "possible ecommerce" was part of the ask.
**Explicitly out of scope for this box** (wrong fit, not "can't run"):
@@ -182,15 +191,18 @@ specifically since "possible ecommerce" was part of the ask.
| Traccar (JVM) | ~425MB |
| NetBird client | ~35MB |
| ntfy, actualbudget, mealie | ~340MB combined |
| Shared MariaDB (WordPress, one-time) | ~180MB |
| WordPress × 4 sites (~120MB each — sized for possible WooCommerce, not a light blog) | ~480MB |
| **Total** | **~3.38GB** |
| WordPress × 4 sites (app ~80MB + dedicated MariaDB ~120MB each) | ~800MB |
| **Total** | **~3.52GB** |
Leaves roughly **~700MB headroom (~17%)** out of 4GB — tighter than the
25-30% rule of thumb above, but with the swapfile now automatic
(`ensure_swapfile`, see above) there's real insurance against burst load
rather than relying on manual setup. At the low end of the site range (2
instead of 4), headroom improves to roughly ~950MB (~23%). `wg-easy`,
Leaves roughly **~580MB headroom (~14%)** out of 4GB at 4 sites — tighter
than the shared-MariaDB design would have been (~700MB), the real cost of
per-site backup/restore isolation, and tighter than the 25-30% rule of
thumb above. With the swapfile now automatic (`ensure_swapfile`, see above)
there's still real insurance against burst load. At the low end of the site
range (2 instead of 4), it's ~3.12GB used, ~976MB headroom (~24%) — the gap
between shared and dedicated MariaDB narrows a lot at low site counts,
since the shared model's one fixed instance cost is amortized across fewer
sites. `wg-easy`,
`homebox`, and `audiobookshelf` from earlier in this doc aren't included in
this specific table — add them back in at ~25MB, ~125MB, and ~200MB
respectively if/when they're actually deployed alongside this baseline.