macOS Environment Variables: Set, View and Manage Terminal Settings

Published:

Think environment variables are a relic?
They’re the silent cause of many “works on my machine” bugs and late-night debugging sessions.

On macOS, shells and GUI apps read different places, and since Catalina switched to zsh the file you edit actually matters.

This post shows how to view variables in Terminal, set temporary ones for quick tests, persist them in the right zsh files, add system-wide paths with path_helper, and expose variables to GUI apps via launchctl or a LaunchAgent.

Read on for fast commands, exact file targets, and common gotchas.

Viewing and Understanding macOS Environment Variables in Terminal

nnkBnBykQc6fyLkuzna0ZA

Environment variables tell your shell and apps how to behave. They store things like your home directory, where commands live, and app preferences. On macOS Catalina (10.15) and newer, Apple swapped the default shell from Bash to Zsh. So knowing which shell you’re running actually matters when you’re debugging or tweaking variables.

The fastest way to see everything is printenv or env in Terminal. Both dump all variables for your current session, but printenv can also grab a single variable (like printenv PATH) without needing the $ syntax. env usually lists everything or runs a command with modified variables.

To check a specific variable, try echo $PATH or echo $HOME. That prints the value straight out. You can pipe printenv through grep to filter, printenv | grep PATH shows anything with “PATH” in the name or value. For variables affecting GUI apps launched from Finder or Spotlight, use launchctl getenv VAR to check values set at the system (launchd) level.

Essential commands for viewing environment variables:

  • printenv lists all environment variables. Add a name (like printenv USER) to see just one value.
  • env lists all variables, also used to run commands with temporary overrides.
  • echo $PATH prints the current PATH. Swap PATH for any variable name (echo $HOME, echo $SHELL).
  • printenv | grep JAVA filters output to show only variables matching “JAVA.”
  • launchctl getenv PATH shows the value of PATH as seen by launchd (affects GUI apps).
  • echo $SHELL confirms which shell is active (usually /bin/zsh on modern macOS).

Setting Temporary macOS Environment Variable Values in a Shell Session

bGOAhAj7Qjmsvi0b2sYrQw

Temporary variables last only for the current Terminal session. They’re handy for quick tests, one-off script runs, or setting a variable you don’t want to keep. The command export VAR="value" creates or updates the variable in your active shell. Close the Terminal window or tab and the variable vanishes.

Validate it right away with echo $VAR or pipe env | grep VAR to confirm it’s set. When you’re done, run unset VAR to remove the variable before the session ends. Clean way to avoid polluting your environment during experiments.

Steps to set, use, and remove a temporary variable:

  1. Set the variable: export MY_NAME="Jane Doe" (no spaces around =, use quotes if the value has spaces).
  2. Verify it: echo $MY_NAME or printenv MY_NAME should return Jane Doe.
  3. Use it in a command or script: the variable’s available to child processes started from that shell.
  4. Unset it when done: unset MY_NAME removes the variable from the session.

Common mistakes: adding spaces around the equals sign (export VAR = value fails) or forgetting quotes around multi-word values (export MY_NAME=Jane Doe treats Doe as a separate command argument, not part of the value).

Persisting macOS Environment Variables Using Zsh and Shell Configuration Files

7uT-gyBvSOClbU22yN1yZA

To keep a variable across shell sessions, add export lines to a shell configuration file that loads automatically at startup. macOS Catalina and later default to Zsh, so the main files are ~/.zshrc, ~/.zprofile, and ~/.zshenv. Each file has a specific purpose and load order. Picking the right one prevents duplication and makes sure your variables are there when you need them.

Zsh reads files in this order (simplified): /etc/zshenv~/.zshenv/etc/zprofile~/.zprofile/etc/zshrc~/.zshrc. For most interactive work in Terminal, ~/.zshrc is your go-to. Login-only setup (like setting PATH or JAVA_HOME once) fits best in ~/.zprofile. Variables needed in all shells (including scripts that skip .zshrc) belong in ~/.zshenv.

After editing any of these files, run source ~/.zshrc (or the file you changed) to apply changes immediately without restarting Terminal. Faster than logging out or opening a new window.

