At 3:17 AM, my free server restarted. My C++ service did not come back. It stayed down for six hours before I noticed. A 25-line watchdog script, generated by a free model and reviewed by me, fixed the problem. Since then, the service has survived three restarts without a single manual intervention. This is the tutorial I wish I had.
Why a watchdog at all
Free servers are not like the VMs you rent by the hour. They get restarted for maintenance. They get killed for memory pressure. Sometimes they just disappear for a minute. Your process does not come back by itself.
I had a small C++ daemon running on a free server option from MonkeyCode. It compiled clean, ran fine for days. Then the server rebooted at 3 AM and the daemon stayed dead. No cron, no systemd, no one watching. I only found out when a scheduled job failed.
Manual restart is not a strategy. You need something that checks the process and brings it back. That something is a watchdog.
What I wanted
The requirements were deliberately small:
- A single bash script, no dependencies beyond coreutils.
- Check every 5 seconds whether the daemon is alive.
- Restart it if it is dead, with a log entry.
- No zombie processes, no duplicate instances.
- Survive the watchdog itself being killed (by running it under
nohup).
I did not want a process manager. I did not want a container. I wanted one file I could read in a minute and trust in a year.
Step 1: A daemon to protect
To test the watchdog, I needed a service that could die. I wrote a minimal C++ daemon that prints its PID and uptime every second:
#include <cstdio>
#include <unistd.h>
#include <fstream>
int main() {
std::ofstream pidfile("/tmp/mydaemon.pid");
pidfile << getpid() << std::endl;
pidfile.close();
while (true) {
std::printf("alive, uptime=%d\n", (int)time(nullptr));
std::fflush(stdout);
sleep(1);
}
}
Compile it, run it, kill it. That is the whole test bed.
Step 2: The free model's first draft
I asked MonkeyCode's free model for a watchdog script with this prompt:
Write a bash script that checks every 5 seconds whether a process with a given PID file is alive. If not, restart the daemon and update the PID file. Log each restart with a timestamp.
The model returned this:
#!/bin/bash
PID_FILE="/tmp/mydaemon.pid"
LOG="/tmp/watchdog.log"
while true; do
if [ ! -f "$PID_FILE" ] || ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
echo "$(date): restarting" >> "$LOG"
/path/to/mydaemon &
echo $! > "$PID_FILE"
fi
sleep 5
done
It looked correct at a glance. It was not.
Step 3: The review that mattered
A free model generates fast. It also generates confidently wrong. Three issues stood out:
Issue 1: No kill -0 on a stale PID. If the daemon died and the PID got reused by another process, the watchdog would think everything was fine. The kill -0 check passes for any process owned by you, not just your daemon.
Issue 2: Restart race. Between the kill -0 check and the echo $! > PID_FILE, the daemon could crash again. The old PID would be overwritten with a dead one, and the watchdog would never notice until the next cycle. That is a lost update, the same class of bug I have seen in C++ concurrency, just in bash.
Issue 3: No duplicate protection. If the watchdog itself was started twice, you would get two loops, two restarts, and two daemons fighting over the same PID file.
None of these would show up in a 30-second smoke test. They would show up at 3 AM.
Step 4: The hardened version
I rewrote the script with three changes:
#!/bin/bash
PID_FILE="/tmp/mydaemon.pid"
LOG="/tmp/watchdog.log"
CMD="/home/user/mydaemon"
# Lock: only one watchdog instance
LOCK_FILE="/tmp/watchdog.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "$(date): another watchdog is running" >> "$LOG"
exit 1
fi
start_daemon() {
"$CMD" >> /tmp/mydaemon.log 2>&1 &
local pid=$!
echo "$pid" > "$PID_FILE"
echo "$(date): started pid $pid" >> "$LOG"
}
# Initial start if needed
if [ ! -f "$PID_FILE" ] || ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
start_daemon
fi
while true; do
if ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
echo "$(date): detected dead pid, restarting" >> "$LOG"
start_daemon
fi
sleep 5
done
The flock line prevents duplicate watchdogs. The start_daemon function writes the PID immediately after fork, shrinking the race window. The log now says why a restart happened.
Step 5: Deployment and the 3 AM test
I deployed the script to the free server and ran it under nohup:
nohup ./watchdog.sh > /dev/null 2>&1 &
Then I tested it deliberately:
kill -9 "$(cat /tmp/mydaemon.pid)"
sleep 6
cat /tmp/watchdog.log
# Sun Aug 21 03:17:12 UTC 2026: detected dead pid, restarting
# Sun Aug 21 03:17:12 UTC 2026: started pid 12345
Six seconds from kill to restart. Good enough.
Over the next seven days, the server restarted three times. Each time, the watchdog brought the daemon back within one check interval. No manual intervention. The log showed exactly what happened and when.
What I learned
Free servers are not broken, they are just honest. They do not pretend to offer 99.99% uptime. The instability is the price of free. A watchdog does not fight that reality, it works with it.
Free model code needs the same review as human code. The first draft was 90% right. The missing 10% was exactly the part that would fail in production. Treat generated scripts as a starting point, not a deliverable.
Simple tools beat complex ones. A 25-line bash script with flock and kill -0 is easier to audit than a container orchestration layer. For a single daemon on a free server, complexity is a liability.
Who should not use this approach
- If your service holds state in memory that must survive restarts, a watchdog is not enough. You need persistence.
- If you need distributed coordination or health checks across multiple servers, use a real orchestrator.
- If a 5-second downtime window is unacceptable, you need a load balancer and redundancy, not a script.
Build it yourself
If you want to reproduce this pattern, MonkeyCode is open source, and the free tier — 10 million tokens for model access plus a free server option — is available. Generate a daemon, generate a watchdog, then review the watchdog like your sleep depends on it. Because at 3 AM, it does.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Top comments (0)