Deploying / WordPress
WordPress
Installing the WordPress template -- the site builder and blog, on MySQL -- and running its installer after the first deploy.
WordPress is the open-source site builder and blog. The
template runs the official image against a MySQL accessory, with
wp-content – plugins, themes and uploads – in its own volume.
| Image | wordpress:7.1.2-php8.4-apache |
| Port | 80 inside the container, behind kamal-proxy |
| Health check | /wp-includes/images/blank.gif |
| Volumes | content |
| Accessory | mysql:8.4.11 |
| Singleton | no |
The health check is a static image rather than a page, because every PHP page redirects to the
installer until WordPress has been set up. A check on / would fail the first deploy.
Before you start
- A server that is registered, and a ready runner
- A hostname, if you have one. WordPress stores its site address in the database the moment the installer runs, so it is much less work to point the domain at the server first than to change the address afterwards
- Somewhere off the machine for backups. There are two things to keep here –
the database and
wp-content– and they are backed up separately
Installing
Templates → WordPress → Install. Give it a name, a runner, an environment, the server and the domain. WordPress asks for nothing else.
Install creates the app and does not deploy it. Point an A record for the domain at the server,
then press Deploy.
What the install sets
| Variable | Value | |
|---|---|---|
WORDPRESS_DB_HOST |
<app name>-mysql |
the accessory’s container on the kamal network |
WORDPRESS_DB_USER |
root |
plain |
WORDPRESS_DB_NAME |
app |
the database the accessory creates |
WORDPRESS_CONFIG_EXTRA |
define('WP_AUTO_UPDATE_CORE', false); |
turns off WordPress’s own core updater |
WORDPRESS_DB_PASSWORD |
the MySQL accessory’s MYSQL_ROOT_PASSWORD |
aliased, not a second copy |
It also generates eight 64-character secrets – WORDPRESS_AUTH_KEY,
WORDPRESS_SECURE_AUTH_KEY, WORDPRESS_LOGGED_IN_KEY, WORDPRESS_NONCE_KEY and the four
matching _SALT values. Without them the image invents new ones on every boot, and every deploy
would sign everybody out.
The database password is an alias, not a copy: KamalCloud writes it into the secrets file as
WORDPRESS_DB_PASSWORD:MYSQL_ROOT_PASSWORD, so the accessory and the app always agree, and
there is one value to keep rather than two to keep matching.
Install also adds a PHP ini to the build:
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
PHP’s 2 MB default refuses most theme and plugin zips.
Running the installer
Open the site’s domain after the first deploy. WordPress asks for the site title and the first admin account. Nothing is set up before that – the deploy succeeding only means the container is answering.
Upgrading WordPress
Change the image tag on the app and deploy. Core comes out of the pinned image on every boot, so the tag is the version that runs.
Do not upgrade from the WordPress dashboard. wp-config.php is written into the container at
each boot and core is replaced from the image, so an update made in the dashboard is undone by
the next deploy. WP_AUTO_UPDATE_CORE is set to false for the same reason.
Plugins and themes are different. They live in the content volume and update from the
dashboard as usual, because the volume survives a redeploy.
Data and backups
Two things hold state, and each is backed up on its own:
- The database is the MySQL accessory. Its dumps are taken like any accessory backup, and it is never published outside the machine – WordPress reaches it by container name on the kamal network
wp-contentis thecontentvolume,<app name>-contenton the host: plugins, themes and every upload
Set a schedule and a bucket on both. A backup of one without the other restores to a site that is missing either its content or its media.
The image sends through PHP’s mail(), which is not configured in the container, so WordPress
sends nothing out of the box. Use an SMTP plugin and a relay or your mail provider’s submission
port. Sending straight out on port 25 is blocked on new Hetzner, DigitalOcean and AWS accounts.
HTTPS
TLS is kamal-proxy’s. The image already trusts its X-Forwarded-Proto, so WordPress builds
https links behind it without a plugin or a wp-config change.
Troubleshooting
- The first deploy’s health check fails – confirm the check is still
/wp-includes/images/blank.gif. A check on/cannot pass before the installer has run. - Everyone is signed out after a deploy – one of the eight key or salt secrets is missing. Check them under Environment → Variables.
- A plugin or theme zip is refused as too large – the PHP ini above is missing from the
build. It is added at install; if the app was made before that, add the
dockerfileline and deploy. - The site address is wrong after moving domains – WordPress stores it in the database.
Change it in Settings → General, or with
wp-clifrom the shell. - WordPress cannot reach the database – check the MySQL accessory is running under
Environment → Accessories, and that
WORDPRESS_DB_HOSTstill matches its container name.