WordPress
This guide sets up a local WordPress development environment with:
- Caddy - Web server that handles HTTP requests and routes them to PHP
- PHP-FPM - FastCGI Process Manager that executes WordPress PHP code
- MariaDB - Database server for storing WordPress content, users, and settings
- wp-cli - Command-line interface for managing WordPress
Quick Start
Section titled “Quick Start”- Run
devenv up— services start, the database is seeded, WordPress is downloaded, andwp-config.phpis created automatically - Visit http://localhost:8000 to complete the WordPress installation
Configuration
Section titled “Configuration”{ pkgs, config, ... }:
{ # WordPress CLI for managing WordPress from the command line packages = [ pkgs.wp-cli ];
languages.php = { enable = true; version = "8.4";
# PHP extensions required by WordPress # Note: common extensions like xml, mbstring, curl are enabled by default extensions = [ "mysqli" # MySQL database connectivity "pdo_mysql" # PDO MySQL driver (used by some plugins) "gd" # Image manipulation (thumbnails, image editing) "zip" # Plugin/theme installation from zip files "intl" # Internationalization support "exif" # Image metadata reading ];
# PHP settings for WordPress ini = '' memory_limit = 256M upload_max_filesize = 64M post_max_size = 64M max_execution_time = 300 '';
# PHP-FPM pool configuration # FPM (FastCGI Process Manager) manages PHP worker processes fpm.pools.web = { settings = { "pm" = "dynamic"; # Dynamic process management "pm.max_children" = 10; # Maximum worker processes "pm.start_servers" = 2; # Workers to start initially "pm.min_spare_servers" = 1; # Minimum idle workers "pm.max_spare_servers" = 5; # Maximum idle workers }; }; };
# MariaDB database server services.mysql = { enable = true; package = pkgs.mariadb;
# Create the WordPress database on first run initialDatabases = [{ name = "wordpress"; }];
# Create database user with access to WordPress database ensureUsers = [{ name = "wordpress"; password = "wordpress"; ensurePermissions = { "wordpress.*" = "ALL PRIVILEGES"; }; }]; };
# Caddy web server services.caddy = { enable = true;
# Serve WordPress on http://localhost:8000 virtualHosts."http://localhost:8000" = { extraConfig = '' root * ${config.devenv.root}/wordpress
# Pass PHP requests to PHP-FPM. php_fastcgi unix/${config.languages.php.fpm.pools.web.socket}
# Serve static files directly file_server ''; }; };
# Download WordPress and write wp-config.php once MariaDB is ready and seeded. # Runs automatically as part of `devenv up` via the caddy process dependency. tasks."wordpress:setup" = { description = "Download WordPress and create wp-config.php"; after = [ "devenv:mysql:configure" ]; cwd = config.devenv.root; exec = '' set -e
mkdir -p wordpress cd wordpress
if [ ! -f wp-includes/version.php ]; then echo "Downloading WordPress..." wp core download else echo "WordPress already downloaded." fi
if [ ! -f wp-config.php ]; then echo "Creating wp-config.php..." wp config create \ --dbname=wordpress \ --dbuser=wordpress \ --dbpass=wordpress \ --dbhost=127.0.0.1 echo "" echo "WordPress configured! Visit http://localhost:8000 to complete installation." else echo "wp-config.php already exists." fi ''; };
# Hold caddy until WordPress is on disk so the first request isn't a 404. processes.caddy.after = [ "wordpress:setup" ];
# Show helpful instructions when entering the shell enterShell = '' echo "" echo "WordPress Development Environment" echo "==================================" echo "" echo "Run devenv up to start services and provision WordPress, then open:" echo " http://localhost:8000" echo "" echo "Database credentials (for wp-config.php):" echo " Host: 127.0.0.1" echo " Database: wordpress" echo " User: wordpress" echo " Password: wordpress" echo "" '';}How It Works
Section titled “How It Works”When you run devenv up, devenv runs the following graph in order:
devenv:mysql:configureruns as a oneshot task once MariaDB is ready, creating thewordpressdatabase, thewordpressuser, and granting privileges.wordpress:setupruns oncedevenv:mysql:configurehas succeeded, downloading WordPress core and writingwp-config.php.- Caddy starts only after
wordpress:setupcompletes, so the first HTTP request doesn’t hit an empty document root. - PHP-FPM exposes a Unix socket that Caddy proxies
.phprequests to. Thephp_fastcgidirective handles WordPress pretty permalinks automatically — non-existent paths fall through toindex.php.
Troubleshooting
Section titled “Troubleshooting”Database connection errors
Section titled “Database connection errors”devenv up seeds the database automatically. If you see “Error establishing database connection”, verify the database and user exist:
mysql -u wordpress -pwordpress -h 127.0.0.1 -e "SHOW DATABASES;"If the wordpress user is missing, the devenv:mysql:configure task didn’t run — check the logs with devenv tasks list and devenv tasks run devenv:mysql:configure. A stale .devenv/state/mysql from an older setup can also cause this; remove it and re-run devenv up.
Port 8000 already in use
Section titled “Port 8000 already in use”If another service is using port 8000, change the port in the Caddy configuration:
services.caddy.virtualHosts."http://localhost:8080" = { ... };PHP extension errors
Section titled “PHP extension errors”If WordPress reports missing extensions, add them to the extensions list:
languages.php.extensions = [ "mysqli" "imagick" # Add additional extensions as needed];Advanced Configuration
Section titled “Advanced Configuration”Adding Redis for caching
Section titled “Adding Redis for caching”Redis improves WordPress performance by caching database queries:
services.redis.enable = true;
languages.php.extensions = [ # ... other extensions ... "redis"];Install a Redis object cache plugin (like “Redis Object Cache”) in WordPress.
Adding Xdebug for debugging
Section titled “Adding Xdebug for debugging”Enable step-through debugging in your IDE:
languages.php.extensions = [ # ... other extensions ... "xdebug"];
languages.php.ini = '' memory_limit = 256M xdebug.mode = debug xdebug.start_with_request = yes xdebug.client_port = 9003'';HTTPS with local certificates
Section titled “HTTPS with local certificates”For plugins that require HTTPS, use local certificates:
certificates = [ "localhost" ];
services.caddy.virtualHosts."https://localhost" = { extraConfig = '' tls ${config.env.DEVENV_STATE}/mkcert/localhost.pem ${config.env.DEVENV_STATE}/mkcert/localhost-key.pem root * ${config.devenv.root}/wordpress php_fastcgi unix/${config.languages.php.fpm.pools.web.socket} file_server '';};Note: HTTPS on port 443 requires elevated privileges. Use a high port like 8443 or configure your system to allow binding to privileged ports.

