Installing CMS platforms from scratch has always been a bit of a chore. Take popular content management systems like WordPress and Drupal for example. Neither of them are able to work immediately after you download the code. You have to install and configure a webserver, a database, and the PHP interpreter with all of the correct plugins first.
Sometimes it feels like you have to duct tape a bunch of other programs together to create a unified system, and for years, that's just been the way of it.
Modern package managers in Linux don't make this problem any easier. You need to feed in the correct sequence of commands in just the right order to get the system you want, and even then, the programs you get at the end of that process aren't configured run a website out of the box, so you need to spend another hour or two writing or copying a working config from somewhere else.
I think that's why pre-built solutions like Docker, ddev, and Softaculous tend to be the sorts of tools system administrators and casual website builders reach for when faced with the prospect of setting up a server or development environment by hand. They work well enough and they're fast, and some may even come with your hosing plan!
However, no solution is perfect. Until recently, I haven't found a single tool that helps you develop a website locally, deploy it, and then manage it over time. You usually need different purpose-built tools for each step in the development lifecycle, and each has their own quirks which can make them difficult to use.
The Problem With (most) Pre-Built Solutions
Generally, Docker is the go-to solution for creating consistency in software environments. However, Docker containers are hard to inspect, and building and deploying a container by hand takes a special skill set. Plus, containers aren't truly reproduceable in the sense that you can spin up two of them at different times and expect the same result every time.
Most containers pull from something like Ubuntu latest to build the OS layer. Unfortunately, the "latest" release is a moving target that gets incremental updates from time to time. This means that two containers that are identical on paper may actually using slightly different software. In short, Docker has the ability to turn any problem into an orchestration problem.
On the other end of the development lifecycle, the traditional approach is to use tools like ddev and Lando to spin up development environments and let you start building something immediately, and they are excellent at doing this. However, when it comes time to deploy, you have to re-create a compatible environment on a real webserver. So, your dev-to-deploy pipeline will have to do all of that work for you, which brings us back to Docker and container orchestration problems.
Software installers like Softaculous are great for getting a website built on a live web server in just a few minutes. They solve the problem of bundling together the correct packages into a working system, but then the administration is up to you. You don't get any fancy tools that would come with with solutions based in gitops, devops, or containers. It's just a website on a server, which, depending on your use case, might be just fine. I used Softaculous to install this blog and, because my needs are minimal, I probably won't do anything else.
Of course, minimal solutions like this aren't well adapted to fast-paced software teams that need to make frequent updates to far-flung architecture throughout the day. Setting up integrations with other tools like Redis, Varnish, Solr, or other hosted services would be a pain.
How NixOS Solves These Problems
I won't get too deep into explaining what NixOS is or how it works, given that other people (and the project itself) have done such a good job of that. However, I will say that it is sort of what Docker would look like if it was an all-batteries-included general purpose operating system instead of just a container management software.
What Is Nix & NixOS (Briefly)?
The entire operating system is built using nix, which is both the name of a package manager and the programming language used to make the package manager do stuff. You can use nix (the language) to declare what programs you want to install and how you would like them configured. The syntax is pretty similar to JSON.
Once you've described how you want your system to look and behave, you just tell nix (the package manager) that you would like to build that system. That resulting build is what NixOS is-- an entire operating system built using a package manager. Because of the way nix computes and stores packages on your system, every time you rebuild your system, NixOS saves a snapshot of the build so you can roll back to the system state you were at before, without erasing user files. This means if you messed something up, you can just go back in time to an earlier time when your system worked.
Using NixOS to Create and Manage Manage Web Services
I've been using NixOS on and off over the course of about two or three years now. I've used it primarily on my personal computer. One of the things that has continually impressed me about it is the fact that you can treat practically any bundle of software as an installable package, including software that typically needs a lot of other services and infrastructure to run.
Building (In Abstract)
If you need a WordPress website you just plop this chunk of code into your configuration.nix
file, run sudo nixos-rebuild switch
and then you get a fresh WordPress instance waiting for you on localhost
.
services.wordpress.sites."localhost" = {};
NixOS will install and configure MySQL, a webserver, PHP, and WordPress in just a few minutes. Then you're free to start building the website however you want.
If you want to share this environment with someone else, you can just copy and paste your services.wordpress
config and share it with them. Granted, they will will need to use the same version of nixpkgs
to get the exact same software you're using, but that can easily be overcome using git to clone nixpkgs
and then git checkout
the same commit. Then, when you trigger a system rebuild, you just point to the version of nixpkgs you want to use.
nix-build /path/to/custom/nixpkgs -A config.system.build.toplevel -I nixos-config=path/to/configuration.nix
See this wiki page on the inner workings of the nixos-rebuild
command for more information.
Thankfully, it's fairly easy to spin up auxiliary services like Redis or Elasticsearch using nix. In the same text file you used to deploy your webserver, you can add something like this to run other kinds of servers.
services.elasticsearch = {
enable = true;
port = 9200;
...
};
services.redis = {
servers = {
"my-redis-server" = {
enable = true;
...
};
};
};
Deploying (In Abstract)
Deploying the website is fairly easy using this architecture. You need a pipeline that checks out your latest configuration.nix
and whatever WordPress code you created, triggers a sudo nixos-rebuild switch
on your webserver running NixOS, and you're done. The live website is now running the same exact software you used to build it on your personal machine.
On top of that, NixOS will create a nice little entry in grub
that allows you to roll back to earlier versions of your system software, as needed. You could easily use this feature to escape some of the pains of containerization such as creating and storing artifacts, waiting for your CD pipeline to download and configure a fresh operating system every time you push, taking the time to vet and configure a "blessed" version of that operating system for internal use, etc.
This solves a lot of system administration problems with a single tool, and it works pretty well.
Once I started getting deeper into NixOS for my personal computing needs, I began to wonder if I could use NixOS to develop and run Drupal.
But despite the fact that NixOS has over 120,000 packages in its official repository, it didn't have any packages for Drupal. So I started looking for other solutions.
Local Dev Environments Using Only Nix
One of the cool projects I found along the way was Drupal Flake. It was a simple flake-based configuration that could run a nix-powered dev environment for Drupal.
The creator of this project designed it around the fact that nix code was easy to share between large teams, and it yielded the exact same environment no matter what OS architecture you were using. In short, it solves the problem of everyone using different techniques for running a development environment on wildly different computers.
As long as you have the nix package manager, you can have the same development environment as your colleagues. This will run on MacOS, Windows, or Linux, or (probably) even the BSDs.
I learned about this tool this year at DrupalCon Atlanta and was impressed at how versatile it was. That said, it is only designed to create development environments, not for running Drupal on a live NixOS system. Which is why, I started looking into packaging Drupal using nix myself.
Running Drupal As A Service using NixOS
The end result of my work, is that I made a contribution nixpkgs(NixOS's default package repository) to make it so Drupal could could benefit from the power of nix on a live server.
The end results allow your to run and maintain a (simple) Drupal website as a service, like so
services.drupal = {
enable = true;
sites = {
"localhost" = {
enable = true;
};
};
};
This installs a local instance, but you could just as easily use a .com or a .local. The default package downloads an configures an instance of Nginx, which, if you're using a production website, will allow you to automatically configure SSL certificates directly in nix.
The Drupal implementation is fairly simplistic. I've not cracked the ability to bundle composer dependencies in the build package yet, but I have made it so running a fairly close-to-core Drupal website is at least possible using Nix, and I think that's a good start for now.
The actual implementation allows you to choose between two different webservers (Caddy and Nginx), and it comes with MYSQL and PHP-FM configured to work with Drupal out of the box. It would be fairly straight forward to write a cron job (also in nix) that creates backups your database and server code on a regular basis. It might even be possible to do this every time you trigger a rebuild on the NixOS host system, making rollbacks a lot easier.
A nice side effect of being run out of NixOS is that this approach makes your website's files (mostly) immutable. I've made sure to provide a way to access and modify some common Drupal directories so you can put custom code in there. However, for the most part, you can consider this a way of hardening your website against attackers out of the box.
In the future, I think it would be cool if you could use nix to manage everything about your Drupal install's infrastructure so you could just focus on building your website
I've written some simple (unofficial) documentation on how you might set up and manage your Drupal site using this new integration at this link. I hope it helps anyone who may want to try this on their own!
Comments