WordPress sandbox on the VPS — full plan (2026-07-14)

The problem (recap)

The dist (27MB, 30 HTML pages, 0 images) is a snapshot of oscarstreeacademy.org.uk from July 11. The HTML is fully rendered (WordPress + Elementor output). But all images/CSS/JS reference https://oscarstreeacademy.org.uk/wp-content/... which is now rate-limited from the VPS IP.

We need a complete, locally-served copy of the WordPress site that we can: - Render with a headless browser (Playwright) - Save as static HTML + assets - Reproduce forever without depending on the live Brixly site

The solution: WordPress in Docker on the VPS

The VPS now has: - Docker (just installed, working — pulled wordpress + mariadb) - 68GB free disk - 1.8GB RAM - 1.3GB available

Plan: spin up WordPress in Docker, load the live site's data, render with Playwright, save as static dist.

What's needed from the operator (one-time)

The operator needs to give me a backup of the live WordPress site. The cleanest way is the All-in-One WP Migration plugin:

  1. Install All-in-One WP Migration on the live WP install
  2. Click Export → "File" → produces a single .wpress file (typically 50-200MB, includes DB + uploads + themes + plugins)
  3. Upload it to me (anywhere: email attachment, Google Drive, drop in a chat, etc.)
  4. I import it into a local WordPress on the VPS
  5. Done. Local copy is now permanent and self-contained.

Alternative (more work for the operator): 1. SSH into the Brixly hosting 2. Run wp db export → SQL file 3. Tar the entire wp-content/uploads/ directory 4. Send me both files 5. I import SQL into MariaDB, untar uploads into WordPress dir

The All-in-One plugin is much simpler. One click, one file.

What I do once I have the .wpress

# 1. Set up WordPress + MariaDB on VPS (already done)
docker run -d --name oscar-mariadb -e MYSQL_ROOT_PASSWORD=oscar-root-pw \
  -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress \
  -e MYSQL_PASSWORD=wordpress-pw -v oscar-mariadb-data:/var/lib/mysql \
  mariadb:10.6
docker run -d --name oscar-wordpress --link oscar-mariadb:mysql \
  -e WORDPRESS_DB_HOST=mysql -e WORDPRESS_DB_USER=wordpress \
  -e WORDPRESS_DB_PASSWORD=wordpress-pw -e WORDPRESS_DB_NAME=wordpress \
  -p 127.0.0.1:8080:80 -v oscar-wordpress-data:/var/www/html \
  wordpress:latest

# 2. Install All-in-One WP Migration plugin
docker exec oscar-wordpress wp plugin install all-in-one-wp-migration --activate

# 3. Import the operator's .wpress
docker cp operator-backup.wpress oscar-wordpress:/tmp/
docker exec oscar-wordpress wp ai1wm restore /tmp/operator-backup.wpress

# 4. Run the verification step (operator-requested)
bash /workspace/verify-local-wp.sh
# Exits non-zero if any critical check fails — prevents
# snapshotting a half-broken local WP

# 5. Set pretty permalinks if not already
docker exec oscar-wordpress wp rewrite structure '/%postname%/' --hard
docker exec oscar-wordpress wp rewrite flush --hard

# 6. Use Playwright to render and snapshot
# 7. Save the static dist to /workspace/projects/oscar-web/dist/
#    and deploy to oscar-web.freshvibeapps.com

Verification step (operator-requested refinement)

Added 2026-07-14. A .wpress can import and look fine but have Elementor/EAEL silently deactivated, permalinks broken, or uploads empty. Snapshotting that would give us a static dist that's visually wrong.

Script: /workspace/verify-local-wp.sh (12.4KB, ~280 lines)

What it checks (each PASS/FAIL/WARN): 1. Container health (Docker running) 2. wp-cli available (installs if missing) 3. WordPress core version 4. siteurl option set 5. Active plugins count (>= 5) 6. Specific expected plugins active: Elementor, Elementor Pro, Essential Addons for Elementor Lite (EAEL), FluentForm, All-in-One 7. Active theme set 8. Permalinks: warns if not pretty permalinks (live site uses /%postname%/) 9. Uploads directory: at least 10 attachments in the library 10. Pages: at least 10 published pages 11. Elementor data: at least 1 page has _elementor_data meta 12. No fatal errors in debug.log after hitting /wp-admin/ and / 13. No PHP warnings 14. Front-end smoke test: home page renders, contains elementor-widget class 15. CSS / JS files loaded by the home page 16. Image sample check: 5 random image attachments have _wp_attached_file meta

Exit codes: - 0 = all PASS or only warnings (safe to snapshot) - 1 = at least one FAIL (do not snapshot, fix first)

Common fix suggestions are printed at the end if any check fails.

After the local WordPress is up

This gives us several long-term benefits:

  1. The FvRE can scan the local site at http://127.0.0.1:8080 to get fresh regions/containers/widgets — no rate limit.

  2. The FES can render the local site to a static dist that we deploy to oscar-web.freshvibeapps.com.

  3. Future WP updates are local-first: when the operator updates a post on their live site, they re-export the .wpress, I re-import, FvRE re-scans, FES re-renders.

  4. No more "robots won't block us" — we never talk to the live site again. The VPS only talks to its own local WordPress.

  5. Pixel-perfect static HTML: Playwright can capture the full rendered output, including the Elementor JS transformations, exactly as the live site produces it.

Risks

What I can do RIGHT NOW (without the .wpress)

  1. Verify the WordPress container works — already done (pulled images, ran hello-world, no errors)

  2. Pre-build the snapshot tooling — write the Playwright script that visits a WordPress site and produces a static dist, before I have the actual .wpress. Test it against any public WordPress site (e.g. wordpress.org/news).

  3. Pre-build the URL rewriter — the existing dist has 89 absolute URLs to the dead domain. I can write the sed/JS script that converts them to relative, ready to run when the .wpress arrives.

  4. Pre-build the Wayback image patcher — for the 13 images that ARE in Wayback, I can write a script to download them and put them at /wp-content/uploads/... in the existing dist. This gives us partial coverage before the .wpress arrives.

  1. Operator installs All-in-One WP Migration on live site
  2. Operator exports → .wpress file
  3. Operator sends me the .wpress
  4. I import into local WordPress on VPS
  5. I use Playwright to render and snapshot
  6. I deploy the new dist to oscar-web.freshvibeapps.com
  7. FvRE scans the local WordPress for regions/containers
  8. FES picks up the new data and the operator can edit via the live site

Total time: 30-60 min once I have the .wpress. Operator's time: 5 min to install plugin + export.