~/.zshrc

Use ~/.zshrc for environment variables, aliases, and functions you use in interactive Terminal sessions. Every time you open a new Terminal window or tab, Zsh reads this file. Right place for PATH modifications you want available during normal work.

Example PATH modification:
export PATH="/opt/homebrew/bin:$PATH"

Example JAVA_HOME (using dynamic version selection):
export JAVA_HOME="$(/usr/libexec/java_home -v 11)"

After adding the line, run source ~/.zshrc to reload. Verify with echo $JAVA_HOME.

~/.zprofile

~/.zprofile is read only for login shells. On macOS, Terminal windows start as login shells by default, so ~/.zprofile runs once per window. Use this file for variables you want set at login but don’t need reloaded in non-login shells (like subshells spawned by scripts).

Example:
export JAVA_HOME="/Library/Java/JavaVirtualMachines/zulu-23.jdk/Contents/Home"

Place this in ~/.zprofile if you want JAVA_HOME available immediately when you open Terminal but don’t need it reloaded every time a subshell starts.

~/.zshenv

~/.zshenv is always read, even for non-interactive and non-login shells. Good spot for variables required by scripts or background processes. Keep it minimal, overloading this file slows down every shell invocation.

Example safe usage:
export XDG_CONFIG_HOME="$HOME/.config"

Don’t put interactive-only settings (aliases, prompts) here.

Legacy Bash files (~/.bash_profile, ~/.bashrc)

If you switched from Bash to Zsh or use both shells, remember that ~/.bash_profile and ~/.bashrc control Bash sessions. Zsh doesn’t read these files. If you need a variable in both shells, duplicate the export line in the corresponding Zsh file (~/.zshrc or ~/.zprofile).

Commands to reload shell config:

  • source ~/.zshrc applies changes in ~/.zshrc without restarting Terminal.
  • source ~/.zprofile applies changes in ~/.zprofile.
  • source ~/.zshenv applies changes in ~/.zshenv (rarely needed manually).

System‑Wide macOS Environment Variables and PATH Management

eM-REFkRRHG-OxLrCqXOXg

macOS builds the system PATH using path_helper, a utility that combines entries from /etc/paths and individual files in /etc/paths.d. When a shell starts, path_helper reads each line from /etc/paths (one path per line), then reads all files in /etc/paths.d (also one path per line), and constructs a single PATH string. Duplicate entries get ignored automatically.

This system-wide mechanism makes sure all users and shells (including GUI apps started via Finder or LaunchServices) inherit the same base PATH. Admins add global paths by placing a new file in /etc/paths.d rather than editing /etc/paths directly, because macOS updates may overwrite /etc/paths but typically leave /etc/paths.d untouched.

Recommended steps to add a system-wide path (e.g., Homebrew on Apple Silicon):

  • Create a new file: sudo nano /etc/paths.d/homebrew
  • Add one path per line (use absolute paths only, avoid ~):
    /opt/homebrew/bin
    /opt/homebrew/sbin
  • Save the file (requires admin password via sudo).
  • Log out and log back in, or restart your shell to pick up the new PATH.
  • Verify with echo $PATH and confirm /opt/homebrew/bin appears.

Always use absolute paths in /etc/paths and /etc/paths.d. Tilde expansion (~) doesn’t work consistently across all processes, especially GUI apps. Write /Users/yourname/bin instead of ~/bin.

File/Directory Purpose
/etc/paths Default system PATH entries (one per line). Read by path_helper at shell startup.
/etc/paths.d Directory containing additional path files. Each file lists one path per line. Survives OS updates better than editing /etc/paths directly.
path_helper Utility invoked at shell startup to merge /etc/paths and /etc/paths.d into a single PATH. Removes duplicates automatically.

Managing macOS Environment Variables for GUI Applications Using launchctl and LaunchAgents

EEr9E2DkRcOM5SaEb7i_uQ

Shell configuration files (~/.zshrc, ~/.zprofile) affect only Terminal sessions and processes launched from them. Apps started from Finder, Spotlight, or the Dock inherit their environment from launchd, macOS’s system-wide process manager. To make environment variables visible to GUI apps, use launchctl to set values at the launchd level or create a LaunchAgent property list (plist) that loads at login.

