guide

Run Codemany on your own Linux server.

Codemany runs your GitHub Actions jobs on a machine you own. Set one up on a small box on your network, or on a cloud VM.

What you need

How big a machine

Codemany sizes the runner count to the machine. Each runner gets 4 GB of memory, 2 CPU threads and 15 GB of disk, and 4 GB of memory and 20 GB of disk stay free for the system. One runner runs one job at a time.

MachineRunners at once
4 threads, 16 GB, 256 GB disk2
8 threads, 32 GB, 512 GB disk4
16 threads, 32 GB, 1 TB disk6
16 threads, 64 GB, 1 TB disk8

A "16 GB" machine shows a little less to Linux, so it fits 2 runners, not 3. After installing, codemany capacity prints the real number and what limits it.

A fresh VM per job (the vm isolation mode) needs hardware virtualization: Intel VT-x or AMD-V, turned on in the BIOS. Check with ls /dev/kvm after installing Linux. This mode is experimental. The default mode does not need it.

1. A small box at home or in the office

Buy a mini PC

Any small x86-64 PC works. We bought this one: on Amazon. For about 6 runners, pick 8 cores (16 threads), 32 GB of memory and a 1 TB NVMe disk. For 2 runners, 4 cores and 16 GB are enough. Wired Ethernet is better than Wi-Fi.

Install Ubuntu Server 24.04 LTS

  1. Download Ubuntu Server 24.04 LTS from ubuntu.com/download/server.
  2. Write it to a USB stick (balenaEtcher, or Raspberry Pi Imager, both free).
  3. Boot the mini PC from the stick (usually F7, F11 or F12 at power-on) and follow the installer.
  4. On the SSH Setup screen, tick Install OpenSSH server.
  5. Pick a username. Jobs run as this user.

While you are in the BIOS: turn on VT-x / AMD-V (for VM-per-job), and set Restore on AC power loss to Power on, so the box comes back after a power cut.

Reach it over SSH

You can unplug the screen and keyboard now. Everything else happens over SSH from your Mac or PC. On the mini PC, hostname -I prints its address. Then, from your computer:

ssh [email protected]          # your username and the box's address

