Nix flake with Node and PHP

Brief note on a nix flake that supports Node 20 and PHP 7.4. My use case is deploying an AWS CDK project that supports a Moodle 3.11 environment. I’m not version-limited on the Node version for CDK, but I need to start with PHP 7.4 to support the Moodle package code, though I’ll be moving to PHP 8.1 soon.

Here’s the flake.nix in its entirety:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
description = "Example JavaScript development environment for Zero to Nix";

# Flake inputs
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
phps.url = "github:fossar/nix-phps";
};

# Flake outputs
outputs = { self, nixpkgs, phps }:
let
# Systems supported
allSystems = [
"x86_64-linux" # 64-bit Intel/AMD Linux
"aarch64-linux" # 64-bit ARM Linux
"x86_64-darwin" # 64-bit Intel macOS
"aarch64-darwin" # 64-bit ARM macOS
];

# Helper to provide system-specific attributes
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
# Development environment output
devShells = forAllSystems ({ pkgs }: {
default = pkgs.mkShell {
# The Nix packages provided in the environment
packages = with pkgs; [
nodejs_20 # Node.js 18, plus npm, npx, and corepack
yarn
phps.packages.${system}.php74
phps.packages.${system}.php74.packages.composer
];

shellHook = ''
yarn
npx aws-cdk --version
php -v
'';
};
});
};
}

Read More

This blog post was written with Hexo and Nix

A few weeks ago I was at HighEdWeb 2023 in Buffalo and caught a talk (“Maybe some other time Docker: Fast, declarative and reproducible developer environments using Nix”) from Jason Woodward about Nix. Nix is many things, including an OS, but he focused on Nix-the-package-manager and that’s what I’m going to (briefly) discuss here.

To grossly oversimplify, Nixpkgs is a collection of tens of thousands of packages precompiled for various platforms. Each version of each package can be referenced by hash. Using a nix flake, you can specify which packages should be associated with your project. The nix develop command will then use path magic to load the specified packages into your shell. When you exit from the shell the paths are unloaded.

I moved this blog to hexo about a year ago. My authoring environment needs a few things: a reasonable version of node, hexo server running in the background to serve pages, and hexo generate --watch to catch changes as I write. My old process involved manually kicking those processes off. Before I could do that, I had to run nvm use to leverage the .nvmrc file at the root of my project directory to ensure that I had the correct version of node active, including the global reference for hexo.

Read More

Space Probe Taurus

The vibe on Space Probe Taurus is weird, for lack of a more specific term. It has the bones of a typical 1950s-1960s hard science fiction near-future let’s-go-conquer-space flick. The four main characters, the only ones we spend any real time with, are stock types: the hard-driving commander, the older scientist/engineer, the tough-on-the-surface female scientist, and the wiseguy she eventually falls for. They crew a rocketship dispatched from Earth in the year 2000 to explore outer space and find a planet suitable for colonization.

Things start weird and get weirder. We open not with this crew, but with the last surviving member of a different rocketship. The ship is located on an undefined planet, the rest of the crew killed by an unspecified disease and/or radiation. Somehow in real-time communication with Earth (this movie’s hand-waving of time and space problems anticipates J. J. Abrams by over 40 years), he implores for Earth to destroy the ship and kill him. After some back-and-forth, Mission Control pushes a button on a panel (!) and destroys the ship.

On the one hand, this trope of self-sacrifice for the conquest of space was pretty common. The Quatermass movies were all over this territory; First Man into Space is another example. On the other, and this is going to be a pattern with this movie, we never really mention this crew and its sacrifice again. Our crew doesn’t travel to this planet; it’s not mentioned in dialogue. I’m not sure it’s a wrong choice–the planet was clearly unsuitable, no use throwing good money after bad etc–it’s just unexpected.

Read More

So your Google WiFi pucks have the slows

The short version of this post is that the “Preferred activities” feature in the Google Home app may have undesired results, and before tearing your hair out try turning it off to see if things improve. Read on for discussion.

The problem

I live a row house and, as it typical of row houses in the Northeast United States, it’s tall and deep, rather than wide. This always posed a problem for WiFi coverage–the router is in the front of the house where the cable comes in from the street, so by the time you reach the kitchen in the rear of the house, to say nothing of the second and third floors, the signal became weak or non-existent.

Read More

Multiple PHP versions under the same domain

I recently had cause to run two different versions of PHP under the same top-level domain. I was able to accomplish this using Apache and PHP-FPM, leveraging Apache’s Directory directive. This builds on DigitalOcean’s excellent tutorial, “How To Run Multiple PHP Versions on One Server Using Apache and PHP-FPM on Ubuntu 18.04”, and assumes that you have done something similar to get multiple versions of PHP running using PHP-FPM.

