Fixing Slow Shell Startup Time in Bash or Zsh
Opening a new terminal tab takes a visibly annoying second or two before you get a prompt. Here's how to actually find which specific line in your config is responsible, rather than guessing.
A shell that takes a noticeable moment to show a prompt after opening a new terminal is almost always caused by something specific and identifiable in your startup configuration — not the shell itself being slow.
Step 1: measure the actual startup time first
time zsh -i -c exit
time bash -i -c exit
Running this gives you a concrete baseline number to improve against, rather than relying on a subjective “it feels slow” impression — and lets you confirm afterward that a specific fix actually helped.
Step 2: profile Zsh startup specifically, if that’s your shell
zsh -xv -i -c exit 2>&1 | ts -i '%.s' | less
Running Zsh with -x (execution trace) and -v (verbose) while timestamping each line reveals exactly which specific config file lines take the most time between timestamps — pinpointing the actual slow operation rather than guessing at your entire .zshrc.
Step 3: profile Bash startup similarly
PS4='+ $(date "+%s.%N")\011 ' bash -x -i -c exit 2>&1 | less
This embeds a timestamp in Bash’s own execution trace output (PS4 controls the trace prefix), letting you spot large time gaps between consecutive traced lines the same way as the Zsh approach above.
Step 4: check for common slow culprits specifically
- nvm, rbenv, or similar version manager initialization
- an Oh My Zsh or framework loading many unused plugins
- a git-status-checking prompt segment run in a very large repo
- a slow network-dependent command (checking for updates, etc.)
Version managers and prompt frameworks are, by a wide margin, the most common source of genuinely slow shell startup — many of them execute non-trivial logic (querying installed versions, checking multiple directories) on every single new shell.
Step 5: lazy-load version managers instead of eagerly initializing them
# instead of eagerly running nvm's full init on every startup,
# wrap it so its actual initialization only happens the first
# time an nvm-related command is actually used
Most version managers’ documentation includes a “lazy loading” pattern specifically because eager initialization on every shell startup is such a well-known performance cost — deferring the actual expensive work until it’s genuinely needed removes that fixed cost from every single new shell.
Step 6: audit your prompt framework’s enabled plugins
# Oh My Zsh: check the plugins=(...) line in .zshrc
# disable anything you don't actually use day to day
Prompt frameworks often enable considerably more plugins by default, or through copied configuration, than any individual user actually uses — each enabled plugin adds some amount of startup cost regardless of whether its functionality gets used in a given session.
Step 7: check for a git-status-checking prompt segment as a specific suspect
If startup time (or, more precisely, prompt-display time when changing directories) is specifically slow inside large git repositories, a git-aware prompt segment evaluating repository status is a very common, specific, and fixable cause — most prompt frameworks offer a setting to skip or limit this check in very large repositories specifically.
Step 8: re-measure after each change to confirm actual improvement
time zsh -i -c exit
Re-running your baseline measurement after each specific change confirms it actually helped, rather than assuming an optimization worked based on a subjective impression alone.
Why profiling beats guessing here
Shell startup slowness has enough different possible specific causes (version managers, framework overhead, network calls, git status checks) that guessing and removing things at random wastes time and risks breaking configuration you actually wanted — the timestamped trace approach in Steps 2-3 identifies the actual specific slow line directly, turning a vague “it’s slow” complaint into a concrete, fixable target.