
Installing a Self-Hosted GitHub Actions Runner on Synology NAS
A step-by-step guide to setting up Synology NAS for launching your own GitHub Actions Runner.
As developers, we all love to observe the glow of green indicators and mesmerising dance of technology that builds and tests our apps. Unfortunately, in the iOS world, reality doesn't look so good. CI services are unaffordable for our pet projects, and hiring a dedicated build machine is even more expensive. The problem, of course, is not that these services are greedy, but the platform to which we are tied. An iOS app can only be built on a macOS, that's it.
In this article, I want to offer a workaround to this problem and show how you can use your own Apple computer that you already use for work as a CI machine. We are going to use GitHub Actions as the CI framework and VirtualBox to run our CI machine in the background. The only requirement is to have an x86 architecture Apple machine and at least 100GB of disk space on your computer. As far as I know, VirtualBox doesn't support the M1 architecture. Still, you can use other virtualization solutions, such as Parallels or VMware Fusion, to achieve the same result.
Running virtualised macOS is a historically touchy subject. As far as I know, everything we do here is legal under Apple's EULA. We're not jailbreaking or running a hackintosh. We're running virtualized macOS on genuine macOS, which is mentioned in the following clause of the EULA.
Quote: (iii) to install, use and run up to two (2) additional copies or instances of the Apple Software within virtual operating system environments on each Mac Computer you own or control that is already running the Apple Software, for purposes of: (a) software development; (b) testing during software development; (c) using macOS Server; or (d) personal, non-commercial use.
Of course, use your best judgement, as I am not a lawyer and cannot give you advice in this area.
As GitHub states itself, "GitHub Actions is an API for cause and effect on GitHub: orchestrate any workflow, based on any event". I'm going to use this particular platform because it gives you a massive amount of features for free if you provide your own build machine. In our case, it's really exciting because by providing a part of our machine, we can get a full-featured macOS CI.
GitHub Actions extends far beyond just building your project. You can do literally anything with it, including good old Bash scripting. The platform uses YAML files to set up the build process defined by steps. The steps can be anything from your own scripts to the massive library of community written and official steps. Likely, everything you might need is already written for you, making your CI setup extremely versatile.
GitHub also offers its own build servers and even free monthly minutes for you to try it out. Unfortunately, in the iOS world, the problems remain. On GitHub, build time is extremely expensive for us and is calculated in multiples of 10 compared to Linux minutes.
So how do we use our machine as a build server, you might ask. GitHub has made it dead simple. Launch the GitHub Action Runner on your machine ( I have another article on how to run it on a Synology NAS) and link it to your account, done. Once your machine is linked, you specify in the YAML file that you want to use a self-hosted build machine then everything works like a charm.
As I mentioned earlier, we are going to run GitHub Actions Runner on a virtual machine. There are several reasons why you might want to do this. First and foremost, there are security concerns. Since you going to be running third-party code provided by the community, you don't want this code running on your main machine. Secondly, it's convenient. We don't want this process running uncontrollably on our machine, spawning processes and installing all sorts of software. By moving it to a virtual machine we isolate that environment, so we don't need to worry much about what happens there. And of course, the virtual machine will have a manageable amount of allocated memory, running neatly in the background.
Sounds good, right? Now let's try to put this into practice.
The installation process may be quite cumbersome, so be sure to follow each step without skipping anything "unnecessary". During the installation process, we will create a bootable ISO file which we will use to install macOS on the virtual machine. Next, we'll prepare a build environment sufficient to build an iOS project. When everything is ready, we'll be able to link it to our GitHub account. As a final step, we'll set up the trickiest part and make everything launch automatically in the background when our machine boots up.
The macOS distribution has everything you need to create a bootable ISO image, thus we won't use any unnecessary third-party tools here. All you need is the official macOS distribution and the terminal.
hdiutil create -o /tmp/BigSur -size 16G -layout SPUD -fs HFS+J -type SPARSE
hdiutil attach /tmp/BigSur.sparseimage -noverify -mountpoint /Volumes/mac_install
createinstallmedia
utility from the macOS distribution, make the mounted image a macOS installation image sudo /Applications/Install\ macOS\ Big\ Sur.app/Contents/Resources/createinstallmedia --volume /Volumes/mac_install
hdiutil detach /Volumes/Install\ macOS\ Big\ Sur
. You may need the -force
flag if it is constantly busy.hdiutil convert /tmp/BigSur.sparseimage -format UDTO -o /tmp/BigSur.iso
cdr
to iso
mv /tmp/BigSur.iso.cdr ~/Desktop/BigSur.iso
rm /tmp/BigSur.sparseimage
That's it. Now that you have a bootable ISO file to install macOS, we can move on to the next step.
My version of VirtualBox at the time of writing is 6.1.22, which may be important if you want to follow these steps in the same environment.
BigSurCI
, Type: Mac OS X
, Version: Mac OS X (64-bit)
, Memory size: 4096 MB
, Hard Disk: Create a virtual hard disk now
.160 GB
(This is a lot, you can reduce the size, but I would suggest at least 100GB, Xcode and CI will take up a lot of space), Type: VDI
, Storage on physical hard disk: Dynamically allocated
(If you don't care about disk space, a fixed size will probably work a little better).2
.128
.off
.SSH Rule
, Protocol: TCP
, Host IP: 127.0.0.1
, Host Port: 2222
, Guest IP: leave it blank
, Guest Port: 22
. With these settings, you should be able to connect to your virtual machine via SSH by connecting to 127.0.0.1:2222
. Later you will need to enable the Remote Login feature for it to work.Disk Utility
.VBOX HARDDISK
media and press the Erase button with the following settings. Name: Macintosh HD
, Format: Mac OS Extended (Journaled)
, Scheme: GUID
.Disk Utility
and select Install macOS Big Sur
.Never
. If you are still experiencing problems with your computer going to sleep, you can always use
this app to get around this problem.Automatic Login
for your user.Remote Login
.Now that we've gone through all this, we can finally start to reap what we've sown. Let's set up everything we need so that our CI can build an app, and then when we see that it works, we can do the final polishing.
ssh [email protected] -p 2222
. Use your username and password of course. If you wish, you can continue to perform all actions in your virtual machine's terminal.sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
./bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
. This may take some time as the Command Line Tools
for Xcode need to be installed along with it. It may get stuck during installation. Rebooting the system and performing this step again is likely to help. If you keep having trouble getting through the Command Line Tools
installation step, you can do this beforehand with a separate xcode-select --install
command.sudo gem install cocoapods
brew install fastlane
Since we now have a complete build environment, this should be enough to build almost any native iOS project. It's time to install the GitHub Actions Runner, which will build your projects on this machine.
We are going to add the GitHub Actions Runner to a single repository to simplify the process. Of course, you can add it to your organisation to share across all your projects. To add the GitHub Actions Runner to your repository, on GitHub open your repository and go to Settings -> Actions -> Runners, click Add Runner. You will be presented with instructions on how to download and activate your Runner. By following these instructions, you will have a fully configured and ready to run CI. You can try it out now.
Although your CI is now up and running, it is far from perfect. You still have VirtualBox and a virtual machine running in the dock. What's more, if you reboot your computer, all the magic will instantly disappear. Let's fix this and make this setup as seamless as possible. To achieve this, we will use launchd
on both the host and virtual machines. On our host machine, we need to make the virtual machine automatically launch in the background. On the other hand, the virtual machine should automatically start the GitHub Actions Runner at startup. The syntax of the launchd
files is quite complicated. If you want to do some customisation I can recommend an
excellent resource that has a description of all these settings.
sudo
privilege, we create a launchd
file which will launch the GitHub Actions Runner when the system starts. Additionally we will set KeepAlive
to keep it running and restart it in case of failure. Create the file sudo vim /Library/LaunchAgents/org.my.actions.runner.plist
and copy the following content into it. Don't forget to update the file path to the correct run.sh
location on your virtual machine.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key> <false/>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>Label</key> <string>org.my.actions.runner</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/bin:/usr/bin:/usr/local/bin</string>
<key>RUNNER_ALLOW_RUNASROOT</key>
<string>1</string>
<key>LC_ALL</key>
<string>en_US.UTF-8</string>
<key>LANG</key>
<string>en_US.UTF-8</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Users/test/actions-runner/run.sh</string>
</array>
</dict>
</plist>
sudo shutdown -r now
and make sure the GitHub Actions Runner is up. After restarting, you can verify that it's up by logging back into ssh
and doing something like this ps -ax | grep runner
. Alternatively, you can see it running on GitHub itself, on the page where you added it. It should show up in the list of Runners with a green indicator next to it. In case it doesn't work properly, I suggest setting up logging paths in that plist
we just created and looking for more troubleshooting advice at
aforementioned website.At this point, your virtual machine is fully automated. Our next and final task is to get this machine to run in the background when your computer starts up. We will use the same approach with launchd
as with the GitHub Actions Runner.
sudo
privilege to create a launchd
sudo vim /Library/LaunchAgents/org.github.actions.vm.launch.plist
file with the following content. Again, remember to check the path to VirtualBox and the name of your virtual machine.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key> <false/>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>Label</key> <string>org.github.actions.vm.launch</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/VirtualBox.app/Contents/MacOS/VBoxManage</string>
<string>startvm</string>
<string>BigSurCI</string>
<string>--type</string>
<string>headless</string>
</array>
</dict>
</plist>
I would like to point out that I have found no way to quickly stop this machine in the background other than toggling the Disabled
flag in the plist
and restarting the computer. If you simply shut it down in VirtualBox, it will restart immediately. If you know of a better way to solve this problem, I would highly appreciate your suggestions.
If you're on a tight budget, this might be a good option for you to get your hands on a real macOS CI. I tried this setup for a month, and from my personal experience, the only problem I encountered was editing videos in DaVinci Resolve, which persisted until I shut down the virtual machine. You also have to sacrifice a small amount of RAM, but you probably won't notice it. Another negative aspect worth mentioning is that the build will fail if you put the machine in sleep mode during the build. These are the only problems I've personally noticed, and frankly, I was expecting many more troubles with this. Anyway, the choice is yours. I am personally leaning towards a bare metal setup and hope that type 1 hypervisors will soon be available on Apple M1 processors. Stay healthy and good luck with your experiments!
Computer: iMac 2019 3,2 GHz 6-Core Intel Core i7 16 GB
MacOS: BigSur 11.4
Virtual Box: 6.1.22
GirHub Actions Runner: actions-runner-osx-x64-2.278.0.tar.gz
CocoaPods: 1.10.1
fastlane: 2.185.1
Xcode: 12.5
A step-by-step guide to setting up Synology NAS for launching your own GitHub Actions Runner.
React Native and Flutter comparison. How to choose the right platform.