Startup files: .zshenv, .zprofile, .zshrc

Zsh · Essentials · 9 min

Putting settings in the wrong file causes slow scripts, missing PATH entries and confusing behaviour.

Zsh reads up to four user files, each after its system-wide twin in /etc, in this order:

  • ~/.zshenv: read by every zsh, including scripts and zsh -c. Keep it tiny and silent: a few environment variables at most.
  • ~/.zprofile: login shells, before .zshrc. A good place for PATH and other environment setup.
  • ~/.zshrc: interactive shells. Aliases, options, completion, key bindings and the prompt go here.
  • ~/.zlogin: login shells, after .zshrc; rarely needed. ~/.zlogout runs when a login shell exits.

Unlike bash, an interactive login shell reads both .zprofile and .zshrc, so there is no need to source one from the other. On macOS every new Terminal tab is a login shell, while most Linux terminal emulators start non-login shells.

On macOS, /etc/zprofile runs path_helper, which reorders PATH; entries you add in .zshenv can end up behind system directories, which is another reason to set PATH in .zprofile.

ZDOTDIR relocates these files (for example to ~/.config/zsh); set it in ~/.zshenv. After editing, exec zsh replaces your shell with a fresh one, which is cleaner than source ~/.zshrc because settings removed from the file really disappear.

Example

# Order for an interactive login shell (e.g. a macOS Terminal tab):
#   /etc/zshenv  ~/.zshenv  ->  /etc/zprofile  ~/.zprofile
#   ->  /etc/zshrc  ~/.zshrc  ->  /etc/zlogin  ~/.zlogin
# ~/.zshenv   : every zsh, scripts included -> keep it tiny
export EDITOR=vim
# ~/.zprofile : login shells -> PATH and environment
export PATH="$HOME/.local/bin:$PATH"
# ~/.zshrc    : interactive shells -> aliases, options, prompt, completion
alias ll='ls -lah'
setopt AUTO_CD
# Reload after editing:
exec zsh
# See which files actually run at startup:
zsh -o SOURCE_TRACE -i -c exit 2>&1 | head

Remember

  • .zshenv: every shell, keep minimal; .zprofile: login; .zshrc: interactive
  • Interactive login shells read both .zprofile and .zshrc
  • exec zsh reloads cleanly

Watch out for

  • Printing output or running slow commands in ~/.zshenv, which affects every script and scp session.

Check yourself

  1. Where should aliases and the prompt be defined?
  2. Which file is read even by a non-interactive `zsh script.zsh`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: zsh.sourceforge.io/Doc/Release/Files.html