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 andzsh -c. Keep it tiny and silent: a few environment variables at most.~/.zprofile: login shells, before.zshrc. A good place forPATHand other environment setup.~/.zshrc: interactive shells. Aliases, options, completion, key bindings and the prompt go here.~/.zlogin: login shells, after.zshrc; rarely needed.~/.zlogoutruns 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 | headRemember
- .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.