The apt package brings Avahi along, so your network finds the box by name (ssh you@<hostname>.local, http://codemany.local:8825). With the one-line installer, install it yourself: sudo apt install -y avahi-daemon avahi-utils.

Install Codemany

With apt (recommended: apt upgrade keeps it updated). The key is installed only if its fingerprint is 92EA E8F4 E140 E71F E883 C291 DBC1 D871 7AE5 E521:

curl --proto '=https' --tlsv1.2 -fsSLo codemany.gpg \
  https://codemany.com/download/linux/apt/codemany-archive-keyring.gpg
gpg --show-keys --with-colons codemany.gpg | grep -qx 'fpr:::::::::92EAE8F4E140E71FE883C291DBC1D8717AE5E521:' \
  && sudo install -m 644 codemany.gpg /usr/share/keyrings/codemany-archive-keyring.gpg \
  || echo "codemany.gpg is not Codemany's key: stop" >&2
echo "deb [signed-by=/usr/share/keyrings/codemany-archive-keyring.gpg] https://codemany.com/download/linux/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/codemany.list
sudo apt update && sudo apt install -y codemany

Installing starts Codemany as your service and prints the setup link.

Or with one line (any glibc distro; it checks the download's signed SHA-256 and starts the service for you):

curl --proto '=https' --tlsv1.2 -fsSL https://codemany.com/install.sh | sh

On a server (no desktop), installing also turns on lingering, so Codemany keeps running after you close SSH and starts at boot.

Finish setup in a browser

On a server, Codemany serves its setup page on your network until it is set up. Print its link again any time:

codemany setup-link

It prints http://192.168.1.50:8825/#code=… (and a 127.0.0.1 one). Open it from any computer on your network; http://codemany.local:8825/#code=… works too. The code makes it yours: without it, nobody can do anything on that page.

Not on the same network? Tunnel over SSH, then print the link for your end of the tunnel:

ssh -L 18825:127.0.0.1:8825 [email protected]
codemany setup-link --port 18825    # on the box: http://127.0.0.1:18825/#code=…

(Port 18825 on your side, because a Mac running Codemany already uses 8825.)

In the setup page:

  1. Connect GitHub: enter a code on github.com, or paste a token.
  2. Pick repos, or an organization.
  3. Start runners. The 14-day trial starts here, no card.

To keep setup on the box itself (tunnel only), install the service with codemany install-service --setup-listen 127.0.0.1:8825. On a cloud VM (Google Cloud, AWS, Azure, DigitalOcean, Hetzner and others) that is the default: setup stays on the box, and you reach it through the tunnel.

Use it in workflows

jobs:
  test:
    runs-on: [self-hosted, Linux, codemany]

Keep Linux in runs-on: without it, a job can land on your Mac too.

Open the dashboard

Once set up, the dashboard is on your network at http://codemany.local:8825. If your Mac runs Codemany too, it already has that name, and the box is http://codemany-2.local:8825. http://<hostname>.local:8825 always works.

Without the admin token, the dashboard is read-only, also on the box and through an SSH tunnel; its footer says so. To use its buttons, run codemany admin-token on the box and paste the token into the dashboard's Admin token button.

Add it to your Mac's fleet

  1. On your Mac, open Machines. The box shows under Nearby.
  2. Choose Add to the fleet…. The Mac asks the box for a code.
  3. On the box, print the code: codemany pair.
  4. Type it on the Mac. Both now show All machines, and either one opens the other.

If the box isn't under Nearby, choose Add a machine… and type its address (192.168.1.50:8825). If ufw is on, allow your network: sudo ufw allow from 192.168.0.0/16 to any port 8825.

Keep it updated and awake

Ubuntu Server installs security updates by itself (unattended-upgrades). To let it update Codemany too:

echo 'Unattended-Upgrade::Origins-Pattern { "origin=Codemany,suite=stable"; };' \
  | sudo tee /etc/apt/apt.conf.d/52codemany-unattended

Or update by hand: sudo apt update && sudo apt upgrade.

Nothing else to do: Codemany notices the new version, lets running jobs finish, and restarts into it. To restart by hand without cutting a job short:

codemany drain --wait && systemctl --user restart codemany && codemany resume

Ubuntu Server does not sleep. If you installed a desktop edition, stop it from sleeping:

sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

2. A cloud VM (Google Cloud)

A cloud VM costs money every hour it runs, and it is not on your network. Read Reaching it safely before you start.

Set up Google Cloud

  1. Create an account at cloud.google.com and open the console.
  2. Billing: add a billing account (Billing › Manage billing accounts).
  3. Project: create one (the project picker at the top › New project), for example codemany-ci, and link it to the billing account.
  4. Compute Engine: open Compute Engine › VM instances and choose Enable for the Compute Engine API.

To use commands instead, install the gcloud CLI, then:

gcloud auth login
gcloud projects create codemany-ci-$RANDOM --name="Codemany CI"   # note the id it prints
gcloud config set project PROJECT_ID
gcloud billing accounts list
gcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID
gcloud services enable compute.googleapis.com

Create the VM

Recommended: e2-standard-4 (4 vCPUs, 16 GB: 2 runners), Ubuntu 24.04 LTS, a 100 GB balanced disk, in a region near you (here us-central1).

gcloud compute instances create codemany-1 \
  --zone=us-central1-a \
  --machine-type=e2-standard-4 \
  --image-family=ubuntu-2404-lts-amd64 --image-project=ubuntu-os-cloud \
  --boot-disk-size=100GB --boot-disk-type=pd-balanced

In the console: Compute Engine › VM instances › Create instance. Name codemany-1, region us-central1, machine type e2-standard-4. Under OS and storage, choose Change: Ubuntu, Ubuntu 24.04 LTS (x86/64), balanced persistent disk, 100 GB. Leave the firewall boxes (HTTP, HTTPS) unticked. Create.

A fresh VM per job needs nested virtualization. E2 doesn't offer it; use an Intel N2 machine instead:

gcloud compute instances create codemany-1 \
  --zone=us-central1-a \
  --machine-type=n2-standard-4 \
  --enable-nested-virtualization \
  --image-family=ubuntu-2404-lts-amd64 --image-project=ubuntu-os-cloud \
  --boot-disk-size=100GB --boot-disk-type=pd-balanced

In the console, choose the N2 series and turn on nested virtualization in the machine configuration's advanced settings. If you can't find it, use the command above. On the VM, ls /dev/kvm should then show the device.

Install Codemany

SSH in (or use the SSH button in the console):

gcloud compute ssh codemany-1 --zone=us-central1-a

On the VM, install with apt or the one-liner exactly as in Install Codemany above.

Reaching it safely

Never open port 8825 to the internet. Don't add a firewall rule for it. Google Cloud's default firewall already blocks it; keep it that way.

Reach the setup page and the dashboard through an SSH tunnel instead. Codemany knows it's on a cloud VM and keeps setup off the VM's network, so the tunnel is the only way in. From your computer:

gcloud compute ssh codemany-1 --zone=us-central1-a -- -L 18825:localhost:8825

While that runs, http://127.0.0.1:18825 on your computer is the VM's dashboard. For setup, open the link codemany setup-link --port 18825 prints on the VM, then connect GitHub and pick repos as in Finish setup in a browser.

To close port 22 to the internet too, use IAP: allow SSH from Google's IAP range only (35.235.240.0/20), and add --tunnel-through-iap to the gcloud compute ssh commands.

codemany.local doesn't reach a cloud machine. Names like that only work on your local network. The cloud VM is managed on its own, through the tunnel, and doesn't show in your Mac's fleet. If you join it to the same private network as your other machines (a VPN such as Tailscale or WireGuard), add it on your Mac with Machines › Add a machine…, its VPN address (100.x.y.z:8825) and the code from codemany pair.

What it costs

An estimate, at on-demand prices in us-central1: e2-standard-4 with a 100 GB balanced disk is about $110 a month. An n2-standard-4 (for VM-per-job) is about $125 to $155 a month, depending on sustained-use discounts. Prices change and vary by region: check the Google Cloud pricing calculator. This is Google's bill, separate from Codemany's.

Stop or delete it

A stopped VM costs nothing for its CPU and memory, but its disk is still billed:

gcloud compute instances stop codemany-1 --zone=us-central1-a
gcloud compute instances start codemany-1 --zone=us-central1-a

Delete it to stop all charges. First free its machine slot: in its dashboard, Settings › License › Deactivate this machine… (or remove it from another machine's Settings › License later):

gcloud compute instances delete codemany-1 --zone=us-central1-a

AWS and others

Any provider with Ubuntu 24.04 works the same way: create the VM, keep 8825 closed, install Codemany, and tunnel with ssh -L 18825:127.0.0.1:8825. On AWS, a t3.xlarge or m7i.xlarge with 100 GB of gp3 is similar to the machine above, and the security group should allow only SSH. VM-per-job needs nested virtualization or a bare-metal instance: check your provider offers /dev/kvm before choosing that mode.

Pricing

One subscription per GitHub account or organization that owns the repos. $20/month includes 2 machines (Mac or Linux), and each extra machine is $10/month. A Mac and a mini PC fit in the $20. 14 days free, no card. See Pricing.

Troubleshooting

Is it healthy? codemany doctor checks the service, the dashboard, the licence, GitHub, the runners, KVM, Avahi and lingering, one PASS, WARN or FAIL line each, and exits non-zero on a FAIL. codemany status shows the runners.

What can jobs use? codemany doctor also lists the tools runner jobs get on this machine (git, Docker, compilers and so on) and the labels they add. codemany capacity says how many runners fit, and why.

Logs. journalctl --user -u codemany -f shows Codemany's log. The full log, with the runners' own output, is in files: tail -f ~/.codemany/logs/codemany.err.log ~/.codemany/logs/codemany.out.log.

It stops when I log out. Lingering is off (codemany doctor says so). Turn it on: sudo loginctl enable-linger "$USER".

codemany setup-link says "no daemon is waiting for setup". The service isn't running: start it with codemany install-service. If it says "already set up", open the dashboard instead.

Port 8825 is taken. If another user's Codemany on the same machine holds 8825, Codemany takes the next free port from 8826 to 8835, and codemany status and codemany setup-link find it. Tunnel to that port instead (codemany setup-link --port 18825 prints the link for your end). Any other program on 8825 must move: sudo ss -ltnp 'sport = :8825' shows which.

Jobs wait in queued. The workflow's runs-on labels must all be on the runner: self-hosted, Linux and codemany (or your own pool label).

VM-per-job won't start. codemany isolation vm-image says why (no /dev/kvm, not in the kvm group, no nested virtualization).