macOS Terminal Setup Guide: zsh, Customization & Essential Commands

A practical guide to the macOS terminal: how zsh works, the essential commands every Mac developer should know, customizing your prompt and shell, and the modern CLI tools that make the terminal a joy to use.

macOS Terminal Setup Guide: zsh, Customization & Essential Commands

The terminal is the most powerful application on your Mac, and it's already installed. Once you're comfortable in it, tasks that take a dozen clicks in the Finder become a single line, and everything from Git to servers to AI coding agents runs through it.

This guide takes you from "I open Terminal occasionally" to a fast, comfortable, well-customized setup. We'll cover how the shell works on macOS, the commands worth memorizing, how to customize your prompt and shell, and the modern command-line tools that make a real difference. Everything here works with the built-in Terminal — and even better in a dedicated terminal app (more on that below).

Opening the Terminal and Understanding Your Shell

Open Terminal from Applications → Utilities, or just press Cmd+Space and type "Terminal." You'll see a prompt waiting for input.

Since macOS Catalina (2019), the default shell is zsh (the Z shell). Older guides reference bash; the commands are mostly identical, but configuration lives in different files. To confirm what you're running:

echo $SHELL
# /bin/zsh

If you're on bash and want the modern default, switch with:

chsh -s /bin/zsh

zsh is backward-compatible with most bash usage but adds better tab completion, smarter globbing, and a thriving ecosystem of themes and plugins. The rest of this guide assumes zsh.

A quick note on the app itself: Apple's Terminal is fine for getting started, but a dedicated terminal renders faster and adds tabs, splits, and session persistence. If you outgrow it, see our comparison of the best Mac terminals.

The Essential Commands

You don't need to memorize hundreds of commands — you need fluency with a couple dozen. Here are the ones that come up constantly.

Navigating and inspecting:

pwd                 # print working directory (where am I?)
ls -lah             # list files: long format, all (incl. hidden), human sizes
cd ~/Projects       # change directory (~ is your home folder)
cd -                # jump back to the previous directory
open .              # open the current folder in Finder

Working with files and folders:

mkdir -p a/b/c      # create nested folders in one go
cp -R src dest      # copy a folder recursively
mv old new          # move or rename
rm -i file          # remove, prompting before each delete (safer)
touch file.txt      # create an empty file / update its timestamp

Reading and searching:

cat file.txt              # print a file
less file.txt             # page through a long file (q to quit)
head -n 20 file.txt       # first 20 lines
tail -f server.log        # follow a log live
grep -rn "TODO" .         # recursively find "TODO" with line numbers

Processes and the system:

top                  # live process list (q to quit)
ps aux | grep node   # find a running process
kill -9 <pid>        # force-quit a process by id
lsof -i :3000        # what's using port 3000?
df -h                # disk space, human-readable

Networking:

If a development server reports that its port is occupied, the macOS port troubleshooting guide shows how to identify the listener, find its project, and resolve the collision.

curl -I https://example.com    # fetch just the headers
ping example.com               # check connectivity (Ctrl+C to stop)
ipconfig getifaddr en0         # your local IP address

A few habits make these safer and faster: use rm -i until deleting is muscle memory, lean on Tab completion for paths (start typing and press Tab), and remember that Ctrl+C cancels a running command while Ctrl+R searches your history.

Configuring zsh with ~/.zshrc

Your shell reads ~/.zshrc every time it starts. This file is where you set environment variables, define shortcuts, and initialize tools. Open it with any editor:

nano ~/.zshrc        # or: code ~/.zshrc  /  vim ~/.zshrc

After editing, apply changes without restarting the terminal:

source ~/.zshrc

A sensible starting point for history behavior — bigger history, no duplicates, shared across tabs:

HISTSIZE=50000
SAVEHIST=50000
setopt SHARE_HISTORY        # share history between sessions
setopt HIST_IGNORE_ALL_DUPS # drop older duplicate commands
setopt HIST_REDUCE_BLANKS   # tidy up whitespace

This alone makes Ctrl+R history search far more useful.

Install Homebrew (the Missing Package Manager)

Almost everything below is installed with Homebrew, the de facto package manager for macOS. Install it once:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

On Apple Silicon Macs, add Homebrew to your PATH by putting this in ~/.zshrc:

eval "$(/opt/homebrew/bin/brew shellenv)"

Now installing tools is one line each: brew install <name>. Update everything with brew update && brew upgrade.

A Better Prompt with Starship

The default prompt is functional but tells you little. Starship is a fast, cross-shell prompt that shows your Git branch and status, the language version for the project you're in, exit codes, and more — and it's asynchronous, so it stays snappy.

brew install starship

Then add this as the last line of ~/.zshrc:

eval "$(starship init zsh)"

Open a new terminal and you'll have a context-aware prompt instantly. Customize it in ~/.config/starship.toml if you want to tweak symbols, colors, or which modules show. If you prefer to stay vanilla, you can craft a PROMPT string by hand in zsh, but Starship gives you 90% of what most people want with zero fuss.

Aliases and Functions: Stop Typing the Same Things

Aliases turn long commands into short ones. Add these to ~/.zshrc:

alias ll='ls -lah'
alias gs='git status'
alias gp='git pull'
alias ..='cd ..'
alias ...='cd ../..'
alias reload='source ~/.zshrc'

For anything that needs an argument, use a function instead. A classic: make a directory and step into it in one move:

mkcd() { mkdir -p "$1" && cd "$1"; }

Now mkcd new-project creates the folder and drops you inside it. Build up a small collection of these over time — they compound.

Modern CLI Tools Worth Installing

The classic Unix tools are decades old and still work, but a wave of modern replacements are faster, friendlier, and prettier. None are required, but together they transform the experience:

