Skip to main content
31.03.2026

Why I Switched from Zsh + P10k to Fish Shell (And Why You Should Too)

head-image

The Problem: 500ms Shell Startup

If you run zsh with Oh My Zsh and Powerlevel10k, you already know the feeling. You open a new terminal tab and wait. You switch tmux sessions and wait. You type faster than your shell can render.

The numbers tell the story:

# Zsh + Oh My Zsh + P10k
time zsh -i -c exit
# real 0m0.487s

# Fish shell (default config)
time fish -c exit
# real 0m0.038s

That is a 12x difference. Half a second versus 40 milliseconds.

Half a second sounds small until you multiply it by every new shell, every tmux session, every fzf picker that spawns a subshell. It adds up to minutes of dead time every day.

When Your Shell Cannot Keep Up With You

Here is a scenario that pushed me over the edge. I use tmux-sessionizer (or similar tools like tmux-session-wizard) to manage projects as separate tmux sessions via fzf.

My workflow was:

  1. Pick a session via fzf
  2. Type nvim and hit Enter
  3. Hit a keybinding to open file search
  4. Type the search query

Sounds fast. Except with Zsh, I would type all of that faster than the shell could process it. Then I would sit there for a full second watching my keystrokes replay in slow motion as everything loaded. The shell was the bottleneck, not me.

With Fish, that entire sequence happens instantly. Keystrokes arrive at their destination the moment I press them.

Autocomplete: From Sluggish to Instant

Zsh autocomplete trained me to press Tab multiple times. The completion engine was slow enough that my first Tab press seemed to do nothing, so I would mash Tab repeatedly.

After switching to Fish, that habit backfired. Fish autocomplete is so fast that each Tab press actually registers. I would end up with duplicate arguments from git add because completions resolved instantly and every extra Tab added another match.

# Fish: Tab completion is instant
git add <Tab>
# Immediately shows matching files

# Zsh: Tab completion has visible delay
git add <Tab>
# ... wait ...
# ... wait ...
# Results appear

This is not a benchmark game. It is a genuine workflow difference you feel in your hands.

The Migration Fear (And Why It Was Overblown)

The biggest reason I resisted Fish for years: it uses its own scripting language. Not POSIX-compatible. Not Bash. Fish syntax.

My mental objections:

  • "What about all my Oh My Zsh aliases?"
  • "I have custom functions that took years to build"
  • "I will have to relearn everything"

Here is what actually happened: I realized I only used aliases for Git and Kubernetes. That is it. Everything else was a handful of custom functions and one-liners.

I fed my .zshrc to Claude and had everything ported to Fish in 30 minutes. Every alias, every function, working.

# Fish aliases (abbreviations)
abbr -a g 'git'
abbr -a ga 'git add'
abbr -a gc 'git commit'
abbr -a gp 'git push'
abbr -a gst 'git status'
abbr -a k 'kubectl'
abbr -a kgp 'kubectl get pods'
abbr -a kns 'kubectl config set-context --current --namespace'

Fish abbr (abbreviations) are actually better than aliases. They expand inline so you can see and edit the full command before running it. You always know exactly what will execute.

Fish Scripting: Less Insane Than Bash

Yes, Fish has its own syntax. No, you do not need to learn much of it.

For DevOps daily work, you need:

  • Conditionals
  • Loops over files and strings
  • Variable expansion

That is about it. Here is the honest comparison:

# Bash: loop over files
for f in *.log; do
  echo "Processing $f"
  grep ERROR "$f" | wc -l
done
# Fish: loop over files
for f in *.log
  echo "Processing $f"
  grep ERROR "$f" | wc -l
end
# Bash: conditional
if [ -f /etc/kubernetes/admin.conf ]; then
  export KUBECONFIG=/etc/kubernetes/admin.conf
fi
# Fish: conditional
if test -f /etc/kubernetes/admin.conf
  set -gx KUBECONFIG /etc/kubernetes/admin.conf
end

The syntax is different, but it is not harder. Arguably it is more readable. And let us be honest: Bash syntax is not exactly a paragon of good language design either. We just got used to it.

What You Get for Free

Fish ships with features that Zsh needs plugins for:

Feature Zsh Fish
Syntax highlighting zsh-syntax-highlighting plugin Built-in
Autosuggestions zsh-autosuggestions plugin Built-in
Tab completions Basic + manual configs Built-in for 300+ commands
Web-based config None fish_config opens a browser UI
Man page completions None Auto-generated from man pages

No plugin manager. No framework. No .zshrc that sources 15 files. Fish works out of the box.

Prompt: Tide Instead of P10k

If you love the Powerlevel10k look, Tide gives you the same two-line prompt with segments, icons, and git info. It is built for Fish and just as configurable.

Or use Starship, which works across all shells. We covered this comparison in our Tide vs Starship post.

The Honest Tradeoffs

Fish is not perfect. Here is what you give up:

POSIX compatibility. You cannot paste Bash one-liners from Stack Overflow directly. Most work, but some need minor tweaks ($(cmd) becomes (cmd), export becomes set -gx).

Some niche plugins. The Zsh plugin ecosystem is larger. If you depend on something very specific, check if a Fish equivalent exists first.

Team scripts. If your team shares Bash scripts, keep Bash for scripts. Fish is for interactive use. You can (and should) still write scripts with #!/bin/bash.

Muscle memory. The first week is rough. You will type export instead of set -gx and $(...) instead of (...). After a week, the new syntax is automatic.

Quick Migration Checklist

1. Install Fish
   brew install fish  # macOS
   sudo apt install fish  # Ubuntu/Debian

2. Try it first (do NOT set as default yet)
   fish

3. Port your aliases
   # Copy your aliases from .zshrc
   # Convert to Fish abbreviations
   # Save in ~/.config/fish/config.fish

4. Install a prompt (optional)
   fisher install IlanCosman/tide  # Tide
   # or
   brew install starship  # Starship (cross-shell)

5. Test for a week

6. Set as default when ready
   chsh -s (which fish)

Performance Comparison Summary

Metric Zsh + OMZ + P10k Fish (default)
Shell startup ~500ms ~40ms
Tab completion Visible delay Instant
Syntax highlighting Plugin required Built-in
Autosuggestions Plugin required Built-in
Plugin count needed 5-10 typical 0-2 typical
Config complexity High (.zshrc, .p10k.zsh) Low (config.fish)

Bottom Line

The switch from Zsh to Fish is a net positive for anyone who values terminal speed. The migration cost is a few hours (less with an AI assistant). The daily payoff is a shell that never makes you wait.

If you are typing faster than your terminal can handle, Fish fixes that.


At Akmatori, our AI agents live inside terminals, running diagnostics and executing remediation commands. Shell performance matters when your agent is spawning hundreds of subshells during incident investigation. Fish helps keep that fast.

Automate incident response and prevent on-call burnout with AI-driven agents!