In my use case, I have multiple different version of Moodle running under the same top-level domain. Each Moodle version is installed in a different directory under that domain, and has its own uploads and database. Moodle is somewhat aggressive about supported PHP versions, and I needed to have both PHP 7.4 (for Moodle 3.9) and PHP 8.0 (for Moodle 4.1) available, and each cannot run under the other version.

The solution is to add an Directory override for the site that needs the lower version of PHP, while leaving the higher version as the default case:

Read More

Browser Headers and WordPress

We’ve been running WordPress using Cloudfront as a full-site front-end cache with a low TTL and it’s been great overall, but one pain point has been which headers to whitelist in the origin configuration. This is pretty important: origin behavior governs what data Cloudfront passes back to the actual containers (or EC2 instances, it doesn’t matter in this case) serving your WordPress site. Omitting the wrong headers can have some fairly drastic consequences, like rich-text editors not displaying or REST API calls failing. This post takes Cloudfront as its example, but this would be relevant with any solution where you’re defining an allowlist of headers instead of just passing everything.

Behaviors

Before tackling headers, let me say a few words about behaviors in Cloudfront. Carl Alexander does a great job on this in How to use CloudFront to do WordPress page caching. For each behavior in Cloudfront you can define a different origin (e.g. Fargate containers, an S3 bucket, a Lambda function), which HTTP methods to allow, which headers to allow, what the caching policy should be. You differentiate behaviors by path. In a WordPress install, that means you could define different behaviors for each of the following paths:

Read More

Proxying Cloudfront with Nginx

Don’t do this.

You’re still here? Cool, let’s continue. For complicated reasons involving a legacy on-premises application I had a static site on AWS that I needed to route to with an Nginx reverse proxy. The application architecture included several locations, each of which was proxied to a different port on an on-premises server. The landing page, and only the landing page (and its assets), are on Cloudfront. For historical reasons, I needed to have this entire collection under a single DNS name.

Location, location …

Read More

EFS mount watchdog and CPU usage

The moral of today’s story is that there is a direct relationship between the number of EFS mounts on an EC2 instance and the resting level of CPU usage.

We ship our Fargate deployments with a bastion host if they contain an EFS filesystem and/or an RDS cluster. This takes the form of an EC2 autoscaling group. As we only need the bastion host for time-limited tasks, we keep the desired capacity at 0 by default and raise to 1 as needed. We use the scaling policies to ensure that the instance is turned down after a period of inactivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const bastionCPUThreshold = opts.cpuThreshold ?? 1;
const bastionScalingPeriod = opts.scalingPeriod ?? Duration.minutes(30);

const CPUUtilizationMetric = new Metric({
namespace: 'AWS/EC2',
metricName: 'CPUUtilization',
dimensionsMap: {
AutoScalingGroupName: this.autoScalingGroup.autoScalingGroupName,
},
period: bastionScalingPeriod,
});

const scalingPolicy = this.autoScalingGroup.scaleOnMetric('ScaleToCPU', {
metric: CPUUtilizationMetric,
scalingSteps: [
{ upper: bastionCPUThreshold, change: 0 },
{ lower: bastionCPUThreshold, change: 1 }
],
adjustmentType: AdjustmentType.EXACT_CAPACITY,
});

Read More

Customizing the CDK bootstrap stack

Amazon’s CDK creates a helper Cloudformation stack (by default named CDKToolkit) with a few resources to assist with deployment. The most familiar to regular users are the S3 asset bucket and the ECR repository.

A source of mild frustration for my colleagues and me is that this default stack doesn’t set lifecycle policies on either, which then trips up Amazon’s security controls (S3 10 and ECR 3, respectively).

Fortunately, there is a relatively straightforward solution, and that’s customizing the Cloudformation template that manages the CDKToolkit stack. I hadn’t realized before now that you can do this but it’s well-documented. First, use cdk bootstrap to output the contents of the template. You’ll want to ensure that you’re using the same version of CDK that you’ll use to deploy the stack:

Read More

Moodle, TypeScript, and CDK

We moved our Moodle workloads to AWS in 2020, using CDK to manage the infrastructure. It’s one of our oldest such deployments and has been stable for years. I was nonplussed, to say the least, when, I attempted to update our development environment to the Moodle 4.1 beta and received almost 400 TypeScript errors like the one below:

1
public/lib/editor/tiny/js/tinymce/tinymce.d.ts(7,10): error TS2304: Cannot find name 'Range'.

It took me a few minutes to realize what I was actually looking at. We install the Moodle core code in the public directory of the project. CDK’s npm run build command wraps tsc, the typescript compiler, and it was looking at TypeScript modules inside the Moodle directory. Thanks to a helpful hint from Stackoverflow, I modified tsconfig.json to exclude public:

Read More