Skip to content
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-content is the content volume, <app name>-content on 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.

Mail

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 dockerfile line and deploy.
  • The site address is wrong after moving domains – WordPress stores it in the database. Change it in Settings → General, or with wp-cli from the shell.
  • WordPress cannot reach the database – check the MySQL accessory is running under Environment → Accessories, and that WORDPRESS_DB_HOST still matches its container name.