Local WordPress Development on NixOS with devenv
Two years ago I made my first attempt with NixOS and after a week of vacation spent spinning my wheels, I didn't have a fully working system so I went back to Fedora and what I knew while adding home manager to my workflow.
Still using home manager and VVV on Fedora brought issues with certificate signing every time the kernel updated. Just before Christmas I had another round of certificate issues and decided that it was time to learn devenv. It worked and now I built my new computer fully on NixOS because the local development environment was worked out.
What is devenv
The whole deal with NixOS is that it's declarative. A basic install has configuration.nix and hardware-configuration.nix. The hardware configuration is specific to your machine, but configuration.nix is agnostic. That means you can install NixOS on any machine, copy your configuration.nix file over, run nixos-rebuild switch and after a few minutes your entire machine will be configured just like your old machine.
devenv extends that to declarative development environments that don't pollute each other. I can have different versions of PHP or node or any other library that is isolated to only the folder for that project.
direnvdoes something similar without spinning up servers. Using shell.nix or flake.nix you can load your project dependencies and they'll be isolated to that project. For my Laravel projects I still usephp artisan servefor a server but define all the requirements for the project with direnv.
But just like Fedora brought its own challenges with certificate signing, devenv on Nix has read only stores which means my setup on Fedora didn't work out of the box.
Today we're going to take a look at my local WordPress development environment on NixOS. We'll get custom domains setup and trust the custom SSL certificate in Firefox so I don't get warnings for every site.
Prerequisites
To get started you're going to need NixOS installed and flake enabled. direnv and a project you want to spin up in devenv.
Setting Up devenv on NixOS
Installing devenv
To start you're going to need to add devenv to your .pkgs and rebuild your system with sudo nixos-rebuild switch.
Here is a full look at the devenv.nix file we'll be using to setup a local WordPress site.
{ pkgs, config, ... }:
{
packages = with pkgs;[
git
wp-cli
];
enterShell = ''
./scripts/setup-wp-stubs.sh
'';
languages.php.enable = true;
languages.php.package = pkgs.php82.buildEnv {
extensions = { all, enabled }: with all; enabled ++ [ redis pdo_mysql xdebug ];
extraConfig = ''
memory_limit = -1
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.idekey = vscode
xdebug.log_level = 0
max_execution_time = 0
'';
};
languages.php.fpm.pools.web = {
settings = {
"clear_env" = "no";
"pm" = "dynamic";
"pm.max_children" = 10;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 10;
};
};
# change the certificate to match your site
certificates = [
"wp.localhost"
];
services.redis.enable = true;
# Links to MariaDB internally
services.mysql = {
enable = true;
settings.mysqld = {
max_allowed_packet = "512M";
};
};
# change the database name and change ensurePermissions to match your site name
services.mysql.initialDatabases = [{name = "wp"; }];
services.mysql.ensureUsers = [
{
name = "admin";
password = "test";
ensurePermissions = { "wp.*" = "ALL PRIVILEGES"; };
}
];
services.caddy.enable = true;
# change wp.localhost to <yoursite>.localhost and the .pem file names should match that
services.caddy.virtualHosts."wp.localhost" = {
extraConfig = ''
tls ${config.env.DEVENV_STATE}/mkcert/wp.localhost.pem ${config.env.DEVENV_STATE}/mkcert/wp.localhost-key.pem
root * .
php_fastcgi unix/${config.languages.php.fpm.pools.web.socket}
file_server
'';
};
}
Trusted Users for Cachix
If you grabbed the file above and dropped it in your project right now you'd get a warning about not being a trusted user of the Nix store. The easiest way to fix this is to make yourself a trusted user of the Nix store. Add the following line to your configuration.nix file.
nix.settings.trusted-users = [ "root" "<youruser>" ];
Then run sudo nixos-rebuild switch again to grant your user privileged access.
Note: A common mistake is to put this in your home-manager files, but it's not a home-manager option and wouldn't be needed if you're using home-manager on other Linux systems.
Binding Caddy to Port 443 on NixOS
To get clean local URLs we need to allow caddy to bind to port 80/443 but Linux restricts that. For non-Nix environments we add this line above our redis declaration.
# This lets Caddy bind to 443
scripts.caddy-setcap.exec = ''
sudo setcap 'cap_net_bind_service=+ep' ${pkgs.caddy}/bin/caddy
'';
Then use devenv shell to drop into the local shell with your defined environment loaded. Then run sudo setcap 'cap_net_bind_service=+ep' $(which caddy).
But we're in Nix so that doesn't work because we can't write to our installed server files and caddy can't bind to ports under 1024 by default. The easiest way to accomplish this is to reset the limit on which ports we can bind to. Add this line to your Nix config.
# Allow unprivileged processes to bind to ports 80+ (needed for devenv Caddy)
boot.kernel.sysctl."net.ipv4.ip_unprivileged_port_start" = 80;
Now caddy can bind to port 80 and above.
If you go digging around the internet you'll find that ports were reserved but it doesn't really matter on your local machine so go for it. Even Kubernetes deploys may change this setting with no ill effects.
Trusting the Local SSL Certificate in Firefox
Next, if you check your site now you'll get a scary warning in your browser of choice about a non-trusted SSL. This happens because Firefox doesn't trust the root certificate we added with devenv. You have two choices here, just click the 'Accept Risk' button and move on or add the certificate to your root store.
I opt to add the certificate by going to about:preferences#privacy then scrolling down to Certificates. Click 'View Certificates' and go to the Authorities Tab then click Import.
You'll find the devenv root certificate at .devenv/state/mkcert/rootCA.pem. Since all devenv projects share the same root certificate this is a one time change.
You should be able to do this in other browsers as well in their settings.
Conclusion
Now you've got a development environment setup that doesn't leak different versions of your libraries into other projects. Yes it required a few system level tweaks, but if you're on NixOS you make those tweaks once and then you don't make them on your next system because they're already made.
Now I cd into my project directory and type devenv up and my development environment starts. Creating a new WordPress environment is as simple as copying the devenv.nix file into a new directory and changing a few values.
No more messing with certificates every time my kernel updates.
Let's build an isolated development environment that's easily portable between systems.