Quick-Guide on ~/.zprofile vs ~/.zshrc ๐
TL;DR โก
~/.zprofileโ runs once when you start a login shell (think "environment setup") ๐ง~/.zshrcโ runs every time you open an interactive shell (think "daily experience") ๐ฎ
Use both strategically: put your PATH and environment variables in ~/.zprofile, and your aliases, functions, and prompt customizations in ~/.zshrc โจ
Understanding Shell Startup ๐
When you open a terminal, zsh can start in two different modes. Understanding which mode you're in helps you decide where to put your configuration.
Login Shell
A login shell runs when you first authenticate. Think of it as the "front door" to your system:
- Opening a new terminal window on macOS (Terminal.app, iTerm2)
- SSH-ing into a remote machine (
ssh user@host) - Running
zsh -lexplicitly
Login shells read ~/.zprofile first, then ~/.zshrc.
Interactive Shell
An interactive shell is simply any shell where you can type commands and see output:
- Every command prompt you see
- New shells spawned from an existing shell
Interactive shells only read ~/.zshrc.
Key insight: On macOS, every new terminal tab/window is a login shell, so both files run. On Linux desktop terminals, you often get non-login shells, so only
~/.zshrcruns.
graph TD
A[Open Terminal] --> B{Login Shell?}
B -->|Yes macOS default| C[Load ~/.zprofile]
B -->|No Linux default| D[Load ~/.zshrc]
C --> D
D --> E[Interactive Prompt Ready]
style C fill:#e1f5ff
style D fill:#fff4e1
style E fill:#e8f5e8
What Goes Where ๐
The rule of thumb: initialization in ~/.zprofile, interaction in ~/.zshrc.
Put in ~/.zprofile ๐
Configuration that needs to happen once per session and must be available to all child processes:
-
Environment variables like
PATH,EDITOR,LANG -
Version managers that modify your environment
-
System-level setup
-
Anything that's expensive to run or that you want to happen once per login
Put in ~/.zshrc ๐
Configuration that affects your interactive experience and can be reloaded easily:
-
Aliases and functions
-
Prompt customization
-
Shell options and behaviors
-
Key bindings and completions
-
Anything you want to tweak and reload with
source ~/.zshrc
graph LR
A[~/.zprofile] -->|Sets up| B[Environment<br/>PATH, tools]
A -->|Runs once| C[Expensive operations<br/>SSH agent, etc]
D[~/.zshrc] -->|Configures| E[Interactive features<br/>Aliases, prompt]
D -->|Can reload| F[Easily tweakable<br/>No logout needed]
style A fill:#e1f5ff
style D fill:#fff4e1
Platform Differences ๐ป
macOS (Terminal, iTerm2)
New terminal tabs/windows start as login shells by default:
- Loads
~/.zprofileโ environment setup - Loads
~/.zshrcโ interactive config - Both files run every time you open a new tab
This is convenientโeverything just works. But be mindful: slow code in either file will delay your prompt.
Linux (GNOME Terminal, Kitty, Alacritty)
Desktop terminals usually start as non-login shells:
- Only loads
~/.zshrc ~/.zprofileis skipped
Solution: If you have important environment setup in ~/.zprofile, add this line at the top of your ~/.zshrc:
Or move your PATH setup directly into ~/.zshrc if you're primarily on Linux.
Practical Rules of Thumb ๐
1. Session vs. Prompt โฑ๏ธ
Ask yourself: "Does this need to run once per login, or every time I see a prompt?"
- Once per login โ
~/.zprofile - Every prompt โ
~/.zshrc
2. Environment First ๐ฃ๏ธ
If child processes (GUI apps, scripts) need to see it, put it in ~/.zprofile:
3. Easy Iteration ๐
Put experimental tweaks in ~/.zshrc so you can test them with:
No need to close your terminal or log out.
4. Performance Matters โก
Slow operations in ~/.zshrc will make every new shell sluggish. Profile your startup time:
If it's slow, move expensive operations to ~/.zprofile or optimize them.
5. Remote Scripts ๐
When writing scripts that need your environment, use a login shell:
This ensures ~/.zprofile runs and your PATH is set up correctly.
Minimal Template ๐
Here's a clean starting point that separates concerns:
Quick Reference ๐ฏ
| Scenario | File | Why |
|---|---|---|
| Set PATH for all programs | ~/.zprofile |
Needs to be available to child processes |
Define alias ll='ls -lah' |
~/.zshrc |
Interactive convenience, reload anytime |
| Initialize pyenv/nvm | ~/.zprofile |
Expensive, needs to run once |
| Customize prompt with colors | ~/.zshrc |
Visual/interactive feature |
Set EDITOR=vim |
~/.zprofile |
Environment variable for other programs |
| Add shell completions | ~/.zshrc |
Interactive feature |
| Start ssh-agent | ~/.zprofile |
Once per session is enough |
| Create shell functions | ~/.zshrc |
Interactive convenience, iterate easily |
Debugging Your Setup ๐
Not sure which file is running? Add these debug lines:
Open a new terminal and see what prints. Remove the debug lines once you understand your setup.
You can also check if you're in a login shell:
Summary โจ
~/.zprofile: Environment setup, PATH, version managersโthings that need to happen once per session~/.zshrc: Aliases, prompt, shell optionsโthings that make your daily shell experience pleasant- macOS starts login shells by default (both files run)
- Linux often starts non-login shells (only
~/.zshrcruns) - Keep it clean, keep it fast, and you'll have a reliable shell environment across machines