zsh-startcache
May 3, 2026 · View on GitHub
Fast zsh startup via time-based caching. One plugin that replaces both evalcache and the slow compinit rebuild cycle.
The Problem
Two things make zsh startup slow:
-
eval "$(tool init zsh)"— Tools likemise,rbenv,pyenv,nvm,direnv, andstarshipneed shell initialization. Eachevalspawns a subprocess and costs 10-50ms. -
compinitrebuilds every session — The completion system checks whether its cache (zcompdump) is stale by comparing the current$fpathas a string. If anything changes — a new completion file, a reordered path, a duplicate entry from tmux re-sourcing.zshrc— it nukes the cache and does a full rebuild (15-30ms).
Together these can add 100-300ms to every new shell.
The Fix
zsh-startcache uses time-based staleness (default: 24 hours) for both:
_startcache_eval— Caches command output to a file. Sources the file on subsequent shells. Only re-runs the command when the cache is older than the TTL._startcache_compinit— Callscompinit -C(skip all checks) when the zcompdump exists and is fresh. Only does a full rebuild when the dump is missing or stale. Also appliestypeset -U fpathto prevent duplicate-induced invalidation.
Installation
With a plugin manager
# zinit
zinit light rndjams/zsh-startcache
# antidote
antidote bundle rndjams/zsh-startcache
# sheldon
sheldon add zsh-startcache --github rndjams/zsh-startcache
# oh-my-zsh (as custom plugin)
git clone https://github.com/rndjams/zsh-startcache ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-startcache
# then add zsh-startcache to your plugins=(...) list
Manual
git clone https://github.com/rndjams/zsh-startcache ~/.zsh-startcache
echo 'source ~/.zsh-startcache/zsh-startcache.plugin.zsh' >> ~/.zshrc
Usage
With Oh My Zsh (or any framework)
Source the plugin before your framework. It intercepts compinit automatically:
# .zshrc
source ~/.zsh-startcache/zsh-startcache.plugin.zsh # ← before OMZ
export ZSH="$HOME/.oh-my-zsh"
plugins=(git fzf kubectl ...)
source $ZSH/oh-my-zsh.sh # compinit is intercepted, deferred, and cached
# Replace slow evals:
_startcache_eval mise activate zsh
_startcache_eval starship init zsh
_startcache_eval direnv hook zsh
That's it. No configuration needed. The plugin:
- Wraps
compinitbefore your framework calls it - Queues any
compdefcalls from plugins - Runs the real
compinitwith time-based caching at first prompt - Replays queued
compdefcalls
Without a framework
source ~/.zsh-startcache/zsh-startcache.plugin.zsh
# Your fpath additions...
fpath=(~/.zsh/completions $fpath)
# Cached evals:
_startcache_eval mise activate zsh
_startcache_eval starship init zsh
# compinit runs automatically at first prompt, or call manually:
# _startcache_compinit
Caching eval initializations
Replace slow eval calls:
# Before (runs every shell, 20-50ms each):
eval "$(mise activate zsh)"
eval "$(starship init zsh)"
eval "$(direnv hook zsh)"
# After (cached, <1ms on subsequent shells):
_startcache_eval mise activate zsh
_startcache_eval starship init zsh
_startcache_eval direnv hook zsh
Clearing the cache
After installing new tools or completions:
_startcache_clear
Configuration
| Variable | Default | Description |
|---|---|---|
ZSH_STARTCACHE_DIR | ~/.cache/zsh-startcache | Cache directory |
ZSH_STARTCACHE_TTL | 24 | Cache lifetime in hours |
Set these before sourcing the plugin:
export ZSH_STARTCACHE_TTL=48 # rebuild every 2 days
source ~/.zsh-startcache/zsh-startcache.plugin.zsh
How it works
When sourced, the plugin immediately:
- Applies
typeset -U fpath— prevents duplicate entries from causing cache invalidation - Wraps
compinitwith a no-op interceptor — frameworks call it, but nothing happens yet - Wraps
compdefwith a queue — plugin registrations are captured for later - Registers a
precmdhook — at first prompt (after.zshrcis fully loaded and fpath is complete), runs the realcompinitwith time-based caching and replays all queuedcompdefcalls
This means it works transparently with Oh My Zsh, Prezto, or any framework that calls compinit internally. No patching, no special flags.
Benchmarks
Measured on Apple M1 Pro, zsh 5.9, stock Oh My Zsh with 4 plugins (git, fzf, kubectl, history), 4 eval tools (brew, mise, direnv, starship).
The real-world scenario: tmux/screen users with fpath drift
| Configuration | Mean startup |
|---|---|
| Stock OMZ, fpath drift (compinit rebuilds every shell) | 624ms |
| Stock OMZ + startcache (immune to drift) | 338ms |
| Savings | 286ms (46% faster) |
Fpath drift happens when tmux panes, screen windows, or new terminal tabs cause $fpath ordering to change between sessions. Stock OMZ detects this as a change and triggers a full compinit rebuild every time.
Component breakdown
| Component | Cost |
|---|---|
| zsh binary startup (floor) | 19ms |
compinit -C (cached, no checks) | 44ms |
compinit full rebuild | 78ms |
| OMZ framework (4 plugins, no evals) | 303ms |
| 4 raw evals (subprocess spawns) | 209ms |
| 4 cached evals (source from file) | 168ms |
Full configurations
| Configuration | Mean | vs stock |
|---|---|---|
| Stock OMZ + raw evals (warm cache) | 434ms | baseline |
| Stock OMZ + raw evals (cold/drift) | 624ms | +44% |
| Stock OMZ + startcache | 338ms | -22% |
| No framework + startcache | 231ms | -47% |
Run your own benchmarks
See bench.sh for a self-contained script that measures your specific setup. Install hyperfine, then:
./bench.sh
Credits
- ctechols — Original "compinit once a day" gist
- mroth/evalcache — Inspiration for the eval caching approach
- mattmc3/ez-compinit — Prior art on compinit management
Bash
Honest note: Benchmarking shows startcache.bash adds ~40ms overhead vs raw evals in bash (253ms cached vs 214ms raw). Bash's
sourceis slower than zsh's, and subprocess spawns are faster in bash — so the caching tradeoff doesn't pay off the same way.The file is here if you want to experiment, but for most bash users, raw evals are already fast enough. The compinit problem that makes startcache essential for zsh doesn't exist in bash.
If you still want it (e.g., you have very slow tool inits or are on a constrained system):
# Add to .bashrc:
source /path/to/startcache.bash
_startcache_eval brew shellenv
_startcache_eval mise activate bash
_startcache_eval direnv hook bash
_startcache_eval starship init bash
Install:
curl -o ~/.startcache.bash https://raw.githubusercontent.com/rndjams/zsh-startcache/main/startcache.bash
echo 'source ~/.startcache.bash' >> ~/.bashrc
License
MIT