
How to Ensure Your GitHub Runner Stays Up? A Smart Solution with PM2
Introduction
Thursday evening. It’s cold outside, warm inside. A glass of whiskey is waiting for me, my favorite show is queued up, and then—the phone buzzes. “Our runners aren’t working! The CI is stuck!” I take a deep breath because I know that if I don’t fix this now, it’ll be even less fun in the morning.
Hoping for a quick fix, I log into the remote Mac. The setup looks familiar—just like the one outlined in this YouTube video (Detailed below). The GitHub Runners are being manually launched through terminal windows. There are two separate sessions running runners, and at first glance, it looks like everything is fine.
But here’s the catch: The moment the machine reboots, this entire setup disappears.
This is a classic case of declaring a task is done when “it works”- not when the task is actually done. The customer asked for a CI/CD pipeline, and they got one. But – they didn’t account for the “C” in Continuous.
So, after taking a moment to sigh (and sip my whiskey), I realized we needed a real solution—one that ensures the system keeps running even if the Mac reboots or the CI crashes.
Understanding the Problem: What the YouTube Video Shows
The setup in the video demonstrates a simple, manual way to launch GitHub Runners:
- Open a terminal window.
- Navigate to the runner directory.
- Run
./run.sh
to start the runner.
This works only as long as the terminal window stays open and the machine doesn’t restart. Once either happens—poof, the runner is gone, and CI/CD is broken.
This approach is great for testing but not production-ready. It fails to account for system reboots, meaning someone has to manually log in and restart the runner every time the Mac restarts. That’s not what we want. Instead, we need a solution that automatically restarts the runner, survives system reboots, and doesn’t require manual intervention every time the Mac restarts. Let’s compare our options –
Choosing the Right Solution
There are multiple ways to ensure the runner stays up, each with pros and cons. Here’s how they compare:
Solution | Ease of Setup | Process Management | Resilience to Restarts | Scalability | Logging & Observability | macOS Compatibility | Verdict |
---|---|---|---|---|---|---|---|
PM2 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ✅✅✅ | ❌ Not ideal for large-scale CI | ⭐ Basic logs only | ✅ Works well | Quick, lightweight, and easy to use |
launchd | ⭐⭐ (Steeper learning curve) | ⭐⭐⭐ | ✅✅✅ | ✅ Scales well | ✅ Integrated with system logs | ✅ Native support | More robust for long-term use, but harder to manage |
Docker | ⭐⭐ (More setup overhead) | ⭐⭐⭐⭐ | ✅✅✅ | ✅✅ Containerized and scalable | ✅ Centralized logging with sidecars | ⚠ Requires additional resources | Best for strict environment isolation, but overkill here |
Supervisor | ⭐⭐⭐ | ⭐⭐⭐⭐ | ✅ | ✅ Works well for multiple processes | ✅ Supports logging | ⚠ Limited macOS support | Great for Linux, but not ideal for macOS |
So, for a quick, lightweight solution, PM2 is the clear winner.
Fixing the Problem: Setting Up GitHub Runner with PM2
PM2 provides a simple, reliable way to keep our runner alive. Here’s how to set it up
1️⃣ Install PM2
brew install pm2
2️⃣ Start the Runner with PM2
pm2 start /Users/youruser/github-runner/run.sh --name github-runner
3️⃣ Save the Runner so It Restarts on Boot
pm2 save
4️⃣ Enable PM2 to Start on System Boot
pm2 startup
Managing Multiple Runners with an Ecosystem File (Optional, but Recommended)
Now that we have one runner working, what if we need more than one GitHub runner? Instead of repeating the same pm2 start command multiple times, we can define them in a single configuration file and manage everything at once.
5️⃣ Create an Ecosystem File for Multiple Runners
Instead of starting each runner manually, create an ecosystem.config.js file:
module.exports = {
apps: [
{
name: "github-runner-1",
script: "/Users/youruser/actions-runner/run.sh",
},
{
name: "github-runner-2",
script: "/Users/youruser/actions-runner2/run.sh",
}
]
};
6️⃣ Start All Runners with a Single Command
Once the file is created, use one command to start both runners:
pm2 start ecosystem.config.js
7️⃣ Save the Configuration
To make sure PM2 remembers these settings on reboot:
pm2 save
8️⃣ Check Runner status
To check our runners status:
pm2 status
And logs:
pm2 logs github-runner1
pm2 logs github-runner2
Conclusion
So, if you don’t want your GitHub Runner to crash the moment you sit down with a glass of whiskey, don’t rely on manually running it in a terminal. PM2 is the smart way to keep everything running smoothly—even when you’re watching a show, sleeping, or just not in the mood to deal with a stuck CI.
Fewer failures, less hassle—back to normal life, faster. Because who wants to fix things in the middle of a Thursday evening? 🚀