The quickest temporary approach is launchctl setenv VAR value, which applies immediately to your user session but lasts only until you log out. For persistent environment variables across reboots, create a plist file in ~/Library/LaunchAgents with an EnvironmentVariables dictionary. When you log in, launchd reads the plist and exports the key-value pairs to all GUI processes.

Creating a LaunchAgent plist for persistent GUI environment variables

A LaunchAgent plist requires a unique Label (reverse-domain format is standard), RunAtLoad set to true to apply at login, and an EnvironmentVariables dictionary containing your variables as key-value pairs. This approach is especially useful for variables like JAVA_HOME or XDG_CONFIG_HOME that development tools launched from an IDE (IntelliJ IDEA, VS Code) need to see.

Steps to create and load a LaunchAgent plist:

  1. Create the file: nano ~/Library/LaunchAgents/com.example.environment.plist
  2. Add plist content (example setting JAVA_HOME and XDG_CONFIG_HOME):
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   <plist version="1.0">
   <dict>
       <key>Label</key>
       <string>com.example.environment</string>
       <key>ProgramArguments</key>
       <array>
           <string>/usr/bin/true</string>
       </array>
       <key>RunAtLoad</key>
       <true/>
       <key>EnvironmentVariables</key>
       <dict>
           <key>JAVA_HOME</key>
           <string>/Library/Java/JavaVirtualMachines/zulu-23.jdk/Contents/Home</string>
           <key>XDG_CONFIG_HOME</key>
           <string>/Users/yourname/.config</string>
       </dict>
   </dict>
   </plist>
  1. Save the file and make sure permissions are correct (chmod 644 ~/Library/LaunchAgents/com.example.environment.plist).
  2. Load the agent: launchctl load ~/Library/LaunchAgents/com.example.environment.plist (or log out and log back in).
  3. Verify: launchctl getenv JAVA_HOME should return the path. echo $JAVA_HOME in a new Terminal window should also show the value.

GUI apps launched after the agent loads will inherit these variables. If you modify the plist, unload it (launchctl unload <path>) and reload, or just log out and back in.

Working With Development Toolchain Environment Variables on macOS

fiIEpj3XTmWaPoLL-vcpKA

Most development tools on macOS rely on a handful of environment variables to locate SDKs, runtimes, or package managers. Setting these variables correctly in your shell configuration (usually ~/.zshrc) makes sure commands and build scripts find the right versions of Java, Node, Python, or Go. Each toolchain has conventions, and macOS provides helpers (like /usr/libexec/java_home) to simplify path management.

JAVA_HOME points to the root of a Java Development Kit installation. Instead of hardcoding the path, use /usr/libexec/java_home -v 11 to dynamically select Java 11, or omit the version flag to pick the system default. Xcode-related builds use SDKROOT to specify the SDK path and MACOSX_DEPLOYMENT_TARGET to set the minimum macOS version for compiled binaries. These are typically set by Xcode itself or xcodebuild, but you can override them in shell scripts.

Homebrew installs packages to /usr/local on Intel Macs and /opt/homebrew on Apple Silicon. Setting HOMEBREW_PREFIX to the correct path makes sure formulae and build scripts locate dependencies. Node Version Manager (NVM) requires NVM_DIR to point to ~/.nvm, and many Node projects read NODE_ENV (like production, development) to configure behavior. Python environment managers like pyenv use PYENV_ROOT (default ~/.pyenv), and Go toolchains read GOPATH for workspace directories and GOROOT for the Go installation itself (though modern Go versions auto-detect GOROOT).

Example export lines for common developer variables (add to ~/.zshrc):

  • export JAVA_HOME="$(/usr/libexec/java_home -v 11)" dynamically set JAVA_HOME to Java 11.
  • export NVM_DIR="$HOME/.nvm" tell NVM where to find installed Node versions.
  • export NODE_ENV="development" default Node environment for local work.
  • export PYENV_ROOT="$HOME/.pyenv" point pyenv to its installation directory.
  • export GOPATH="$HOME/go" define Go workspace (optional in Go 1.11+, but some tools still check it).
  • export HOMEBREW_PREFIX="/opt/homebrew" set Homebrew path for Apple Silicon Macs (Intel uses /usr/local).

