Home / Guides / Troubleshooting / Apple

Raising the Metal VRAM ceiling on Apple Silicon

macOS

Written by Jakub Rusinowski · Last updated September 1, 2026

Founder, LLM Configurator — AI educator & workshop leader on local LLM deployment

The error

ggml_metal_init: recommendedMaxWorkingSetSize  = 98304.00 MB
ggml_metal_init: skipping kernel_... (not supported)
ggml_backend_metal_buffer_type_alloc_buffer: error: failed to allocate buffer, size = 42000.00 MiB

Which one is it?

If you seeThe cause isGo to
recommendedMaxWorkingSetSize is roughly 75% of your installed memoryThat is the macOS default split — nothing is wrong yetFix 2: raise it if you have headroom to spare
A Metal buffer allocation fails just above that figureThe model wants more than the working-set ceiling permitsFix 2, then confirm with Fix 3
Memory Pressure is already yellow or redYou are short of memory, not short of ceilingThis is the swapping page, not this one
sysctl iogpu.wired_limit_mb says "unknown oid"Your macOS version uses the older debug.iogpu.wired_limit keyFix 1: find which key your system accepts
You set it, rebooted, and it revertedsysctl set at the command line does not persistFix 4: make it persistent deliberately

When you see it

You have plenty of memory and the loader disagrees. The line that gives it away is recommendedMaxWorkingSetSize in llama.cpp or Ollama's startup output, reporting a ceiling well below what the machine actually has installed.

What's actually going on

Metal advertises a recommended maximum working-set size — the amount of unified memory it considers safe to hand the GPU — and llama.cpp, Ollama and most Metal-based runtimes treat that figure as a hard ceiling. macOS sets it conservatively, commonly around 75% of installed memory, reserving the rest for the system. On a 128 GB machine that means roughly 96 GB for the GPU, which is a lot in absolute terms and can still be just short of what a large model plus its KV cache wants. The iogpu.wired_limit_mb sysctl changes that split. It is an undocumented tunable: Apple does not publish it, its name has already changed once between macOS versions, and it can change again in any release.

How to fix it

1. Find out which key your macOS version accepts, and what it is set to

Two keys have carried this setting. Current systems use iogpu.wired_limit_mb; older macOS versions used debug.iogpu.wired_limit. Reading them tells you which one exists on your machine — and this matters, because setting a key your system does not have appears to succeed and does nothing at all, which is how people conclude the tunable is a myth. A value of 0 means "use the system default", not "no limit".

zsh
# zsh — which key exists here?
sysctl iogpu.wired_limit_mb 2>/dev/null || echo "iogpu.wired_limit_mb: not present"
sysctl debug.iogpu.wired_limit 2>/dev/null || echo "debug.iogpu.wired_limit: not present"

# Total installed memory, in MB
sysctl -n hw.memsize | awk '{printf "%d MB installed\n", $1/1048576}'
Did it work? Exactly one of the two keys returns a value. That is the key to use everywhere below — substitute it if yours is the older one.

2. Raise the limit, in megabytes, leaving real headroom for macOS

The value is in megabytes: 60 GB is 61440, 96 GB is 98304. Set it with sysctl and it takes effect immediately with no reboot. How much to leave is a judgement, and this table is our recommendation, not an Apple-documented value — the upstream guidance is simply that going near 100% is where things go badly wrong, with about 70% of physical RAM commonly cited as the safe ceiling. On a 32 GB Mac leave at least 8 GB for the system (set ~24576). On 64 GB, leave at least 10 GB (set ~55296). On 128 GB, leave at least 16 GB (set ~114688).

⚠️ Do not set this near 100% of installed memory. macOS needs memory it cannot page out, and starving it produces kernel panics and forced reboots — you lose whatever was unsaved, and the machine can panic again during recovery. Leave the headroom above.

zsh
# zsh — 60 GB ceiling on a 64 GB Mac; takes effect immediately, lost on reboot
sudo sysctl iogpu.wired_limit_mb=55296

# Read it back
sysctl iogpu.wired_limit_mb
Did it work? The sysctl reads back your value, and the loader now reports a higher recommendedMaxWorkingSetSize.
sysctl iogpu.wired_limit_mb

3. Confirm the runtime actually sees the new ceiling

Setting the sysctl is not the same as the runtime honouring it, and this is the check that closes the loop. Start a model and read the ggml_metal_init: recommendedMaxWorkingSetSize line in the loader output. If that number has not moved, either you set the key your system does not use, or the runtime was already running and cached the old value — restart it.

