My Terminal Setup, Explained
The Zsh, tmux, and Git configs I use daily. Annotated so you can understand what each block does and take what's useful.

People ask about my terminal setup more than almost anything else I write about. Usually after they see me working on a shared screen and notice something โ the auto-suggestions, or the way I jump between tmux panes, or the fuzzy finder popping up when I hit Ctrl+R.
I've tweaked this setup over a few years. Most of it stabilized about a year ago and I haven't changed much since. What I have now is the result of trying a lot of tools, discarding the ones that felt gimmicky or slowed me down, and keeping the ones I actually reach for daily.
I'll walk through the pieces I use and explain what they do. Steal whatever looks useful and ignore the rest.
Zsh Plugins
I run Zsh with Oh My Zsh, which is probably the most basic setup possible and I don't care. Some people give you a look when you say you use Oh My Zsh โ like you should be running a hand-curated framework-less Zsh config with manually compiled plugins. Maybe they're right. But OMZ works, the plugin system is simple, and I have better things to do with my time than optimize my shell bootstrapping.
Four plugins I'd reinstall first on a new machine:
plugins=(
git
z
zsh-autosuggestions
zsh-syntax-highlighting
)
git gives you about a hundred aliases for git commands. gst for git status, gco for git checkout, gp for git push. I don't use all of them โ maybe 10-15 regularly โ but the ones I use save enough keystrokes to be worth it. glog in particular (formatted git log with branch graph) is something I use all the time.
z is a directory jumper. After you've navigated to a directory a few times, z learns it and lets you jump there with a partial name. Instead of cd ~/projects/technoknowledge24/src/components, I type z compon and it takes me there. The matching is based on "frecency" โ a combination of how often and how recently you've visited the directory. It's surprisingly accurate. I'd guess it saves me 20-30 cd commands a day.
zsh-autosuggestions shows a ghost-text suggestion from your history as you type. If I start typing docker compose it fills in the rest of the command I ran most recently that started with those words. Press the right arrow key to accept it. This one took some getting used to โ the ghost text was distracting at first. But after a week I couldn't live without it.
zsh-syntax-highlighting colors your command green if it's a valid command and red if it's not, as you type. Catches typos before you hit Enter. Simple but effective.
Aliases
alias ..="cd .."
alias ...="cd ../.."
alias wipe="clear && printf '\e[3J'"
alias ports="lsof -i -P -n | grep LISTEN"
alias dev="npm run dev"
alias dcu="docker compose up -d"
alias dcd="docker compose down"
Nothing exciting here. ports is the one I use most after dev. When you get "address already in use" and you need to find what's hogging port 3000, ports shows you the answer immediately.
wipe is clear but it also flushes the scrollback buffer. Regular clear just moves your prompt to the top of the screen โ if you scroll up, all the previous output is still there. Sometimes I want a truly blank terminal. The printf '\e[3J' escape sequence handles that.
FZF (Fuzzy Finder)
FZF replaced the default Ctrl+R history search and Ctrl+T file picker for me. The built-in Zsh history search is functional but limited โ it only matches from the beginning of the command. FZF does fuzzy matching, so if you type "docker" it shows every command containing "docker" anywhere, ranked by relevance.
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border --color=dark'
export FZF_CTRL_T_OPTS="--preview 'bat --color=always --line-range=:100 {}'"
The --preview option on Ctrl+T shows a file preview (using bat for syntax highlighting) as you scroll through the matches. This is the feature that gets the "wait, what was that?" questions during screen shares. It looks flashy but it's genuinely practical โ when you're looking for a file and can't remember the exact name, seeing a preview of the content as you browse is faster than opening each candidate.
Replacing the Classic Unix Tools
I'm not a "rewrite everything in Rust" evangelist, but a few Rust-based CLI tools have replaced their GNU counterparts in my daily workflow because they're noticeably better.
ripgrep (rg) replaces grep. It's faster on large codebases โ not marginally faster, noticeably faster. But the real win is the defaults. It respects .gitignore by default, so you don't get flooded with matches from node_modules. It shows file names and line numbers in a readable format. And the flag syntax is more intuitive than grep's.
rg "useState" --type ts
Search for "useState" in all TypeScript files. With grep, that would be grep -rn "useState" --include="*.ts" --include="*.tsx" . โ more typing, harder to remember.
fd replaces find. The syntax is immediately more readable:
fd -e json "config"
Find files with a .json extension that have "config" in the name. The find equivalent would be find . -name "*config*" -name "*.json" or something like that. I always have to look up the find flags. fd I can type from memory.
bat replaces cat. It adds syntax highlighting, line numbers, and git diff markers in the gutter. I have alias cat="bat" in my config, which means I get syntax highlighting every time I view a file in the terminal. Probably the lowest-effort upgrade that provides the most visible improvement.
eza replaces ls. Tree view, git status per file, icons. I use eza --tree --level=2 --git to get a quick overview of a project structure with git status. Nicer than ls -la by a wide margin.
tmux
I use tmux for two things: keeping sessions alive on remote servers, and splitting my local terminal into panes.
The "keeping sessions alive" part is the main reason I started using it. If you SSH into a server and start a long-running process, then your SSH connection drops, the process dies. With tmux, the process runs inside a tmux session on the server. If your connection drops, the session keeps running. You reconnect and reattach. The process didn't notice anything happened.
Locally, I use it to split my terminal into panes โ usually editor on the left, dev server on the right, and a small pane at the bottom for running one-off commands. Some people use VS Code's built-in terminal for this. I prefer tmux because the layout is more flexible and the shortcuts are faster once you've memorized them.
My tmux config changes a few defaults that I find annoying:
# Change prefix from Ctrl+b to Ctrl+a
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
# Split with | and - instead of % and "
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Enable mouse
set -g mouse on
# Start window numbering at 1, not 0
set -g base-index 1
setw -g pane-base-index 1
The prefix change from Ctrl+B to Ctrl+A is the most important one. Ctrl+B requires an awkward hand position โ your index finger stretches to B while your pinky holds Ctrl. Ctrl+A lets you keep your left hand in a natural position. Small thing, but tmux is all keyboard shortcuts, so the ergonomics matter.
The mouse support gets me some side-eye from terminal purists. But scrolling through log output with the mouse wheel is just faster than pressing Ctrl+A then [ then navigating with keys. I'll take the practical tool over the ideologically pure one.
#{pane_current_path} on the split commands means new panes open in the same directory as the current pane. Without this, every new pane starts in your home directory and you have to cd to wherever you were working. A small annoyance that I fixed once and never thought about again.
Git Aliases
[alias]
s = status -sb
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
wip = !git add -A && git commit -m "WIP"
undo = reset --soft HEAD~1
branches = branch --sort=-committerdate --format='%(committerdate:relative)%09%(refname:short)'
lg is the one I use most. The default git log output is borderline unreadable on a busy repo. This alias shows a compact, colorized graph view with short hashes, branch decorations, relative dates, and author names. I use it so often that I sometimes forget what the actual git log output looks like.
wip is a quick save before I switch branches or go to lunch. It stages everything and commits with "WIP". Not clean, but I squash these before opening a PR.
undo resets the last commit but keeps all the changes staged. Useful when you commit too early and realize you forgot something, or when you want to change the commit message. Safer than --hard because nothing gets deleted.
branches sorts your local branches by most recently committed. When I have 15 branches and can't remember what the one from Tuesday was called, this shows the recent ones at the top with dates. More useful than git branch which just shows them in alphabetical order.
That's basically everything. The whole setup takes maybe 30 minutes to configure on a new machine โ most of it is installing the Rust tools and copying config files. I keep my dotfiles in a git repo which makes it even faster, though I should probably automate the installation with a script. One of those things I keep meaning to do.
Written by
Anurag Sinha
Developer who writes about the stuff I actually use day-to-day. If I got something wrong, let me know.
Found this useful?
Share it with someone who might find it helpful too.
Comments
Loading comments...
Related Articles
Linux CLI: The Commands I Use Every Day
A collection of bash commands and shortcuts I reach for constantly, organized by what I'm trying to do rather than by category.
Learning Docker โ What I Wish Someone Had Told Me Earlier
Why most Docker tutorials fail beginners, the critical difference between images and containers, and what actually happens when you run a container.
Cloud-Native Architecture: What It Means and What It Costs
Reference definitions for the 12-factor app methodology, containerization, infrastructure as code, and CI/CD pipelines.