Previous slide Next slide Toggle fullscreen Open presenter view
This is a Nix talk with some PHP, not a PHP talk with some Nix
About Me
HTML, CSS, PHP since 2007
Drupal since 2008
JavaScript, TypeScript, Go
Full-time Linux since ~2015
Nix/NixOS since 2022
Homelabber since 2024
Linux? macOS? Windows/WSL?
Who has PHP installed on their computer?
Who has multiple versions of PHP installed?
Installing PHP
WAMP (Windows), MAMP (macOS), XAMPP
Homebrew (brew install php)
apt, dnf, yum, pacman yay
Laravel Herd/Sail
Symfony CLI?
asdf
Containers (Docker, Podman, LXC, etc)
Virtual machines (Vagrant, VMWare, Virtualbox, etc)
What is Nix?
Package manager with more than 120,000 packages (nixpkgs)
Operating system with more than 20,000 packages (NixOS)
Tool for declaratively building software reproducibly
A functional domain-specific configuration language
Different implementations - Lix, Tvix
Home Manager, nix-darwin, devenv
Started as a research project by Eelco Dolstra around 2003
What is Nix?
Package manager
Build tool
Operating system
Language
Installing Nix
https://nixos.org/download
$ sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) \
--daemon
Different instructions for Linux, macOS and Windows (WSL2).
Different instructions for NixOS.
$ nix --version
nix (Nix) 2.28.4
This installation tool will set up your computer with the Nix package
manager. This will happen in a few stages:
1. Make sure your computer doesn't already have Nix. If it does, I
will show you instructions on how to clean up your old install.
2. Show you what I am going to install and where. Then I will ask
if you are ready to continue.
3. Create the system users (uids [30001..30032]) and groups (gid 30000)
that the Nix daemon uses to run builds. To create system users
in a different range, exit and run this tool again with
NIX_FIRST_BUILD_UID set.
4. Perform the basic installation of the Nix files daemon.
5. Configure your shell to import special Nix Profile files, so you
can use Nix.
6. Start the Nix daemon.
Installing PHP with Ubuntu
$ apt update -y
$ apt-get install php
$ which php
/usr/bin/php
$ php -v
PHP 8.3.6
Running PHP with Nix
$ nix run nixpkgs#php -- -v
PHP 8.4.11
$ nix shell nixpkgs#php
$ which php
/nix/store/s4kv9bzqawhqcyjg9xm2hji3jap6d6kd-php-with-extensions-8.4.11/bin/php
$ php -v
PHP 8.4.11
Running PHP with Nix
$ nix run nixpkgs#php83 -- -v
PHP 8.3.25
$ nix run nixpkgs#php82 -- -v
PHP 8.2.29
Using a shell.nix file
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [
php
phpPackages.composer
phpactor
];
}
$ nix-shell shell.nix
[nix-shell:~/my-project]$ composer -V
Composer version 2.8.5 2025-01-21 15:23:40
Using a flake.nix file
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable" ;
outputs = inputs:
let
system = "x86_64-linux" ;
pkgs = import inputs.nixpkgs { inherit system; };
in {
devShells.${system}.default = (import ./shell.nix {
inherit pkgs;
});
};
}
Using a flake.nix file
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable" ;
outputs = inputs:
let
system = "x86_64-linux" ;
pkgs = import inputs.nixpkgs { inherit system; };
in {
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [
php
phpPackages.composer
phpactor
];
};
};
}
Structure of a Flake-based project
.
├── composer.json
├── composer.lock
├── flake.lock
├── flake.nix
├── output_dev
├── source
└── vendor
As well as flake.nix , we also get flake.lock .
Pinning packages
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable" ;
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-24.11" ;
nixpkgs-pinned.url = "github:nixos/nixpkgs/45570c299dc2" ;
}
{
let
pkgs-stable = import inputs.nixpkgs-stable { inherit system; };
in {
packages = [ pkgs-stable.php ];
};
}
Managing services
With NixOS:
services.mysql.enable = true ;
services.mysql.initialDatabases = [
{ name = "my_project" ; }
{ name = "another_project" ; }
];
Without NixOS:
https://github.com/juspay/services-flake
Managing services
{
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts" ;
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable" ;
process-compose.url = "github:Platonic-Systems/process-compose-flake" ;
services.url = "github:juspay/services-flake" ;
};
Managing services
imports = [
inputs.process-compose.flakeModule
];
perSystem = { config, lib, pkgs, ... }: {
process-compose."default" = {
imports = [
inputs.services.processComposeModules.default
];
services = {
settings.processes = {
}
Managing services
services = {
mysql."mysql1" = {
enable = true ;
initialDatabases = [
{ name = "drupal_nix_flake_example" ; }
];
};
};
Managing services
settings.processes = {
php = {
command = pkgs.writeShellApplication {
name = "php-local-server" ;
text = "${getExe php} -S 127.0.0.1:${toString webPort} -t web" ;
};
depends_on."mysql1" .condition = "process_healthy" ;
};
};
Managing services
devShells.default = pkgs.mkShell {
inputsFrom = [
config.process-compose."default" .services.outputs.devShell
];
nativeBuildInputs = with pkgs; [
php
phpPackages.composer
];
};
Run nix run . and go to http://localhost:8000 .
Multiple PHPs
Laravel Herd/Sail
Containers
phpenv, brew-php-switcher, phpbrew
asdf (not just for PHP)
Vagrant, VMWare, VirtualBox
Different physical machines?
Nix is like nvm for everything
My ideal environment
$ php -v
The program 'php' is currently not installed.
$ cd ~/my-project
$ php -v
PHP 8.4.10
$ cd ~/another-project
$ php -v
PHP 8.3.24
Using direnv
https://direnv.net
It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.
In NixOS:
{
programs.direnv.enable = true ;
programs.direnv.nix-direnv.enable = true ;
}
The .envrc file
If you have a flake.nix:
use flake
Using a remote Flake:
use flake "git+https://git.oliverdavies.uk/opdavies/dev-shells#php84"
use flake "github:opdavies/dev-shells#php84"
Packaging for Nix
stdenv.mkDerivation (finalAttrs: {
pname = "hello" ;
version = "2.12.2" ;
src = fetchurl {
url = "mirror://gnu/hello/hello-${finalAttrs.version} .tar.gz" ;
hash = "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm0x6nc4IGKs=" ;
};
env = lib.optionalAttrs stdenv.hostPlatform.isDarwin {
NIX_LDFLAGS = "-liconv" ;
};
doCheck = true ;
doInstallCheck = true ;
Packaging for Nix
pkgs.writeShellApplication {
name = "preview" ;
runtimeInputs = with pkgs.nodePackages; [
browser-sync
];
text = ''
browser-sync start
--ignore '**/.*' \
--no-notify \
--no-ui \
-sw
'' ;
}
Packaging PHP for Nix
php.buildComposerProject2 (finalAttrs: {
pname = "phpactor" ;
version = "2025.07.25.0" ;
src = fetchFromGitHub {
owner = "phpactor" ;
repo = "phpactor" ;
tag = finalAttrs.version;
hash = "sha256-9XWlWwq+xvqPgKIc7IGoMVTxajjYsrPo/ra/0JIE168=" ;
};
vendorHash = "sha256-3xkt0QjytW4BOCgZdevat7zkSuZTPPvwz3yptiq5zoo=" ;
Packaging PHP for Nix
php.buildComposerProject2 (finalAttrs: {
pname = "pest" ;
version = "3.7.4" ;
src = fetchFromGitHub {
owner = "pestphp" ;
repo = "pest" ;
tag = "v${finalAttrs.version} " ;
hash = "sha256-ddsdVx/Vsg7GG11fGASouBU3HAJLSjs1AQGHx52TWzA=" ;
};
composerLock = ./composer.lock ;
vendorHash = "sha256-rOJ6PFp4Xfe89usoH455EAT30d2Tu3zd3+C/6K/kGBw=" ;
Packaging npm
pkgs.buildNpmPackage rec {
pname = "openapi-generate-html" ;
version = "0.5.3" ;
src = pkgs.fetchFromGitHub {
owner = "qazsato" ;
repo = pname;
rev = "v${version} " ;
hash = "sha256-+RmwoRhvfkaj/d3EwID7E6noVV+M3h6pe7IEVYyuUwk=" ;
};
dontNpmBuild = true ;
npmDepsHash = "sha256-7yYM43fAR2HLACOQNw7N/t8Lk+17qNfeDKzfb1wx/0U=" ;
}
Packaging Go
pkgs.buildGoModule rec {
pname = "openapi-mock" ;
version = "0.3.9" ;
src = pkgs.fetchFromGitHub {
owner = "muonsoft" ;
repo = pname;
rev = "v${version} " ;
hash = "sha256-7u//uwcVV1/EI6Rr3ju7KOwMYt/dXivyvBWIpTaoWZk=" ;
};
vendorHash = "sha256-KPCRunuCIbBX+YpHgshixmrxM3Ey0LIdEC0Z4CtpQoI=" ;
}
NixOS in production?
services.phpfpm.enable = true ;
services.mysql.enable = true ;
services.nginx.enable = true ;
NixOS in my homelab
services.jellyfin.enable = true ;
services.immich.enable = true ;
services.paperless.enable = true ;
Not sure?
Want to try it, but you're not sure?
$ docker run --rm -it nixos/nix
$ podman run --rm -it nixos/nix
PHP examples, but apply to other languages.
Replace things with your language/CMS/framework of choice
This is a Nix talk with some PHP, not a PHP talk with some Nix.
PHP and Drupal Development. Linux-based infrastructure and systems administration. Automation.
Vagrant, Puppet, Ansible.
Who has different projects that need different versions of PHP or needs to support multiple versions of PHP?
There are a lot of different ways to install PHP.
Different package managers for different Linux distributions.
Any other ways?
Nix !== NixOS
Similar to Homebrew, apt, etc.
Similar to Ansible.
Ecosystem of tools. Home Manager for user home configuration, nix-darwin for managing macOS.
Can select from a stable release or unstable.
Unstable is more recently updated.
Two major releases a year in May and November (e.g. 24.11, 25.05).
Number of total packages vs fresh packages.
One version of PHP installed globally.
Install vs run.
Applications are placed in the Nix store and added to $PATH.
Arbitrary commands and ephemeral shells.
Great for one-off tools, but what if you need more than one thing
A more recent approach.
Can import shell.nix.
You can have multiple versions of Nix and applications installed at once.
Pin to a stable version of nixpkgs or a specific commit.
Could even use "master", which is newer than "unstable".
What if you need long-running services, like a database
Software that allows you to have multiple versions of PHP installed and the ability to switch between them.
Any other ways?
If there are multiple versions in nixpkgs, like PHP, you can pick the version you need.
If needed you can pin nixpkgs to a specific version for that project.
- Environment variable manager based on directories.
- If no flake, use `use nix`.
Explicit and bundled dependencies. "Batteries included". More shareable and no need to remember what's installed elsewhere.
Existing builders for PHP, node, Go, etc, and functions to download source code from GitHub, GitLab or any remote URL.
Learn once, use everywhere.