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 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.
The operator needs to give me a backup of the live WordPress site. The cleanest way is the All-in-One WP Migration plugin:
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.
# 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
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.
This gives us several long-term benefits:
The FvRE can scan the local site at http://127.0.0.1:8080 to get fresh regions/containers/widgets — no rate limit.
The FES can render the local site to a static dist that we deploy to oscar-web.freshvibeapps.com.
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.
No more "robots won't block us" — we never talk to the live site again. The VPS only talks to its own local WordPress.
Pixel-perfect static HTML: Playwright can capture the full rendered output, including the Elementor JS transformations, exactly as the live site produces it.
All-in-One plugin version mismatch: the plugin version on the live site must support importing backups from older plugin versions. The plugin has a paid extension for multi-site imports >512MB. For 50-200MB sites, free works.
Media library size: if the site has many large images, the .wpress might exceed upload limits in the local WP. Workaround: untar manually + import DB.
Plugin/theme compatibility: the local WordPress needs
the same PHP version + extensions as the live site. If
the live site uses PHP 8.x with specific extensions,
our local WordPress must match. The wordpress:latest
image ships with PHP 8.x and most extensions.
Elementor license: if the live site uses Elementor Pro, the local copy might show "unlicensed" notices. Not a blocker for snapshotting, but visible in screenshots.
Verify the WordPress container works — already done (pulled images, ran hello-world, no errors)
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).
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.
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.
Total time: 30-60 min once I have the .wpress. Operator's time: 5 min to install plugin + export.