overengineered/OVERKILL

My homelab laptop wakes my gaming PC, hands it a job over SSH, then sends it back to bed

6 MIN READPOST 2 OF 2
TL;DR
WHAT
A homelab laptop wakes my gaming PC with Wake-on-LAN, runs heavy jobs on it over SSH, and shuts it down again
STACK
Wake-on-LAN · OpenSSH on Windows · bash · Tailscale
LINKS

the itch

I have two machines that complement each other in a slightly annoying way.

The laptop has the uptime. It runs the homelab and is on all day. The gaming PC has the real GPU, a lot of RAM and the fans of a small drone, and it costs power and noise every hour it’s awake. I only need it for a few of those hours.

So the laptop is the always-on boss and the PC is the heavy worker, asleep until there’s a job. Wake it, hand it the job, read the output from my chair, switch it off again. Is that overkill for one person? Yes. That’s why it’s in this series.

how it works

01Laptopruns `pcpy job.py`
02Magic packetto the LAN broadcast
03PCsshd answers, venv ready
04Joboutput streams back
two SSH keys: one runs jobs, the other is only for switching the PC off
  • pcctl is a small bash script with three verbs: on, off and status.
  • on sends the magic packet. status just checks whether port 22 answers.
  • pcpy wakes the PC if it has to, waits, copies the project over and runs a script there, streaming the output back.

The magic packet

Wake-on-LAN is older and dumber than it sounds. A magic packet is 6 bytes of 0xFF followed by the target’s MAC address repeated 16 times, sent as a UDP broadcast. The network card stays powered while the PC is off and watches for that pattern. No handshake, no authentication, no reply.

A broadcast stays inside one network segment, so the sender has to live on the same LAN. Here that’s easy: the laptop is on Wi-Fi, the PC is wired, and both share a subnet.

The card also needs permission to listen: a firmware setting (often Wake on LAN or Power On By PCI-E) and the adapter’s power options in Windows. Names vary by board and driver. Fast Startup is the usual suspect when wake works from sleep but not from a full shutdown. On this PC its flag is on but hibernation is off, and Fast Startup depends on hibernation, so it does nothing.

Port 22 is not “ready”

Pinging the PC is the wrong way to tell if it’s awake, because the network can come up before sshd does. pcctl status checks port 22 instead. pcpy polls it for up to three minutes, then waits five more seconds before it tries anything. Nothing has gone wrong without that pause. It’s a margin, because an open port doesn’t mean the machine is ready for work.

Running the job on Windows

pcpy copies the project’s src folder over with scp, then runs one script through SSH. Three details of that last line are worth stealing:

  • It calls the venv’s Python by path. .venv\Scripts\python.exe instead of activating the environment, which is one less thing to go wrong in a non-interactive shell.
  • set VAR=value&& next has no space before the &&. In cmd, a space there becomes part of the value.
  • The output goes through tr -d '\0'. Windows tools sometimes emit UTF-16, which shows up as NUL bytes on a Linux terminal.

Two keys, one dangerous

There are two SSH keys for the PC. The work key is for pcpy. The other is only for pcctl off, so the everyday key can’t do the dangerous thing by accident. A comment at the top of pcpy says it plainly: “Never use id_pc: that key shuts the PC down.”

pcctl off runs ssh with no command at all, so the server side must be doing the work. The usual way is to lock that key to one forced command in the PC’s authorized_keys. The repo’s README has an example. And because the laptop is on Tailscale, my iPhone has Shortcuts for the same three verbs: on, off and status.

known gaps

Nothing broke while I built this, which makes me suspicious. Here is where I’d look first.

  • The job runs in the foreground of an SSH session. If the connection drops, it probably goes with it. Running it detached, or as a scheduled task, is the fix.
  • pcpy doesn’t switch the PC off. pcctl off does, as a separate step I have to remember. The plan is a pcrun that wakes, syncs, runs, pulls results back and shuts down.
  • Wake-on-LAN has no security. Anyone on the LAN can send the packet. That’s fine at home, and it stays off the internet.
  • An AMD GPU on Windows can be a bumpy road for ML frameworks. Check what your stack supports first.
  • I haven’t timed the wake. I don’t have a number from pcctl on to SSH answering, and I won’t make one up.

steal this

pcctl is the whole control surface, with my network details replaced by placeholders. The working scripts and a README are in the pc-over-lan repo.

pcctl
#!/usr/bin/env bash
PC_IP=[PC IP]
PC_MAC=[PC MAC]
BCAST=[LAN BROADCAST ADDRESS]
case "$1" in
  on)     wakeonlan -i "$BCAST" "$PC_MAC" ;;
  off)    ssh -i ~/.ssh/id_pc -o BatchMode=yes -o ConnectTimeout=5 [USER]@"$PC_IP" ;;
  status) nc -z -w 2 "$PC_IP" 22 && echo up || echo down ;;
  *)      echo "usage: pcctl on|off|status" ;;
esac

pcpy wakes the PC if it’s down, waits for it, syncs the code and runs the script on the other side.

pcpy
#!/usr/bin/env bash
# pcpy <script.py> [args...] : sync the project to the PC and run a script there.
# Uses the work key. Never use id_pc: that key shuts the PC down.
K=~/.ssh/id_pc_work; H=[USER]@[PC IP]; IP=[PC IP]
O="-i $K -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=8"
if ! nc -z -w2 $IP 22 2>/dev/null; then
  echo "[pcpy] waking PC..." >&2; pcctl on >/dev/null 2>&1
  for i in $(seq 1 60); do nc -z -w2 $IP 22 2>/dev/null && break; sleep 3; done
  sleep 5
fi
[ -n "$1" ] || { echo "usage: pcpy <script.py> [args...]" >&2; exit 2; }
script=$1; shift
scp -q $O -r ~/[PROJECT]/src $H:C:/[PROJECT]/ || { echo "[pcpy] scp failed" >&2; exit 1; }
ssh $O $H "cmd /c \"set DATA_DIR=C:\\[PROJECT]-data&& cd /d C:\\[PROJECT]&& .venv\\Scripts\\python.exe src\\$script $* 2>&1\"" | tr -d '\0'

what i’d do differently

  • Detach the job from the SSH session. A dropped Wi-Fi link shouldn’t kill an hours-long run.
  • Finish pcrun. Wake, sync, run, pull results, shut down, in one command. Then there’s nothing to forget.
  • Light the desk strip while the PC works. The strip from the other post already knows how to show a colour. A busy PC deserves one too.
  • Time the wake, with one log line per run, to see whether the PC is slowly getting slower to boot.

secrets

This site has hidden things. Every one has instructions below, but what it is and what it does stays secret until you find it. You found 0 of 20.

Shortcut: press ? on any page to open this panel. The pixel buddy knows every secret and gives hints.