Back to the blog ·

When the server asks permission

Alongside the serious services on my server, there is a Minecraft server. A scheduled job checks on it regularly, and when something goes wrong the cause is almost always the same: a new version is out and the old one has given up.

The obvious fix would be to install the update automatically. I decided against it, and the reason is the actual subject of this post.

Two extremes, both wrong

Doing it all by hand means: the job reports an error, I read the message in the evening, open the laptop, connect over SSH, look around, install the update. Five minutes of work spread across twenty minutes of context switching.

Doing it all automatically means: at three in the morning, an unsupervised script unpacks a new server version over a running directory. If something goes wrong there – an incomplete download, a changed directory layout, an overwritten config – I find out the next morning from the complaints.

Between the two sits the version I consider right: the job spots the problem itself, proposes the fix itself, and asks once. The decision takes two seconds on a phone; the machine handles everything before and after it.

The shape of it

In n8n this is five building blocks:

  1. Schedule Trigger – the scheduled run
  2. Execute Command – the actual check
  3. On failure: Telegram, "Send and Wait for Response" with response type Approval
  4. If on the result
  5. On approval: Execute Command with the update, then a report back

The third block is the one that matters. The Telegram node pauses the execution, sends a message with two buttons, and continues once one is clicked. No second workflow, no state management, no table of pending requests. n8n holds the paused execution until an answer arrives.

Four traps

Your failure branch is never reached. This is the mistake I made first. The Execute Command node throws on a non-zero exit code and aborts the execution – the branch below it never runs, and neither the success nor the failure message arrives. You do not notice something is broken; you only notice that it is quiet. In the node's settings, On Error has to be Continue (using error output).

Without a time limit the workflow waits forever. Do not press the button and the execution stays open. On an hourly job these pile up. Set a limit – an hour is plenty – and treat expiry as a "no".

The response link is the key. The buttons are links to your n8n webhook with a token in them. Whoever holds the link can trigger the action, with no access to your chat at all. Do not forward such messages, and put the recipient address in the node rather than taking it from the trigger.

A question without information is not a question. "Error occurred, install update?" is worthless – you have no idea whether the error has anything to do with the update. Put the actual error output in the message. Then you decide on what happened rather than on an assumption.

And the update itself

Two rules learned the expensive way with Minecraft Bedrock: back up before you touch anything, and exclude the config files when unpacking, or the archive will overwrite server.properties, permissions.json and allowlist.json with defaults.

tar czf backups/worlds-$(date +%F-%H%M).tar.gz worlds
tmux send-keys -t mc "stop" Enter
unzip -o -q bedrock-server-new.zip \
  -x 'server.properties' 'permissions.json' 'allowlist.json' 'worlds/*'

What I take from it

The interesting question in automation is rarely "can this be automated". Almost everything can. The interesting question is: what happens when it goes wrong and nobody is watching?

Where the answer is "nothing much", let it run. Where the answer is "data loss", put a human in the middle – but one for whom the machine has done all the work up to the decision, and takes over again after it. That is exactly what this prompt does. It costs two seconds and prevents the one case you would otherwise regret the next morning.