Laufen auf Mac mini / Cloud Mac Ollama, Claude Code und GitHub Actions parallel, ist das häufigste Problem nicht „zu langsam“, sondern Swap, ruckelnde CLI und langsamere CI.
Dieser Text ohne Tool-Installation; sondern warum bei drei parallelen AI-Workloads der Speicher wackelt, und wie Scheduling das verhindert.Gegenbeispiel, Speicherbudget, Schwellen-Scheduling, 30-Sekunden-Runbook und vollständiges Skript; ohne Wiederholung des 16GB-vs.-24GB-Benchmarks.
TL;DR
- Ollama dauerhaft + CI-burst → auf M4 häufig Swap (siehe Gegenbeispiel)
- Meist kein Mac-Upgrade, sondern Schichtplan für burst / interactive / background
- Vor CI
ollama stop— wichtigster und einfachster Schritt (30-Sekunden-Version)
Das Modell AI workload scheduling: in Cloud-Mac-Szenarien kommen die meisten Swap/OOM eher aus Scheduling als aus absolut fehlendem RAM.
Das eigentliche Problem: meist nicht zu wenig RAM, sondern keine Prioritäten
Symptome: „lokales Modell im Leerlauf + CI-Spitze + träger Coding-Agent“ — oft fälschlich „mehr RAM kaufen“.
In unseren Cloud-Mac-Szenarien stammen die meisten Swap/OOM eher aus Workload-Scheduling als aus absolut fehlendem RAM. Auf einem Mac mini existieren drei Lasttypen (nächster Abschnitt):
- Burst — GitHub Runner /
xcodebuild, bei push +4–8 GB - Interactive — Claude Code, IDE + Terminal + großes Repo
- Background — lokale Inference wie Ollama; geladenes 8B hält still 5–7 GB
Standardmäßig konkurrieren alle gleichberechtigt um Unified Memory — niemand „muss weichen“.Die Richtung: Prioritäten und Trigger definieren, Background-Inference von „nach Install dauerhaft“ zu entladbarer, von CI verdrängbarer Ressource machen.
Schlechtes Scheduling (auf M4 häufig, kein Einzelfall)
Kombination, die wir auf Cloud Mac in kleinen Teams wirklich gesehen haben — als „verbotenes Muster“ beim Onboarding:
Bad pattern · all three workloads default「full on」 qwen3:8b always loaded → 5–7 GB (background, zero calls) push triggers xcodebuild → +4–8 GB peak (burst) Claude Code indexing large repo → +2–3 GB (interactive) Observed (24GB M4 Mac mini) Swap 0 → 2.1 GB xcodebuild link stage latency +~40% Claude Code terminal noticeably sluggish
Auf M4 mit Unified Memory ist dauerhaftes 8B + CI-burst + interaktives Coding parallel sehr oft Swap.Der Wert des Gegenbeispiels: workload scheduling ist kein Nice-to-have, sondern Voraussetzung bei mehreren Lasten.
Drei Lastformen: burst / interactive / background
Scheduling nur nach der Uhr reicht nicht. Auf Cloud Mac unterscheiden sich die Speicherprofile stark:
| Form | Beispiel | Schicht | Speicherprofil | Priorität |
|---|---|---|---|---|
| Burst | xcodebuild, Link, Simulator |
L1 Runner | Spitze, unvorhersehbar | Höchste — Fact darf nicht scheitern |
| Interactive | Claude Code, IDE, SSH | L3 | mittel, Mensch am Gerät | Hoch — Inference über API, kein Großmodell |
| Background | Ollama Embedding, Log-Zusammenfassung | L2 | verzögerbar, entladbar | Niedrigste — muss burst weichen |
Kurz: L2 ist die einzige Schicht im Stack, die „vom Host geworfen“ werden darf — nicht weil sie unwichtig ist, sondern weil ihre Tasks meist asynchron und wiederholbar sind.
Speicherbudget: Anteil der drei Workloads
Ollama / Claude Code / GitHub Runner als Beispiel-Komponenten; Zahlen ausM4 Mac mini Messungen(Steady State, keine Compile-Spitzen):
| Komponente | Schicht | Typisch | Spitzen |
|---|---|---|---|
| macOS + System-Cache | L0 | 3–4 GB | relativ stabil |
| Claude Code Workspace | L3 | 1–3 GB | Inference über API, keine Modellgewichte |
| GitHub Runner job | L1 | 2–6 GB (steady) | Link-Phase +4–8 GB |
| Ollama · qwen3:8b | L2 | 5–7 GB | freigegeben mit ollama stop |
| Ollama · qwen3:14b | L2 | 9–13 GB | mit burst leicht 2GB+ Swap |
| Ollama · nomic-embed-text | L2 | 0.3–0.8 GB | leichtes background tagsüber dauerhaft |
Grobrechnung 24GB tagsüber „Coding + CI + 8B dauerhaft“ ≈ 17 GB — noch Puffer; Mit dauerhaftem 14B schnell über 22 GB.Die Budget-Tabelle beantwortet „geht es?“; Scheduling beantwortet „wer wann Speicher nutzt“.
Scheduling-Modell: von Zeitfenstern zu memory_pressure-Schwellen
Einstieg mit Tag/Nacht-Tabelle (Modus ② unten); operativ reifer: Scheduling nach Speicherdruck — ohne dass jemand „22 Uhr Batch“ erinnern muss:
AI Workload Scheduler · L2-Q03 recommended rules When memory_pressure enters warn / critical (or equivalent > ~70%): → auto ollama stop qwen3:8b / qwen3:14b When memory_pressure normal (< ~50%) and idle > 10 min: → auto preload nomic-embed-text (keepalive 10m) When CI event trigger (Runner job start): → force CI mode: stop all Ollama large models, priority L1 > L3 > L2 When CI job succeeds + memory drops: → async trigger L2 batch (log summary / embedding rebuild)
Zeitfenster sind Baseline; Schwellen-Scheduling ist Upgrade. Kleine Teams starten mit Runbook + stop vor CI; danach memory_pressure-Guard (§ Runbook).
Drei Basis-Scheduling-Modi
Grundstrategien zusammen mit Schwellen-Scheduling:
| Modus | Vorgehen | Passt für |
|---|---|---|
| ① Leicht parallel | tagsüber nur nomic-embed-text; 8B/14B on-demand |
16GB Mac mini, Fokus Coding |
| ② Zeitfenster | 09–18 Coding+CI / 22–06 Nacht-Batch | 24GB, geplante Embedding-/Log-Tasks |
| ③ CI löst Yield aus | vor Job ollama stop, danach asynchron L2 |
häufige pushes, hohe xcodebuild-Spitzen |
Empfohlen: ① + ③ für alle; ② als Nacht-Batch-Ergänzung; Schwellen-Guard als Netz.
Pipeline-Aufteilung: was auf L2, was auf L3
Vor dem Scheduling die Pipeline festlegen, sonst läuft Ollama leer (siehe L2-Q01 · typische Fehleinschätzung):
| Aufgabe | Schicht | Scheduling-Hinweis |
|---|---|---|
| Repo ändern, Patch erzeugen | L3 Claude Code(API) | kein lokales Großmodell |
| Build, Test, Archiv | L1 Runner | höchste burst-Priorität; L2 vor Spitze stoppen |
| CI-Fehlerlog-Zusammenfassung | L2 qwen3:8b |
nach Job asynchron oder Nacht-Batch |
| CodeGraph / RAG embedding | L2 nomic-embed-text |
tagsüber dauerhaft möglich (<1GB) |
Runner-Spitzen und CI-Versatz
L1 Runner liefert Fact — Inference ersetzt kein Build-Ergebnis. Minimale CI-Änderung:
# .github/workflows/ios.yml · self-hosted macOS runner - name: Enter CI mode — free memory for xcodebuild run: | ollama ps ollama stop qwen3:8b 2>/dev/null || true ollama stop qwen3:14b 2>/dev/null || true sleep 30 # wait for memory reclaim; do not start xcodebuild same second - name: Build run: xcodebuild ...
Gemessen:Auf 24GB M4 gibt ollama stop qwen3:8b freigibt in ~5–15 s 5–7 GB; bei bestehendem Swap dauert vollständige Rückgewinnung Minuten — Daher muss stop vor CI mindestens 30 Sekunden früher erfolgen.
Runbook: 30-Sekunden-Version und vollständiges Skript
30-Sekunden-Version (für die meisten reicht das)
Keine Zeit für das volle Skript? Diese drei Blöcke decken ~80 % ab:
① Vor CI (entscheidend) — in GitHub Actions oder Runner-Hook:
ollama stop qwen3:8b ollama stop qwen3:14b sleep 30
② Tagsüber — nur leichtes Embedding, keine großen Modellgewichte:
ollama run nomic-embed-text --keepalive 30m
③ Nacht — im Batch-Fenster 8B laden:
ollama run qwen3:8b # then run your log summary / embedding rebuild scripts
Vollversion (Produktions-Runbook)
Für LaunchAgent / cron / mehrere Umgebungen speichern als ~/bin/cloud-mac-stack-runbook.sh:
Vollständiges Runbook · Unterbefehle
day-start · ci-pre · ci-post · night-batch
#!/usr/bin/env bash # cloud-mac-stack-runbook.sh — L2-Q03 standard Runbook set -euo pipefail OLLAMA_HOST="${OLLAMA_HOST:-127.0.0.1:11434}" export OLLAMA_MAX_LOADED_MODELS=1 ensure_ollama() { curl -sf "http://${OLLAMA_HOST}/api/tags" >/dev/null || ollama serve & sleep 2 } ci_pre() { # before CI: force CI mode, L1 wins ollama ps || true ollama stop qwen3:8b 2>/dev/null || true ollama stop qwen3:14b 2>/dev/null || true sleep 30 } ci_post() { # after CI: restore light embedding (lowest background tier) ensure_ollama ollama run nomic-embed-text --keepalive 10m } day_start() { # login / boot: embedding only or stop all ensure_ollama ollama stop qwen3:8b 2>/dev/null || true ollama stop qwen3:14b 2>/dev/null || true ollama run nomic-embed-text --keepalive 30m } night_batch() { # night batch (cron: 0 22 * * *) ensure_ollama ollama run qwen3:8b --keepalive 6h # ./your-log-summary-or-embed-rebuild.sh } case "${1:-}" in day-start) day_start ;; ci-pre) ci_pre ;; ci-post) ci_post ;; night-batch) night_batch ;; *) echo "Usage: $0 {day-start|ci-pre|ci-post|night-batch}"; exit 1 ;; esac
Memory guard (cron alle 5 Minuten oder LaunchAgent empfohlen): bei hohem Speicherdruck große Modelle entladen — minimale Umsetzung von Schwellen-Scheduling.
#!/usr/bin/env bash # memory-guard.sh — memory_pressure threshold guard PRESSURE=$(memory_pressure 2>/dev/null | head -1 || true) if echo "$PRESSURE" | grep -qiE 'warn|critical|urgent'; then logger -t cloud-mac-stack "memory guard: stopping Ollama 8B/14B ($PRESSURE)" ollama stop qwen3:8b 2>/dev/null || true ollama stop qwen3:14b 2>/dev/null || true fi # optional: when pressure normal + no Runner job, restore embedding # if echo "$PRESSURE" | grep -qi 'normal'; then ... fi
Anbindung (Beispiele):
- GitHub Actions — erster Step
ci-pre, letzterci-post - LaunchAgent — bei Login
day-start; 22:00 cronnight-batch - cron —
*/5 * * * * /path/memory-guard.sh
16GB Mac mini planen
- 14B nicht dauerhaft; 8B nur in
night-batch - Tagsüber nur
day-start(Embedding oder alles stoppen) - Jede CI braucht
ci-pre, ohne Ausnahme - Wenn „Desktop + 8B + Claude Code“ dauerhaft parallel laufen soll → 24GB; Scheduling hebt keine Hardware-Grenze
16GB: tagsüber kein großes Modell, nachts ein Modell pro Task, vor CI immer ci-pre.24GB: tagsüber Embedding, 8B gegen CI versetzt, 14B nur nachts.
Entscheidungstabelle: welche Strategie
| Ihre Situation | Empfehlung |
|---|---|
| 24GB · wenige pushes · vor allem Claude Code | day-start + Embedding; kein Nacht-Batch |
| 24GB · tägliche CI · Log-Zusammenfassung | ci-pre/post + night-batch + Memory Guard |
| 16GB · lokales 8B nötig | nur night-batch; tagsüber Claude API |
| Ollama eine Woche ohne Aufruf | zuerst L2-Q01 — Pipeline definieren |
Serie · Cloud Mac AI Stack
L2-Q03 · Memory-Scheduling-Schicht — beantwortet nach außen „Swap auf Mac mini vermeiden“; intern Fortsetzung von L2-Q01 Private Inference-Schicht:
- L2-Q01 — Was Inference ist (Positionierung)
- L2-Q03 · dieser Text — Memory-Scheduling-Schicht (Scheduling auf einem Host)
- Geplant — Model-Pinning, Health-Check 11434, Ollama-Aufruf aus CI
- Downstream — L4 Context + MCP-Scheduling (morgen L4-Q03)
Kein Ollama-Tutorial und kein reiner CI/CD-Tuning-Text — sondern erste Schicht eines AI-Workload-Schedulers auf Apple Silicon.
Bezug zu veröffentlichten Artikeln
- L2-Q01 · Private Inference-Schicht — L2-Position; dieser Text ist die Scheduling-Fortsetzung.
- L2-Q02 · 16GB vs 24GB — Swap-Zahlen; dieser Text wiederholt den Benchmark nicht.
- L1-Q01 · Runner — höchste burst-Priorität.
- L3 · Claude Code — interaktiver Hauptpfad.
FAQ
Führen Ollama, Claude Code und GitHub Actions parallel auf dem Mac mini zu Swap?
Ja, wenn burst-, interactive- und background-Workloads ohne Priorität parallel laufen. Siehe § Das eigentliche Problem und Gegenbeispiel.
Muss Ollama dauerhaft laufen?
Nein. Behandeln Sie lokale Inference als schedulbare Ressource: tagsüber nur nomic-embed-text, große Modelle on-demand oder im Nacht-Batch.
Ollama vor CI-Builds stoppen?
Empfohlen. Siehe 30-Sekunden-Runbook · vor CI oder ci-pre in der Vollversion.
Claude Code und Ollama parallel?
Ja. Der Coding-Pfad nutzt die API; lokal konkurrieren Modellgewichte und CI-Spitzen. CI-vor-stop oder Zeitfenster reichen.
Ist OOM auf Cloud Mac ein RAM-Problem?
In unseren Szenarien stammen die meisten OOM eher aus Scheduling als aus absolut fehlendem RAM. Die 16GB-Grenze: L2-Q02 Messungen.
Zeitfenster oder memory_pressure-Schwellen?
Zuerst das 30-Sekunden-Runbook; danach memory-guard.sh (siehe vollständiges Runbook unten).
Worin unterscheidet sich dieser Text von L2-Q01?
Q01 klärt die Inference-Service-Position; Q03 (Memory-Scheduling-Schicht) erklärt, wie AI-Workloads auf derselben Maschine geplant werden.
Cloud Mac AI Stack · Vorschau morgen
Claude Code + MCP: GitHub, lokale Dateien und API in einer Kette
Wenn L2-Schichten stehen, folgt L4 Context: wie MCP GitHub, Repo-Dateien und APIs in den Claude-Code-Workflow einbindet.
Zum Blog · Cloud Mac AI Stack