I'm blogging about Emacs, Drupal, php and exciting subjects

Ansible orchestration: the thing Puppet's model cannot express

Take a requirement that sounds unremarkable: upgrade twelve web servers with no downtime. One at a time, drained from the load balancer first, health-checked before returning to service, and the whole thing stops if more than a quarter of them fail.

Every word of that is about coordination between machines. And that is precisely the thing a per-node convergence model has no vocabulary for.

Measuring search quality: judged sets, nDCG, and knowing whether you improved anything

Here is how relevance tuning usually goes. Someone complains that searching for the company name returns a press release above the About page. You raise the title boost. You try the query. It is fixed. You ship it.

What you do not know — cannot know, from that process — is what happened to the other four thousand queries people run against your search box. Maybe nothing. Maybe you just broke a hundred of them in a way nobody will report, because people who do not find what they want do not file bug reports. They leave.

Hybrid search: combining BM25 with embeddings in OpenSearch

There is a class of search failure you cannot tune your way out of. Someone searches for infrastructure automation; you have an excellent page about configuration management; the words never overlap and BM25 scores it zero. No field boost fixes that, no stemmer, no synonym list you will actually maintain. The term simply is not there.

Drupal Search API with OpenSearch: from setup to relevance tuning

Most Drupal search articles stop at "and now it works." That is the easy half. The hard half is the phone call three weeks later where someone points out that searching for the company's own name returns a 2019 press release above the About page, and nobody can explain why.

This covers both — getting OpenSearch wired up behind Search API, and then actually understanding the scores it produces well enough to change them on purpose.

Provisioning a PHP web server with Ansible: MySQL, PHP-FPM, nginx and Let's Encrypt

Four packages, one playbook. This should be a boring afternoon, and it mostly is — except for one ordering problem that breaks almost every tutorial on the subject, in a way that only shows up on a genuinely fresh server.

So let me start there, because if you understand it the rest is just YAML.

The deadlock nobody warns you about

You write an nginx vhost template with TLS configured, because that is the end state you want:

Installing OpenVox 8 on Ubuntu 24.04: the open-source Puppet replacement

If you came here looking for how to install open-source Puppet on a current Ubuntu, the short answer is that you can't, and it is not your fault for not knowing. The instructions everyone wrote — add deb.puppetlabs.com, install puppet-agent — stopped producing new versions in early 2025.

This article covers what to do instead. If you are running Puppet today, the migration is at the bottom and it is less painful than you are probably expecting.

tmux and Emacs: settling the keybinding war

Run Emacs inside tmux with the stock configuration and you hit the problem within about four seconds. tmux's prefix key is C-b. In Emacs, C-b is backward-char — one of the six or seven keys you press most in any editing session.

So every tmux command now begins by moving your cursor one character left, and every attempt to move left now opens a tmux prefix that swallows whatever you type next. Both tools are behaving correctly. They just want the same key, and the collision is on a binding neither side can reasonably give up.

Multistage Docker builds for PHP: keeping composer install out of your rebuild loop

Here is the Dockerfile almost everyone writes first:

FROM php:8.3-fpm

RUN apt-get update && apt-get install -y git unzip libicu-dev \
    && docker-php-ext-install intl opcache pdo_mysql

COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer

WORKDIR /app
COPY . .
RUN composer install --no-dev

It works. It is also the reason your build takes ninety seconds every time you fix a typo.

Writing a polite web crawler

Every other piece of a search engine operates on data you already have. The crawler is the one component that reaches out and spends somebody else's money — their bandwidth, their CPU, their database connections — without asking first.

Subscribe to