After adding these lines, run source ~/.zshrc and verify each variable with echo $JAVA_HOME, echo $NVM_DIR, and so on. Place any PATH modifications related to these tools (like export PATH="$GOPATH/bin:$PATH") in the same file.

Troubleshooting macOS Environment Variables and Ensuring Correct Shell Behavior

fE9scUkWQ5GK_W9xPu5kTw

Most frequent issue is editing the wrong file or forgetting to reload it. Run echo $SHELL to confirm which shell is active. /bin/zsh means you should edit ~/.zshrc or ~/.zprofile, not ~/.bash_profile. After making changes, run source ~/.zshrc (or the file you edited) to apply them immediately. If the variable still doesn’t appear, check for typos, missing quotes, or spaces around the = sign.

For variables that should affect GUI apps, use launchctl getenv VAR to confirm the value is set at the launchd level. If launchctl getenv JAVA_HOME returns nothing but echo $JAVA_HOME works in Terminal, the variable is shell-only and won’t reach apps launched from Finder. In that case, add it to a LaunchAgent plist or use launchctl setenv for a temporary fix.

Common troubleshooting steps:

  • Verify file load order: Zsh reads ~/.zshenv~/.zprofile~/.zshrc. If a variable is set in multiple files, the last one wins. Use echo $PATH and printenv | grep VAR to see the final value.
  • Check GUI inheritance with launchctl: launchctl getenv PATH shows what GUI apps see. If it’s missing directories from ~/.zshrc, those are shell-only.
  • Restart the shell or log out/in: Some changes (especially system-wide /etc/paths.d edits or LaunchAgent plists) require a full logout or restart to take effect.
  • Audit environment for conflicts: Run printenv and look for duplicate or conflicting values. If two tools set the same variable, the last export wins.
  • Don’t commit secrets to dotfiles: Never store API keys, passwords, or tokens in ~/.zshrc or plist files. Use macOS Keychain or a secrets manager instead.
  • Confirm plist permissions and format: LaunchAgent plists must be valid XML. Use plutil -lint <file> to check syntax. Permissions should be readable by your user (chmod 644).
  • Identify mistaken shell usage: If a variable works in one Terminal window but not another, check if one window is running Bash and the other Zsh. Each shell has separate config files.

Final Words

View values fast: printenv, env, echo $VAR, and launchctl getenv for GUI processes. Set temporary vars with export and unset, and persist important ones in ~/.zshrc or ~/.zprofile.

Manage system PATH via /etc/paths and /etc/paths.d, and use launchctl or LaunchAgents for GUI apps. Keep common toolchain exports (JAVAHOME, NVMDIR, HOMEBREW_PREFIX) in your shell files, validate with printenv | grep, and avoid storing secrets in plain text.

Follow these steps and macos environment variables will stop being a mystery—and your setup will be steadier.

FAQ

Q: Are there environment variables in macOS?

A: There are environment variables in macOS. macOS exposes env vars to shells and system processes; Zsh is the default since Catalina, and vars are viewable in Terminal or managed for GUI apps via launchctl.

Q: How to go environment variables in Mac?

A: To view environment variables on Mac, use Terminal commands like printenv, env, or echo $VAR; use launchctl getenv VAR to check variables visible to GUI applications.

Q: How to list all env variables in Mac?

A: To list all env variables in Mac, run printenv or env in Terminal; pipe to less or grep to filter; for GUI processes, check launchctl getenv or the relevant LaunchAgent plist.

Q: Where is java_home in Mac?

A: The javahome location on Mac is provided by /usr/libexec/javahome; run /usr/libexec/javahome -v 11 for Java 11, or export JAVAHOME in ~/.zshrc for a persistent value.

curtisharmon
Curtis has spent over two decades guiding hunters and anglers through the backcountry of Montana and Wyoming. His expertise in elk hunting and fly fishing has made him a sought-after voice in the outdoor community. Curtis combines traditional woodsmanship with modern techniques to help readers succeed in the field.

Related articles

Recent articles