Ana SayfaBlogSelf-Hosted GitHub Actions Runner'i Mac Mini M4'te Calistirdim: 45 Gunluk Notlar
CI/CD & Automation

Self-Hosted GitHub Actions Runner'i Mac Mini M4'te Calistirdim: 45 Gunluk Notlar

Emre Ferit Aslantas25 Mayıs 202613 dkMakale
github-actions self-hosted-runner mac-mini-m4 ci-cd apple-silicon devops
Sponsored

GitHub Actions'in ucretsiz tier'ina (private repo'larda 2.000 dakika/ay) yetisemedigimi farkettigim gun, hesabima 14$'lik bir on-demand fatura cikmisti. Cok degil ama "ay sonunda gizli bir abonelik gibi" hissettiriyordu. Mono-repo dedigim sey: bir Python backend, bir Astro static site, bir Electron uygulamasi ve birkac shared library. Her PR'da matrix build alti ortami birden vuruyordu. Ucretsiz dakikalar ay ortasinda eriyordu.

Mac Mini M4 (24 GB) zaten 7/24 acik ve homelab'imda hem AI Lab K8s hem de yerel LLM inference cevirdigi icin "bos kapasite"si vardi. Self-hosted runner'i ciddi sekilde denedim. Bu yazi 45 gun sonra geriye bakip ne kazandigimi, neyi yarim biraktigimi anlattigim notlardir.

Neden GitHub-Hosted Yetmedi

Ucretsiz tier'da yasamayi denedigim ay icindeki kabaca dagilim soyleydi:

  • Python backend test matrix (3 surum) — PR basina ~6 dk x 3 = 18 dk
  • Astro site build + Lighthouse CI — PR basina ~4 dk
  • Electron build (mac + linux + windows) — release basina ~22 dk
  • Nightly tum repo integration — gece basina ~14 dk

Ortalama haftalik 8-10 PR. Hesabini yapmasi kolay: ayda 1.800-2.200 dk arasinda gidip geliyordum. Ya paths-ignore ile is sayisini azaltacaktim ya da on-demand'e gececektim. Iki kez yaptigim hesap, on-demand'in private repo Linux runner icin 0.008$/dk olduguydu — yani ayda 8-15$ arasi. Bir yilda 100-180$. Mac Mini zaten elektrik harciyor; ekstra is 5-10W eklerse aylik 5-8 TL'lik elektrik ile karsilanacak gibi geldi. Test ettim.

Kurulum: launchd ile Servis Yapma

GitHub Settings → Actions → Runners → New self-hosted runner ekraninda macOS / ARM64 sekmesini secince size hazir bir indirip-kos scripti veriyor. Onu calistirmak yetiyor, ama ./run.sh foreground'da koruken Terminal'i kapatinca runner duruyor. Bunu Apple'in launchd servisine vermek lazim.

Runner repo'sundaki actions-runner klasoru icindeki svc.sh install komutu, asagidakine yakin bir LaunchAgent plist'i ~/Library/LaunchAgents altina yaziyor:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>actions.runner.efa-mono.mac-mini-m4</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/efa/actions-runner/runsvc.sh</string>
  </array>
  <key>WorkingDirectory</key>
  <string>/Users/efa/actions-runner</string>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/Users/efa/actions-runner/_diag/stdout.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/efa/actions-runner/_diag/stderr.log</string>
  <key>ProcessType</key>
  <string>Interactive</string>
</dict>
</plist>

svc.sh start ile baslayinca runner GitHub'a "online" gorunuyor. Reboot sonrasi otomatik geliyor. Buraya kadar acisiz.

Asil sorun bir sonraki adimda baslar: workflow'lari runner'a yonlendirmek. Default runs-on: ubuntu-latest calismaz, etiket ile cagirmaniz lazim. Ben repo seviyesinde runner kayit ederken self-hosted, macOS, ARM64 etiketlerine bir de mac-mini-m4 ekledim. Workflow tarafinda:

# .github/workflows/backend-tests.yml
jobs:
  test:
    runs-on: [self-hosted, macOS, ARM64, mac-mini-m4]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Install deps
        run: |
          python -m venv .venv
          source .venv/bin/activate
          pip install -r requirements.txt
      - name: Run tests
        run: |
          source .venv/bin/activate
          pytest -x --tb=short

Onemli ayrinti: actions/setup-python self-hosted macOS runner'da varsayilan olarak RUNNER_TOOL_CACHE icine indirir, ama Python interpreter'ini her seferinde tekrar indirmek yerine pyenv ile pre-install edip PATH'e koymak daha hizli. Ilk seferinde fark etmemistim, ikinci hafta setup-python adimi 60 saniye tutunca farkettim.

Performans: Beklemediginim Bir Yer Hizlandi

Asagidaki sureleri 10 PR uzerinden ortalama aldim. Hem GitHub-hosted (ubuntu-latest) hem de Mac Mini self-hosted icin ayni commit'leri kosturdum.

| Job | GitHub-hosted | Mac Mini M4 self-hosted | |---|---|---| | Python backend test matrix (tek surum) | ~6 dk 10 sn | ~2 dk 40 sn | | Astro static build | ~3 dk 50 sn | ~1 dk 15 sn | | Lighthouse CI | ~2 dk | ~1 dk 30 sn | | Docker build + push (Buildx) | ~4 dk 20 sn | ~1 dk 50 sn | | npm ci (Astro project, ~280 deps) | ~55 sn | ~12 sn |

npm ci farkinin sebebi cok belirgin: GitHub-hosted'da her job temiz bir VM'de basliyor, ~/.npm cache'i workflow cache eylemiyle (yani tarball indirme + unpack) restore ediliyor. Self-hosted'da ayni ~/.npm zaten diskte. Acgozlu davranip her job'a actions/cache@v4 koydugumda bile, self-hosted'in HDD'siz NVMe'si tarball acmayi 4-5 kat hizli yapiyor.

Backend testlerindeki 2.3x'lik fark cogunlukla CPU farkindan geliyor. M4 performance core'lari, ubuntu-latest runner'in 2 vCPU paylasimli host'una karsi acik fark atiyor. pytest -xvs sirasinda Activity Monitor'de runner process'i ortalama 180-220% CPU goruyorum (yani 2 core'u doluya yakin kullaniyor).

Pratik Surtunmeler: 3 Sorun

1. Workspace Kirleniyor

Self-hosted runner her job'dan sonra _work/<repo> klasorunu temizlemiyor, sadece git pull ile guncelliyor. Ilk hafta sonu du -sh ~/actions-runner/_work calistirdigimda 11 GB gormustum. node_modules, .venv, build artifact'leri birikiyor. Cozum olarak workflow sonuna bir adim koydum:

- name: Clean workspace
  if: always()
  run: |
    rm -rf node_modules .venv dist build .pytest_cache

Bu yetersiz, cunku farkli branch'lerin checkout'lari da workspace'i sisirebiliyor. Daha temiz cozum: launchd plist'ine her gece 04:00'te git clean -xfdq calistiran bir LaunchAgent eklemek. Hafta sonu yaptim, problem du -sh ile 2-3 GB araliginda kaldi.

2. Secret Sizinti Riski

GitHub-hosted runner'da is bittiginde VM yokediliyor, secret'lar onunla beraber gidiyor. Self-hosted'da env: ile gecirdiginiz her sey runner process'inin env'inde yasiyor — paralel job aldigi an, teorik olarak baska bir job'in process tree'sinden okunabilir. Public repo'larda kesinlikle self-hosted runner kullanmayin (third-party PR'lar arbitrary code calistirir). Benim repo'lar private oldugu icin pratik risk dusuk ama secret'lari yine de Vault'tan job icinde cekiyorum, workflow secrets: blogundan degil:

- name: Fetch secrets from Vault
  run: |
    export VAULT_ADDR=https://vault.efa.local
    export VAULT_TOKEN=$(cat ~/.vault-token)
    vault kv get -format=json secret/efa-agent/ci > /tmp/secrets.json

Bu sekilde GitHub'in secret store'unda sadece VAULT_TOKEN duruyor, geri kalan her sey job basina, ihtiyac duydugu surece runner'in /tmp alaninda yasiyor.

3. Apple Silicon ARM64 Image'lar

Docker build sirasinda bazi resmi base image'larin (mcr.microsoft.com/playwright mesela) ARM64 varianti yok ya da geriden geliyor. docker build --platform linux/amd64 ile QEMU emulation acmak isimi gorur ama o build'in suresi 6-8 kat artiyor. Playwright icin sonunda kendi ARM64 base image'imi yaptim ve internal registry'mde tutuyorum.

Vazgectigim Bir Sey: Actions Runner Controller (ARC)

Ilk hafta sevince kapilip, runner'i K8s'e gomek istedim. Actions Runner Controller projesi, runner pod'lari otomatik olarak dogurup ya da ephemeral mod ile her job basina yeni pod aciyor. Docker Desktop K8s'imde denedim.

Iki gun ugrastiktan sonra durdum. Sebepler:

  • macOS runner'i K8s'e tasimak fiili olarak imkansiz — ARC Linux container'larini yonetiyor, M4'un Neural Engine ya da Metal avantajlarini kullanamiyor. Yani M4'un yarisini bos birakacaktim.
  • Linux ARM64 runner pod'lari Docker Desktop VM altinda calisirken Buildx ile multi-arch image build etmek ic ice nested virtualization gerektiriyor; benchmark'larda native runner'dan 3 kat yavasti.
  • ARC kurulumu icin GitHub App izinleri, webhook routing, ayri service account — homelab icin overkill.

Karar: tek bir bare-metal LaunchAgent runner. Eger ikinci runner gerekirse Proxmox'taki Intel NUC'a ARM64 olmayan ikinci bir runner kurarim. Simdilik ihtiyac yok.

Maliyet: Gercek Rakamlar

45 gun sonra:

  • GitHub Actions on-demand kalemi: 0$ (eski aylarda 8-14$ idi).
  • Mac Mini ek elektrik: aktif build sirasinda kabaca +25W. Gunde toplam ~45 dakika build aktivitesi gozlemledim. Ayda ekstra ~0.55 kWh, yani 1 TL'nin altinda.
  • Setup zamani: launchd + workflow guncellemeleri + ARC denemesi + temizlik adimlari icin toplam ~6 saat. Tek seferlik.
  • Bakim: simdiye kadar 2 kez. Birinde actions/checkout yeni surume gectiginde permission hatasi aldim, _work klasorunun sahipligini geri verdim. Digerinde GitHub runner agent kendisini auto-update etti ve plist log path'leri kufrettigim icin yeniden olusturmak zorunda kaldim.

Yillik tahmini tasarruf: 100-180$ + her PR'da 2-4 dakika daha kisa feedback dongusu.

Sonuc Notlari

  • Mono-repo + sik PR akisina sahip private projeler icin self-hosted runner anlamli. Ayda 1.500 dakikanin altinda kosuyorsaniz ucretsiz tier zaten yetiyor, ekstra DevOps yuku gereksiz.
  • Public repo veya org-wide runner istiyorsaniz self-hosted yapmayin ya da sandbox ephemeral runner kurun. Aksi halde supply chain saldirisi davetiyesi.
  • launchd + tek bare-metal runner, K8s/ARC karmasindan once iyi bir baslangic. Ihtiyac olunca olceklendirin.
  • Workspace temizligi ilk gunden plana koyun. Aksi halde 2 ay sonra "disk neden doldu" panigi yasarsiniz.
  • CPU sansli oldugunuz makinada sureler yari yariya inebilir; Apple Silicon native CI build'leri Linux x86 emulasyonundan dramatik sekilde hizli.

Bir sonraki yazida muhtemelen Vault'tan CI'ya nasil dinamik secret'lar verdigimi anlatacagim — Vault'un AppRole auth'unu GitHub OIDC ile birlestirmek ilk denemede acisiz olmadi ve oradan ogrendigim birkac sey var. Eger siz de self-hosted runner deniyorsaniz ve farkli bir tuzaga dustuyseniz, GitHub uzerinden bana yazin — ozellikle ARC'a yeniden donmeyi degerlendirmek isteyebilirim, dogru kullanim hikayesi gormeyi merak ederim.

Sponsored

Haftalık DevOps Bülteni

Yeni tool incelemeleri, karşılaştırmalar ve DevOps trendleri haftada bir kutuna gelsin.