As of 2022, I’ve been using zsh as my primary shell for 14 years.

Over that time I’ve experimented with and accumulated a lot of configuration, scripts, hacks, plugins and themes. I’ve settled on a configuration that I’m mostly happy with, over this series I’m going to share my current setup.

I store all of my configuration in a git repository available here: zsh-bootstrap Star Fork

File Structure

I’ve organised my setup into a few different files.

Installation Used to install the shell, configuration, plugins and homebrew packages when first setting up a new macOS machine/VM.


  • zshrc - The main zsh config file, it sources the following (.rc) files which are all just standard zsh files.
  • 0-paths.rc - Sets up the PATH variable.
  • 1-zgen.rc - Loads the plugin manager and plugins.
  • 3-location_specifics.rc - Sets up location specific configuration (e.g. based on network/hostname).
  • 4-aliases.rc - Sets up command aliases.
  • 5-exports.rc - Sets up environment exports.
  • 6-zsh_options.rc - Sets up zsh options/configuration flags.
  • 7-history.rc - Sets up history configuration.
  • 9-functions.rc - Sets up reusable functions.
  • 10-prompt.rc - Sets up the prompt.
  • 14-source-files.rc - Sources any files / configuration provided by tools, e.g. fnm, asdf, iterm2_shell_integration etc.

The only exception to this is files with private (.*(.|-)private(.|-).*) in the name, which are ignored by git, they don’t contain anything sensitive such as secrets, but I don’t want to share them.

Brew packages

  • Brewfile-<hostname.fqdn> - Brewfile for the current machine, this is used by the bootstrap script to install homebrew packages, and by the alias brewbackup to backup the current packages.


And a number of dotfiles that are symlinked into place:

  • .vimrc - Vim configuration.
  • .gitconfig - Git configuration.
  • .gitconfig-no_push - Git configuration only sourced on repositories where I wish to prevent pushing to a specific branch.
  • .gitignoreglobal - Global gitignore file.
  • .dircolors - Configuration for ls colours.
  • tmux.conf - Tmux configuration.

More on these files later!

Plugin Management

I use zgen, its lightweight, simple and fast.

You define a list of plugins you want to use, save that list as part of your zsh config, zgen will download and load them for you.

git clone "${HOME}/.zgen" --depth=1


oh-my-zsh plugins

zgen also supports loading individual oh-my-zsh plugins (which is far faster than loading all of oh-my-zsh).

  • macos - macOS specific aliases and functions.
  • docker - Docker aliases and functions.
  • docker-compose - Docker compose aliases and functions.
  • aws - AWS aliases and functions.
  • colored-man-pages - Coloured man pages.
  • command-not-found - Suggests installing missing commands.
  • fnm - Fnm aliases and functions.
  • fzf - Fzf aliases and functions.
  • nmap - Nmap aliases and functions.
  • gh - GitHub CLI aliases and functions.

Putting it all together

The following script is sourced on shell initialisation, it loads zgen, and then loads all the plugins.

# load zgen
source "${HOME}/.zgen/zgen.zsh"

export ZGEN_RESET_ON_CHANGE="${HOME}/.zshrc"

if ! zgen saved; then
  echo "Creating a zgen save"

  # load omz plugins
  zgen oh-my-zsh
  zgen oh-my-zsh plugins/macos
  zgen oh-my-zsh plugins/docker
  zgen oh-my-zsh plugins/docker-compose
  zgen oh-my-zsh plugins/gitfast
  zgen oh-my-zsh plugins/aws
  zgen oh-my-zsh plugins/colored-man-pages
  zgen oh-my-zsh plugins/command-not-found
  zgen oh-my-zsh plugins/fnm
  zgen oh-my-zsh plugins/fzf
  zgen oh-my-zsh plugins/nmap
  zgen oh-my-zsh plugins/gh

  # load normal plugins
  zgen loadall <<EOPLUGINS

    # git related

    # fzf wrappers

  # source completions
  zgen load zsh-users/zsh-completions src
  zgen save

Coming up soon in this series:

  • Functions
  • Themes / Colour Schemes
  • Scripts
  • Aliases
  • Dotfiles