Modern tool Replaces Why it's better
eza ls Colors, icons, a --tree view, Git status per file
bat cat Syntax highlighting, line numbers, paging
ripgrep (rg) grep Dramatically faster, respects .gitignore by default
fd find Simpler syntax, fast, sensible defaults
fzf — Fuzzy finder for files, history, and anything piped in
zoxide cd Jumps to directories by frecency: z proj
btop top A beautiful, interactive system monitor
delta git diff Side-by-side, syntax-highlighted diffs
jq — Slice and query JSON from the command line
tldr man Practical examples instead of exhaustive manuals

Install the set in one go:

brew install eza bat ripgrep fd fzf zoxide btop git-delta jq tlrc

Then wire up the two that need initialization in ~/.zshrc:

# zoxide: smarter cd — use `z <partial-name>`
eval "$(zoxide init zsh)"

# fzf: fuzzy history (Ctrl+R) and file search (Ctrl+T)
source <(fzf --zsh)

# make eza the default ll
alias ll='eza -lah --git --icons'
alias cat='bat'

After this, Ctrl+R becomes an interactive fuzzy search through your command history — one of the biggest single upgrades to terminal life.

Keyboard Shortcuts That Save Real Time

The shell uses Emacs-style line editing by default. These work in any zsh prompt:

  • Ctrl+A / Ctrl+E — jump to the start / end of the line
  • Ctrl+U / Ctrl+K — delete to the start / end of the line
  • Ctrl+W — delete the previous word
  • Ctrl+R — reverse-search your history (incredible with fzf)
  • Ctrl+L — clear the screen (keeps your command)
  • Ctrl+C — cancel the current command
  • Ctrl+D — end of input / close the shell
  • Option+← / Option+→ — move word by word (enable "Use Option as Meta key" in your terminal settings)

Memorize Ctrl+A, Ctrl+E, Ctrl+R, and Ctrl+W first — they'll change how it feels to type commands.

SSH: Connecting to Servers Properly

Sooner or later you'll work on a remote machine. Generate a modern key pair once:

ssh-keygen -t ed25519 -C "you@example.com"

Copy your public key to a server so you can log in without a password:

ssh-copy-id user@your-server.com
ssh user@your-server.com

Stop typing full host strings by defining shortcuts in ~/.ssh/config:

Host prod
    HostName 203.0.113.10
    User deploy
    IdentityFile ~/.ssh/id_ed25519

Host *
    AddKeysToAgent yes
    UseKeychain yes

Now ssh prod is all you need, and macOS will remember your key passphrase in the Keychain. To move files, scp file user@host:/path works, and rsync -avz local/ user@host:/remote/ is better for syncing folders. (If you do a lot of remote work, a terminal with a built-in file browser saves a lot of scp gymnastics — more below.)

Keeping Sessions Alive: tmux, zellij, and Beyond

A plain terminal loses everything when it closes or your laptop sleeps mid-task. Two tools fix that by running a persistent session you can detach from and reattach to later:

brew install tmux        # the classic, ubiquitous multiplexer
brew install zellij      # a modern, friendlier alternative with discoverable keybinds

Start tmux, run your long process, detach with Ctrl+B then D, and reattach later with tmux attach. Your work keeps running the whole time. This is essential for remote sessions and long builds.

Some terminals build persistence in so you don't have to think about it. If your workflow centers on running AI coding agents — keeping Claude Code or Codex alive across restarts, in split sessions, with files and SSH alongside — a purpose-built app like Agents UI handles persistence, splits, an editor, and remote file browsing natively, so you're not stitching it together with tmux and scp. For a traditional setup, tmux or zellij plus your favorite terminal works great too.

Make It Portable: Dotfiles

Once your setup is dialed in, don't lose it. Put ~/.zshrc, ~/.ssh/config (without private keys), ~/.config/starship.toml, and any other config into a Git repository — your "dotfiles." Then on a new Mac you can clone it and symlink the files back, and you're home in minutes instead of rebuilding from memory.

A minimal approach:

mkdir ~/dotfiles && cd ~/dotfiles
git init
cp ~/.zshrc ./zshrc
ln -sf ~/dotfiles/zshrc ~/.zshrc

From there, commit and push to a private repo. Plenty of frameworks exist (chezmoi, GNU Stow, yadm), but even a folder of files and a few symlinks gets you 90% of the benefit.

A Suggested Starting ~/.zshrc

Pulling it together, here's a clean baseline you can adapt:

# Homebrew (Apple Silicon)
eval "$(/opt/homebrew/bin/brew shellenv)"

# History
HISTSIZE=50000
SAVEHIST=50000
setopt SHARE_HISTORY HIST_IGNORE_ALL_DUPS HIST_REDUCE_BLANKS

# Aliases
alias ll='eza -lah --git --icons'
alias cat='bat'
alias gs='git status'
alias ..='cd ..'
alias reload='source ~/.zshrc'

# Functions
mkcd() { mkdir -p "$1" && cd "$1"; }

# Tools
eval "$(zoxide init zsh)"
source <(fzf --zsh)

# Prompt (keep this last)
eval "$(starship init zsh)"

Where to Go Next

You now have a fast, comfortable, well-organized terminal: a modern shell, a smart prompt, better tools, and the muscle memory to move quickly. The best way to get fluent is to use it for things you'd normally do in a GUI — copy files, search code, manage Git — until reaching for the mouse feels slower.

When you're ready to upgrade the terminal app itself, our guide to the best terminals for Mac in 2026 walks through the options, from minimalist speed demons to tools built for AI-agent workflows. Either way, the setup above will follow you wherever you go.

Try Agents UI

A native terminal for AI coding agents with persistent sessions, SSH workflows, and built-in editing.