AI Portfolio Lab Tools Games Blog Feedback
All Posts

My Restart Kept Serving Old Code

I wrote a deploy script for the Tagalog trainer after two deployment bugs bit the live server. The watchdog I already built was doing it correctly. I just wasn't using it.

2 min read
aitools

I run the Tagalog trainer on a home Windows machine. Self-hosted, Tailscale-gated, not on any public site. Deploying to it felt like the least interesting part of the project. It’s my box. I know where it lives.

Two bugs later, I wrote a deploy script.

schtasks /end doesn’t kill the process it launched. It detaches from it. The Node server stays alive, holds port 8790, and keeps serving yesterday’s code. I’d pull the latest changes, stop the task, restart it, and nothing would change. The old version never left. The new process couldn’t bind the port, so it died while the old one kept going.

The fix is to kill by command line, not by task name. Find the processes running whisper and the trainer server, Stop-Process -Force, then start the tasks. There’s a watchdog I wrote for this project that does exactly that, every five minutes, correctly. The watchdog never had this problem. I just wasn’t using its approach when deploying by hand.

; is a command separator in bash. In cmd.exe, which is what you get when you SSH into a Windows box, it isn’t. I was chaining two commands into a single SSH call with a semicolon. The first ran. The second was attached as an argument to the first and got dropped. Two commands, one of them silently gone.

Separate SSH calls. Or join them in a layer that knows what & means.

Both of those bit live. Neither one announced itself. The first looked like a successful restart with no change on the page. The second ran the first command correctly and then nothing, no error, the second half just wasn’t there. The failure mode in both cases was silence, which is what makes them the kind of bug you need to encode rather than remember.

I wrote deploy.sh after both hit. It’s 39 lines. Pull on the box, kill processes by command line, start the tasks, sleep eight seconds, hit /api/health, exit nonzero if the judge isn’t up or warming. Every step verifies before the next one.