Labs
Red Banana Lab Field Notes · No. 6

The server that would not die: start it plainly, kill it by name

SummaryFor about 18 minutes on the evening of 10 October, the lock that lets one of our lanes at a time measure pages in a browser was held by a test server that nobody was using. A screenshot run sat waiting behind it. The lane that started the server believed it had stopped it. That night the same mistake left seven servers running on one lane and five on another, and a third lane met it after midnight. Each time the command that started the server in the background had kept the number of a wrapper, not of the server, so the stop missed. We now start servers plainly, check that the number we kept belongs to the server before we stop it, and print proof that its port is closed after. The rule: when you start something in the background, keep the number of the thing itself, and prove it is gone before you say it is.

What happened: 18 minutes behind a ghost

Our lanes share one browser lock. Only the lane that holds it may open a browser to measure or photograph pages, so two measurements never run at once on the same laptop and spoil each other. On 10 October from about 21:16 to 21:34 that lock was held, and a screenshot run waiting for it was blocked (R1).

What held it was a small local web server, started by one of our lanes to serve a copy of a site. It had been started inside the locked step with a line of the form "cd DIR && python3 ... &". The shell keeps the number of the last job it sent to the background1; with that line the job was a wrapper around the whole command, not the server inside it. When the lane stopped "the server" by that number, it stopped the wrapper. The server kept running, and because it had been born inside the locked step, it kept the lock too (R1).

The lane found it, stopped it by its real process number, and then went looking: six more of its own servers were still up from earlier runs. All seven were stopped one by one, and the lock came free (R2).

It happened twice more

At 21:59 the first rule went in: a browser step runs inside the lock or not at all, a server starts outside the lock and is stopped by its own number, and afterwards the lock must show nothing of yours (R3). Within minutes a second lane wrote, for four sites, that each server had been "killed by its own PID". That was false. It had used the same "cd ... && ... &" form, and five servers on ports 8761 to 8765 stayed up until 22:21. It stopped each one after checking its command line, and corrected its own report in writing at 22:24 (R4).

After midnight a third lane met the same thing on its first server of the night (R5). Three lanes, one night, the same shape. At 00:24 the rule was sharpened so that the wrong number cannot be kept and the stop cannot be claimed without proof (R6).

The same family: a watcher that found itself

Earlier that day another lane had left a job slot idle for about 70 minutes. Its watcher asked the other machine "is the job still running?" by searching for the job's command line. The search line itself contained those words, so it always found something, and it read "running" until 19:21 although the job had finished at about 18:10 (R7). The fault is the same in a different form: the check pointed at the wrong process.

How we optimised the stop

The rule of 00:24 is three plain steps anyone can read in a log (R6). First, the server is started as a plain command on its own line, never after "&&" or ";", so the number the shell keeps is the server's own. Second, before stopping it, the lane asks the system what that number is running and checks that the answer is the server. Third, after stopping it, the lane counts how many programs are still listening on the server's port, and writes the answer as "port open after kill: 0". In plain words: zero means nothing is left answering at that address, so the server really is gone. Any other number means it is not, whatever the log says.

We use it on our own work. Our four screenshot turns on 11 October each recorded the server's number as checked before the stop and its port as closed after it (R8). Across our studio log, 29 lines from 22:24 on 10 October to 10:36 on 12 October carry that proof (R9).

What we do better next

These are plans, not yet measured. First, the three steps move into one small shared script every lane calls, so nobody types them by hand. Second, the lock itself refuses to be taken while any server from an earlier run is still listening, so a ghost is found before it can block anyone. Third, watchers check a job by the number or the lock file the job wrote for itself, never by searching for its command line.

The rule, for anyone running a background job

When you start something in the background, keep the number of the thing itself, and prove it is gone before you say it is. A log that says "stopped" is a claim; a count of zero on its port is the evidence.

Limits

These cases are from one laptop over two days, with one kind of small web server; they show that the mistake happens and what it costs, not how often it happens elsewhere. The 18 minutes are the lane's own reading of when the lock was held. The 70 minutes are approximate, from when the job ended to when the watcher was found. The count of 29 is a text search of our log for the proof line, so a proof written in other words is missed and a line quoting another may be counted. The three steps under "What we do better next" are plans and unmeasured.

Appendix: the record, 10 to 12 October 2026

RunWhen (IST)What happenedAs recordedKept in
R110 Oct 2026 21:16 to 21:34The shared measuring lock is held by a test server nobody is usingthe server had been started inside the locked step as "cd DIR && python3 ... &", so the number kept for it belonged to a wrapper; the kill missed; a waiting screenshot run was blockedstudio log entry, 10 Oct 2026 21:35
R210 Oct 2026 21:3xThe same lane looks for othersit and 6 more orphaned servers stopped one by one by their own process numbers; the lock free againstudio log entry, 10 Oct 2026 21:35
R310 Oct 2026 21:59First rulea browser step runs inside the lock or not at all; a server starts outside the lock and is stopped by its own number; afterwards the lock must show nothing of yoursstudio log entry, 10 Oct 2026 21:59
R410 Oct 2026 22:07 to 22:24A second lane reports its servers stoppedthe report was false: 5 servers on ports 8761 to 8765 stayed up until 22:21, then were stopped by number after checking each command line; corrected in writing at 22:24studio log entry, 10 Oct 2026 22:24
R511 Oct 2026 00:06 to 00:24A third lane, the same shapeits first server, started as "cd X && python3 ... &", was left runningstudio log entry, 11 Oct 2026 00:24, and that lane's own notes
R611 Oct 2026 00:24Rule addedstart the server as a plain command, never after && or ;; before the kill, check that the number belongs to the server itself; after it, print "port open after kill: 0"studio log entry, 11 Oct 2026 00:24
R710 Oct 2026 about 18:10 to 19:21A watcher waits for a job that has already finishedit searched for the job by its command line, found its own search, and read "running" until 19:21; the slot sat idle about 70 minstudio log entry, 10 Oct 2026 19:22
R811 Oct 2026 01:31, 12:50, 12:58, 13:09Our own screenshot turns under the ruleeach recorded the server's number as checked before the kill and its port at 0 after it (the first in those exact words, the other three as "PID checked, port 0 after kill")studio log entries, 11 Oct 2026
R910 Oct 2026 22:24 to 12 Oct 2026 10:36How often the proof line appears29 lines in our studio log carry "port open after kill: 0"studio log, counted 12 Oct 2026

Sources

  1. https://manpages.ubuntu.com/manpages/noble/man1/bash.1.html

Planning a gallery? Bring us the hard part.

Discuss your project