How to Fix Slow Zsh Startup Time

September 26, 2026 · 3 views
How to Fix Slow Zsh Startup Time

If opening a new terminal tab makes you wait a second or two before you can type anything, you're dealing with slow zsh startup time — and it's one of those problems developers tolerate for months before finally fixing it. The good news is that zsh startup time is almost always dominated by a small number of predictable culprits, and you can find and fix them in about fifteen minutes with the right diagnostic tool.

This guide walks through profiling your zsh startup time with zsh's built-in zprof module, identifying exactly which plugins or tools are slowing you down, and applying targeted fixes instead of guessing.

Why Zsh Startup Gets Slow

Every time you open a new shell, zsh reads and executes .zshenv, .zprofile, .zshrc, and .zlogin in sequence. On a fresh install this takes single-digit milliseconds. The slowdown creeps in as you add tools over time: an oh-my-zsh framework with a dozen plugins, a version manager like nvm or pyenv that shells out to initialize itself, a prompt theme that queries git status on every render, and zsh's own completion system rebuilding its cache more often than it needs to.

None of these are slow in isolation. The problem is that they add up, and because they all run synchronously during startup, a shell that should open instantly ends up blocking for 1-3 seconds while it waits on all of them.

Profiling Your Zsh Startup Time with zprof

Don't guess which plugin is the problem — measure it. Zsh ships with a profiler called zprof that gives you a sorted breakdown of exactly how long each function call during startup took.

Add this as the very first line of your ~/.zshrc:

zmodload zsh/zprof

And add this as the very last line:

zprof

Now open a fresh terminal (or run zsh in your current one) and you'll see a table like this:

num  calls  time (ms)   avg    %
1    1      412.33      412.33 38.2%   compinit
2    1      198.71      198.71 18.4%   nvm.sh
3    24     87.42       3.64   8.1%    omz_plugin_load

That output tells you exactly where the time is going. In this example, compinit and nvm sourcing account for over half of total startup time — both are common and both are fixable.

Common Causes and How to Fix Each One

1. compinit rebuilding its completion cache every launch

Zsh's completion system checks for new completion definitions on every shell start unless you tell it not to. Since your installed completions rarely change day to day, you can safely cache the check and skip it most of the time:

autoload -Uz compinit
if [[ -n "$HOME/.zcompdump"(#qN.mh+24) ]]; then
  compinit
else
  compinit -C
fi

This only runs the full, slow compinit if the cache file is missing or older than 24 hours — otherwise it loads the cached version with -C, which skips the security check and file scan entirely.

2. Version managers (nvm, rbenv, pyenv) sourcing eagerly

Tools like nvm are notorious for adding 100-300ms to every shell startup because they scan your Node installations immediately, even if you don't touch node or npm in that session. Lazy-load them instead so the cost only hits the first time you actually use the command:

nvm() {
  unset -f nvm
  export NVM_DIR="$HOME/.nvm"
  source "$NVM_DIR/nvm.sh"
  nvm "$@"
}

This defines a lightweight placeholder function named nvm that only sources the real, expensive nvm.sh script the first time you actually call nvm. Every startup after that until you use it stays fast.

3. Oh-my-zsh plugins loaded synchronously

Every plugin in your plugins=(...) array in .zshrc gets sourced in order, blocking startup until each one finishes. Audit that list — most developers accumulate plugins they installed once and never use. Trim it to what you actually rely on daily, and for anything you use occasionally, source it manually or via a keybinding instead of on every launch.

4. A prompt theme querying git on every render

Powerlevel10k and similar themes are fast by default, but if you're on an older prompt or a heavily customized one, check whether it shells out to git status synchronously. Powerlevel10k solves this with instant prompt — enabling it renders a cached prompt immediately and fills in live data (like git branch) asynchronously once it's ready:

# Near the very top of .zshrc, before Oh My Zsh sourcing
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
  source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi

Step-by-Step: Putting It Together

  1. Add zmodload zsh/zprof to the top and zprof to the bottom of .zshrc, then open a new shell to capture a baseline.
  2. Note the top 2-3 time consumers from the output.
  3. Apply the matching fix above for each one — cached compinit, lazy-loaded version managers, trimmed plugin list, or instant prompt.
  4. Remove the zprof lines and re-run the profile once more to confirm the improvement.

Most developers who go through this process take their startup time from 1-2 seconds down to under 100ms, which is the difference between a shell that feels instant and one that makes you wait every single time you open a tab.

Best Practices to Keep Startup Fast

  • Re-run the zprof profile any time you add a new plugin or tool — regressions creep back in quietly.
  • Prefer lazy-loading for any tool you don't use in every session (version managers, cloud CLIs, language-specific completions).
  • Keep your plugin list to what you actually use; an unused plugin costs load time with zero benefit.
  • Use compinit -C with a time-based cache check instead of running full completion initialization on every launch.

Frequently Asked Questions

Does switching from oh-my-zsh to a minimal framework actually help? It can, but the framework itself is rarely the real cost — the plugins and tools loaded through it are. Profiling with zprof first will tell you whether a framework switch is worth the migration effort or whether trimming plugins gets you the same result faster.

Will these fixes work the same way on macOS and Linux? Yes. zprof, compinit, and lazy-loading patterns are zsh features, not OS-specific behavior. The only difference is where tools like nvm install themselves, which doesn't change how you profile or lazy-load them.

Is bash faster than zsh by default? A bare bash shell with no configuration starts slightly faster than a bare zsh shell, but the difference is a few milliseconds and irrelevant in practice. The 1-3 second startup times developers complain about come from accumulated plugins and eager tool sourcing, not from zsh itself — the same bloat would slow down bash just as much.

Should I just disable compinit entirely? No — that removes tab completion for commands and arguments, which is one of the more useful features of an interactive shell. Caching it with the -C flag pattern above keeps the feature while removing the repeated cost.

Key Takeaways

Slow zsh startup time is almost never one single problem — it's a handful of synchronous, eagerly-loaded tools stacking up. Profile first with zprof instead of guessing, fix the specific top offenders it points to (usually compinit, a version manager, or an unused plugin list), and re-profile to confirm the gain. Do this once and a slow shell that costs you a second or two dozens of times a day becomes one that opens instantly every time.

#developer-productivity #zsh #terminal #shell #macos #dotfiles
Share this article:

0 Comments

No comments yet — be the first to share your thoughts.

Leave a comment

Never published.