cd /news/developer-tools/bringing-down-my-zsh-load-times-from… · home topics developer-tools article
[ARTICLE · art-31524] src=iam.mt ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Bringing down my ZSH load times from ~3.1s to ~230ms

A developer reduced ZSH shell load times from 3.1 seconds to 230 milliseconds by profiling and optimizing their configuration. Key changes included replacing nvm with fnm, lazy loading conda, hardcoding brew --prefix, and fixing fpath duplication that caused compinit to rebuild its cache on every session.

read4 min views1 publishedJun 17, 2026

This year, the amount of time I spend in the terminal has gone significantly up thanks to Claude Code.

Couple of things:

  • I have this insane tendency to close tabs as soon as I’m done with them
  • Over the year, my work computer ZSH dotfiles have gotten extremely bloated

I got to a point where I couldn’t tolerate how slow opening a new shell session got. Naturally, I decided to fix it.

First, I needed to truly understand how slow my zsh was.

I ran time

to get the exact load time of my zsh.

time zsh -il -c exit

-i

: interactive mode; needed to understand prompts, completions, aliases, etc.-l

: login mode; needed to load /etc/zprofile

and ~/.zprofile

, etc.) in addition to .zshrc

(I only use .zshrc

and a ~/shell_overrides.sh

file to load computer specific config)-c exit

: run the exit command immediately and quit

These give you a true load time. The result didn’t come as a surprise.

3.1 seconds load time. It’s a ridiculously high number. No wonder I hated opening a new terminal session so much. For reference, my personal MacBook Air loads my zsh session in ~150ms.

I wanted to get to that number. At least as close as I could.

Next, I wanted to profile the full zsh setup. If you know me, I’m a minimalist. I don’t have any fancy setup. So, I knew the slowness was caused by the programs I was using.

So, I ran a profile. The way you do that in your zsh is by using a the zprof

module.

I added the zmodload zsh/zprof

to the top of my .zshrc

and zprof

to the bottom of my .zshrc

.

zmodload

: a zsh command for optional zsh modules that aren’t loaded by default, since 99.999999% of times you don’t need ’em.zsh/zprof

: the profiling module; once loaded, it records the timestamps for every function call.zprof

: we need to call this at the bottom of .zshrc

to dump the report to the stdio.

Boom! The biggest culprits – eager conda and

. Between the two, they took up around 2.2s of load time.

nvm

I replaced nvm with fnm and started lazy

conda

.Next up, hardcoded brew --prefix

to the actual path on my computer to shave off another ~60ms.

The last one was a hairy one.

  1. fpath

is a zsh array variable that lists directories where zsh looks for autoloadable functions, most importantly, completion definition files. It’s the zsh equivalent of PATH

, but instead of finding executables, it finds function files.

  1. compinit

initializes zsh’s completion system, it’s what enables tab completion. Scans every directory in fpath

looking for completion definition files (files named like _git

, _npm

, _docker

, etc.). Builds a completion dump file (~/.zcompdump

) that indexes all those completions. On subsequent runs, loads from that cached dump instead of rescanning everything.

  1. If the cache is missed, it builds the cache and it can be expensive. I found that out the hard way.

In my case, every single time I opened a new terminal session brew

kept adding the /opt/homebrew/share/zsh/site-functions

to the fpath

and compinit

hashed the current fpath

and compared it to the hash stored in .zcompdump

. Because the hash didn’t match, it kept rebuilding it every time.

To fix this, I made the fpath

a unique array, meaning all the dupes would be dropped anytime they get added to it. typeset -U fpath

– this essentially solved my problem. This shaved another ~430ms off.

That’s it, thanks to Ghostty + the new session setup, my new sessions open in ~230ms, which is pretty good.

And, for the first time in over 14 years, I’ve upgraded my console prompt to something more nicer than the basic text. This is what it looks like:

Features #

Directory focus

The path displayed only highlights the current working directly for better focus.

Error code display

I added a feature to display the error code from the last run command.

Execution timer

If a command takes longer than 4 seconds, the time taken to run the command is displayed on the right side of the terminal (you can see the 6s

on in the screenshot).

The other features include the standard ones like git

branch and dirty status. That’s it. It’s still quite lightweight in terms of the features, but I like it that way.

Anyway, I had a real good time chasing these issues down and fixing them.

── more in #developer-tools 4 stories · sorted by recency
── more on @zsh 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/bringing-down-my-zsh…] indexed:0 read:4min 2026-06-17 ·