I spend most of my working hours in the terminal. Over the years, my setup has gone through many iterations — from the default Bash on Ubuntu, to iTerm2 with Oh My Zsh, to a heavily customized Kitty setup. Each time, I thought I’d found the endgame. I never had.
This post documents my current terminal setup as of early 2026, running on Fedora 43. It’s fast, minimal, and — most importantly — stays out of my way.
Ghostty #
Ghostty is my terminal emulator. Built by Mitchell Hashimoto (the creator of Vagrant, Terraform, and many others at HashiCorp), Ghostty is GPU-accelerated, written in Zig, and genuinely feels like it was designed by someone who uses a terminal 12 hours a day.
What sold me on Ghostty over alternatives like Kitty or Alacritty:
- Native splits and tabs — no need for tmux just to have panes (though I still use tmux for sessions).
- Fast — rendering is buttery smooth even with large scrollback.
- Sane defaults — most things work well out of the box. The config file is minimal.
- Native GTK on Linux — it feels like a proper GNOME app, not an electron wrapper.
My config is straightforward:
# Font
font-family = "JetBrainsMono Nerd Font"
font-size = 12.5
# Theme
theme = "TokyoNight"
window-theme = "ghostty"
# Padding
window-padding-x = 12
window-padding-y = 8
window-padding-balance = true
# Shell
command = "/usr/bin/zsh"
# Cursor
cursor-style = "block"
cursor-style-blink = true
# Scrollback
scrollback-limit = 1000000
# Misc
mouse-hide-while-typing = true
copy-on-select = true
unfocused-split-opacity = 0.85The TokyoNight theme runs consistently across Ghostty, tmux, and my editor — having a unified color scheme across tools removes a surprising amount of visual friction.
For keybindings, I use Ghostty’s built-in splits alongside tmux. Ghostty splits (Ctrl+Super+R/D) for quick side-by-side work, tmux sessions for persistent workspaces that survive terminal restarts:
# Splits
keybind = "ctrl+super+r=new_split:right"
keybind = "ctrl+super+d=new_split:down"
keybind = "ctrl+super+z=toggle_split_zoom"
# Split navigation
keybind = "alt+shift+left=goto_split:left"
keybind = "alt+shift+right=goto_split:right"
keybind = "alt+shift+up=goto_split:up"
keybind = "alt+shift+down=goto_split:down"
# Tab navigation
keybind = alt+1=goto_tab:1
keybind = alt+2=goto_tab:2
keybind = alt+3=goto_tab:3Zsh + Zinit #
I use Zsh as my shell, managed with Zinit as the plugin manager. I moved away from Oh My Zsh as a framework a while ago — it’s great for getting started, but loading the full framework adds noticeable startup latency. With Zinit, I cherry-pick only the OMZ snippets I actually need.
My plugin setup:
# Plugin manager
source "${ZINIT_HOME}/zinit.zsh"
# Completions (load before compinit)
zinit light zsh-users/zsh-completions
# Oh My Zsh snippets — only what I use
zinit snippet OMZP::git
zinit snippet OMZP::sudo
zinit snippet OMZP::docker
zinit snippet OMZP::docker-compose
zinit snippet OMZP::node
zinit snippet OMZP::python
zinit snippet OMZP::command-not-found
# Initialize completion
autoload -Uz compinit && compinit
# fzf-tab (must be after compinit)
zinit light Aloxaf/fzf-tab
# Autosuggestions, then syntax highlighting (order matters)
zinit light zsh-users/zsh-autosuggestions
zinit light zsh-users/zsh-syntax-highlightingThe three plugins that make the biggest difference in daily usage:
- zsh-autosuggestions — suggests commands as you type based on history. Accept with right arrow. Once you’re used to this, you can’t go back.
- zsh-syntax-highlighting — colors valid commands green and invalid ones red as you type, before hitting enter. Catches typos instantly.
- fzf-tab — replaces the default tab completion with a fuzzy finder. Combined with fzf-preview, it shows directory contents while you’re navigating.
# fzf-tab: preview directories while completing
zstyle ':fzf-tab:complete:cd:*' fzf-preview 'ls --color $realpath'
zstyle ':fzf-tab:complete:__zoxide_z:*' fzf-preview 'ls --color $realpath'History is configured to deduplicate and share across sessions:
HISTSIZE=5000
HISTFILE="$HOME/.zsh_history"
SAVEHIST=$HISTSIZE
setopt appendhistory sharehistory
setopt hist_ignore_all_dups hist_save_no_dupsStarship Prompt #
Starship is a cross-shell prompt written in Rust. It’s fast (sub-millisecond rendering), configurable, and shows contextual information based on the current directory — git branch, language versions, Docker context, command duration.
My prompt format keeps the left side clean (directory + git) and pushes language/tool indicators to the right using a fill:
format = """
$directory\
$git_branch\
$git_status\
$fill\
$python$nodejs$golang$rust$docker_context\
$cmd_duration\
$line_break\
$character"""
add_newline = false
palette = 'nord'I use the Nord color palette for the prompt, which pairs well with the TokyoNight terminal theme — both are cool-toned and low-contrast enough to not be distracting.
A few tweaks that matter:
[directory]
truncation_length = 3
truncation_symbol = '…/'
truncate_to_repo = false
[cmd_duration]
min_time = 500
style = 'fg:gray'The cmd_duration module shows elapsed time for any command that takes longer than 500ms. Useful for noticing when a build or test run took longer than expected.
tmux #
tmux handles session persistence. If my terminal crashes or I close the window, my sessions survive. I can also detach from a session on my desktop and reattach from SSH on my laptop — the session state is preserved.
I rebind the prefix to Ctrl+Space instead of the default Ctrl+B:
unbind C-b
set -g prefix C-Space
bind C-Space send-prefixPane navigation is bound to Alt+Arrows (no prefix needed), and new splits inherit the current working directory:
# Pane navigation without prefix
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# Keep cwd for new panes/windows
bind '"' split-window -v -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"The Tokyo Night tmux theme keeps the status bar consistent with the rest of the setup. I disable the clock and git indicator to keep it minimal — Starship already handles those.
CLI Tools #
These are the tools that replaced standard Unix utilities in my workflow:
eza #
eza replaces ls. Git-aware, icons, tree view, and color-coded output. I alias ls to it globally:
alias ls='eza --icons'
alias ll='eza -l --icons'
alias la='eza -la --icons'
alias lt='eza --tree --icons --level=2'
alias lg='eza -l --git --icons'The lg alias is particularly useful — it shows git status per-file inline with the listing.
bat #
bat replaces cat. Syntax highlighting, line numbers, git diff integration. Aliased to cat because there’s zero reason to use plain cat for reading files.
alias cat='bat'fzf #
fzf is a fuzzy finder that integrates with everything — shell history (Ctrl+R), file search (Ctrl+T), directory navigation, and tab completion via fzf-tab. It’s one of those tools that once you internalize the keybindings, you wonder how you ever lived without it.
zoxide #
zoxide replaces cd. It tracks the directories you visit and lets you jump to them with partial matches. Instead of typing cd ~/workspace/projects/my-app, I just type cd my-app and zoxide figures out the rest.
# Replaces cd with zoxide's smart jump
eval "$(zoxide init --cmd cd zsh)"uv #
uv is a Python package manager written in Rust. It’s absurdly fast — installing packages feels instant compared to pip. I use it for all Python work now.
The Full Picture #
Here’s how everything fits together:
Ghostty (GPU-accelerated terminal)
└─ Zsh (shell)
├─ Zinit (plugin manager)
│ ├─ zsh-autosuggestions
│ ├─ zsh-syntax-highlighting
│ └─ fzf-tab
├─ Starship (prompt)
├─ tmux (session management)
└─ CLI tools
├─ eza (ls)
├─ bat (cat)
├─ fzf (fuzzy finder)
├─ zoxide (cd)
└─ uv (python packages)The entire setup initializes in under 100ms. Zsh startup is fast because Zinit lazy-loads most plugins, Starship renders the prompt in single-digit milliseconds, and Ghostty’s GPU rendering means there’s never a visible lag between keypress and output.
What I’d Change #
No setup is perfect. A few things I’m still iterating on:
- Neovim integration — I use VS Code as my primary editor, but I keep meaning to build out a proper Neovim config for remote editing over SSH where VS Code feels heavy.
- Dotfiles management — My configs are currently just files on disk. I should probably set up a proper dotfiles repo with symlink management via
stoworchezmoi. - Shell startup profiling — Zsh startup is fast enough, but I haven’t profiled it in a while. There might be low-hanging fruit I’m missing.
Wrap Up #
A terminal setup is deeply personal — what works for me might feel wrong to you. The key is to invest time in tools you use every day. Small optimizations in your terminal workflow compound over thousands of hours of use. A 2-second save on a command you run 50 times a day is nearly 10 hours saved per year.
If you’re still on the default terminal with default Bash, I’d encourage you to try just one thing from this post. Start with Starship or eza — they’re drop-in replacements that require zero commitment. Once you see the difference, you’ll naturally want to optimize more.
You can find my full tech stack here.
Thanks for reading!