zsh
# zsh — llama.cpp prints it on every load
./llama-cli -m model.gguf -ngl 999 2>&1 | grep -i recommendedMax

# Ollama: restart the app, then read its log
log show --last 5m --predicate 'process CONTAINS "ollama"' 2>/dev/null | grep -i recommendedMax
Did it work? The reported working-set size reflects your new value. If it does not, nothing downstream has changed regardless of what sysctl reports.
./llama-cli -m model.gguf -ngl 999 2>&1 | grep -i recommendedMax

4. Make it persistent — or deliberately do not

A sysctl set at the command line is lost on reboot. Adding the key to /etc/sysctl.conf makes it permanent. There is a reasonable argument for not doing that: a value that suits a long inference session is not one you want in force while you are editing video or running a VM, and a per-session sudo sysctl keeps the change scoped to when you actually need it. If you do persist it, err generously on the headroom, because a bad value now applies to every boot.

⚠️ A too-aggressive persistent value applies at every boot, including the boot you use to fix it. Test with a live sysctl first and only persist a value you have run under load for a while.

zsh
# zsh — persist across reboots
echo "iogpu.wired_limit_mb=55296" | sudo tee -a /etc/sysctl.conf

# Or revert to the system default at any time (0 = default, not unlimited)
sudo sysctl iogpu.wired_limit_mb=0
Did it work? After a reboot, the sysctl reads your persisted value rather than the default.
sysctl iogpu.wired_limit_mb

5. Watch for the symptoms of having gone too far

Upstream's warning is blunt — things go seriously wrong if you do not leave enough space — and it is worth knowing what "seriously wrong" looks like so you recognise it as your own doing. The progression is: the system becomes sluggish under load; the window server stutters and applications are slow to redraw; then a kernel panic and a forced reboot, losing unsaved work. If you see any of the first two after raising the limit, lower it rather than pressing on.

zsh
# zsh — did the machine panic since you changed this?
ls -lt /Library/Logs/DiagnosticReports/*.panic 2>/dev/null | head -3

# Back off to the default while you reconsider
sudo sysctl iogpu.wired_limit_mb=0
Did it work? No new panic reports after a full session under load. If there are, the value is too high for this machine and workload.
ls -lt /Library/Logs/DiagnosticReports/*.panic 2>/dev/null | head -3

6. Remember this does not create memory Most common fix

The most important sentence on this page: raising the limit changes the split, it does not add memory. If a model needs more than your Mac has, no value of this sysctl will help, and pushing the ceiling up on a machine that is already short simply moves the pain from a Metal allocation failure to swapping or a panic. It is also not a substitute for buying enough memory — on a 16 GB Mac the honest answer is a smaller model. Check what actually fits first; raise the ceiling only when the arithmetic says you are genuinely just short.

zsh
# zsh — installed, current ceiling, and what is left for macOS
sysctl -n hw.memsize | awk '{printf "installed: %d MB\n", $1/1048576}'
sysctl -n iogpu.wired_limit_mb 2>/dev/null | awk '{printf "gpu ceiling: %s MB\n", $1}'
Check what fits your hardware — see whether a larger model genuinely fits before you raise the ceiling
Open the VRAM checker →

If none of this worked

If Memory Pressure was already red before you touched any of this, the ceiling was never the constraint and the swapping page is the right one. If Metal is not being used at all, no ceiling change will help.

Include this when you report it

Related

A model that fits most setups:
View model & requirements →

Frequently asked questions

What does iogpu.wired_limit_mb actually change?

How much of unified memory Metal will let the GPU wire down, which runtimes read as recommendedMaxWorkingSetSize and treat as a hard ceiling. It redistributes existing memory between GPU and system; it does not add any.

How much should I leave for macOS?

Our recommendation is at least 8 GB on a 32 GB Mac, 10 GB on 64 GB and 16 GB on 128 GB. Upstream guidance is looser — do not go near 100%, with roughly 70% of physical RAM commonly cited as safe. These are judgements, not Apple-documented values.

Does the setting survive a reboot?

No. A command-line sysctl is lost at reboot. Adding the key to /etc/sysctl.conf persists it. Many people prefer not to, so an aggressive value is only in force during the sessions where they actually need it.

My sysctl says "unknown oid". What now?

Your macOS version uses the older debug.iogpu.wired_limit key instead. This matters because setting a key your system does not have looks like it worked and changes nothing — always read the value back and confirm the loader's reported working-set size moved.

What happens if I set it too high?

The system becomes sluggish, the window server stutters, and eventually the machine kernel panics and reboots, losing unsaved work. macOS needs memory it cannot page out. If you see the first two symptoms after raising the limit, lower it.