Static’d? Static-ified? Enstaticed?
Context
Previously, I “un-multisite’d” a few WordPress blogs I had running on Strider’s Virtual Multiblog (VMB). I used a plugin called Simply Static which worked well. However, there were a couple of things I realised I needed to do to the site, but since it was now static, I couldn’t update the few things using WordPress.
Updates
wanted to do a couple of things but the most important thing was to put a notice on each page noting that the site was archived, read-only and wouldn’t be receicing udpates in the form of new posts or comments (being a good netizen). Removing the comment forms too would make that much more obvious.
Off the top of my head, I figured I had a couple of options:
- update the static files using some kind of mass-edit tool (sed, emacs, vi etc)
- host a temporary development instance of the WordPress site, then re-export it with Simply Static again
I figured the latter would be moderately easier as Simply Static worked well, plus spinning up a quick wordpress dev instance isn’t too hard. Looking back, I should have gone with the first option. I could have probably done the job two or three times over, rather than it taking most of the day to do (along with searching for car insurance, which is a very separate topic but equally if not more perplexing).
For anyone else wanting to hide comment forms, you can use the following custom CSS:
/* override.css */ div#respond { display: none; }
Prerequisites
I had everything I needed already:
- a backup of the site files (you have backups, right?)
- a backup of the WordPress database (youdohave backups, right?)
- somewhere to host the site – I have a Docker host which I use to self-host some services
As my first aside, I realised I wanted to restore part of a borg archive but couldn’t quite remember how to do it.--pattern +/- <pattern>
is the tl;dr for including and excluding. Everything is included by default.
In my case I used:borg extract /path/to/repo::archive --pattern '+ var/www/wordpress' --pattern '- *'
which included the wordpress path and excluded everything else, respectively.
See borg list
(look under ‘common options’ and examples) for more info.
Setting Up The Test Site
Docker Setup
I figured I could use the WordPress Docker image to host the site temporarily. I was able to get it up and running with a minor modification to the docker-compose.yml
file to use traefik to give me a nice URL which was not needed, but I thought I would refresh my memory of it.
I’m glad I did, as I ran into a minor issue causing the site to not be resolvable through traefik (see ‘Issues’ section) which needed a tweak to fix.
Importing WordPress Database
It’s pretty straightforward. My SQL dumps include all tables, but all I needed was the particular one:
$ grep -A 999999999999999 "Current Database: \`wordpress\`" all-dbs.sql > wp.sql $ docker exec -i wpdb mysql -u root -p [password] < wp.sql
Thanks to this ServerFault answer for the tip.
Using PHP & Nginx
Unfortunately, while the WordPress image gave me the setup wizard (as expected), once I brought in the database and files I got the dreaded “white screen of death (WSOD)” and couldn’t get any useful information as to why, so I switched over to a php + nginx combo.
The guide at JonsDocs on nginx and php was very helpful. I ended up with a slightly different php-dockerfile
(and changed context to a subdirectory) and dropped things that I didn’t need (php logging, composer, fancy URL schemes). Note that I used the php:7.4-fpm image, not the 8 branch which was current at time of writing as that was causing complaints like:
Fatal error: Uncaught ArgumentCountError: Too few arguments to function WP_Widget::__construct(),
When I used the standard off-the-shelf php image, WordPress complained that it didn’t have the mysql
extension (there is a guide to what sort of environment WP needs, including PHP extensions), so I had to build one. I used php7.4, and borrowed from the JonsDocs guide for the dockerfile:
# php-dockerfile FROM php:7.4-fpm RUN apt-get update && \ apt-get install -y zip curl libcurl3-dev libzip-dev libpng-dev libonig-dev libxml2-dev RUN docker-php-ext-install curl gd mbstring mysqli xml dom zip
Note: Make sure you don’t try to build an already-included module (like json), or you’ll run into an error there!
eg:
Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20200930/ cp: cannot stat 'modules/*': No such file or directory make: *** [Makefile:87: install-modules] Error 1
As another aside, I ran into space issues while building the php image, and had to resize the docker zvol. Realising I wouldn’t have enough space to hose the few gigabytes of site files (it had lots of photos!), I realised I had to enlarge the filesystem of the container itself, and though that was a regular dataset it had no quota, meaning that proxmox was limiting the space available.
To grow the docker zvol, which was formatted xfs because of reasons of hosting in an unprivileged container (if I remember correctly), and the rootfs of the container (which didn’t have a quota!):## to grow zvol
## replace /dev/zd0 with the device corresponding to your zvol
$ mkdir /tmp/resize/; mount /dev/zd0 /tmp/resize; xfs_growfs /tmp/resize; umount /tmp/resize
## to grow proxmox container rootfs
## replace ctid with your container id
## eg pct resize 101 rootfs 10G
$ pct resize <ctid> rootfs <size>
Site Access
Obviously all the references in the site’s database were to the actual domain. To get around that and keep the site live, I used the time-honoured trick of pointing to the development site in my /etc/hosts
, and adding a corresponding traefik label for the site name. Make sure you have the site subdomain if applicable- I had to switch from bare domain to www.
This was enough to get the site working! I was able to install the Simply Static plugin, and it didn’t work. Didn’t do anything. At first I thought it was a permission issue as the diagnostics page complained it didn’t have read or write accesss to the output directory, which is a fair enough complaint. However, even after fixing that it still didn’t do anything.
Nothing in the activity log.
Nothing in the output directory.
Nothing in nginx/php logs.
Nothing.
I tried a bunch of things- reinstalling the plugin, installing the plugin from the VPS itself, installing the plugin via git clone
and rolling back to an earlier version. Other wild things that I would never do live, like giving global recursive read/write access to the entire wordpress directory in case there was another odd permission / ownership issue (note: please, for the love of Yggdrasil, don’t randomly futz with overly-permissive permissions on live sites). I updated other plugins, I updated WordPress. I wasn’t far from losing my sanity when I had a realisation.
On the diagnostics page, one of the checks is that WordPress can talk to itself, and indeed my site reported it could talk to itself on its internal docker IP quite happily. But the gears in my head finally turned and it clicked- I was seeing nothing in the access logs because the dev site was (probably) trying to make requests to the live site. I had bypassed DNS by adding an entry in /etc/hosts
, but that was local to me.
The solution was to add the mapping of IP-sitename (of the nginx container service and ‘real’ domain respectively) of the development site to the containers’ /etc/hosts
.
Once I did that I saw actual activity in Simply Static’s activity log. I regained some semblance of sanity, though it wasn’t to last car insurance.
After the export was concluded I was able to rsync the files to the VPS hosting the live version of the site, change the root
directive in the nginx config file, and the updated version is live!
Issues
Minor Issue 1: Traefik Complained `No such service: wordpress@docker’
tl;dr: docker-compose services are not the same as traefik services
This one, like many minor issues, are on me for not fully remembering how traefik labels work. the docker-compose.yml
file specifies services, made of images which have volumes, environment values, network definitions, whatever you have in your docker-compose ‘unit’. Those services are not the same as traefik services!
I had to add an extra label to create a traefik service (loadbalancer) to stop it complaining and link up to the service:
labels: - "traefik.enable=true" - "traefik.docker.network=traefik_default" - "traefik.http.routers.wpdev.rule=Host(`www.sitename.tld`)" - "traefik.http.routers.wpdev.service=wordpressdev" - "traefik.http.services.wordpressdev.loadbalancer.server.port=80" - "traefik.http.routers.wpdev.tls=false"
Minor Issue 2: WordPress complains ‘Error establishing a database connection’
This cropped up a a couple of times. Things to check:
- you have containers on the same network- I added a
wpdevnet
network as by adding the frontend to the traefik network, I had inadvertently split it from the database - check credentials match
- check wp-config is up-to-date
- make sure your database user has access to the wordpress database (see eg SHOW GRANTS)
Minor Issue 3: MariaDB / MySQL complains about ‘Incorrect definition of table mysql.column_stats: expected column’
I was getting repeated errors in the console from the mariadb container:
wpdb | 2023-09-04 14:33:49 1911 [ERROR] Incorrect definition of table mysql.column_stats: expected column 'histogram' at position 10 to have type longblob, found type varbinary(255). wpdb | 2023-09-04 14:33:49 1912 [ERROR] Incorrect definition of table mysql.column_stats: expected column 'hist_type' at position 9 to have type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB'), found type enum('SINGL E_PREC_HB','DOUBLE_PREC_HB').
Turns out this is an upgrade issue. You can update it by using the mysql_update
tool (works for mariadb too):
docker exec -i wpdb mysql_upgrade -u root -p
Changing wpdb
for your database container name.
Pingback: Fixing Site Issues (Multiblog, Default Site, & Speed) – Rob's Blog