One of the minor annoyances of working in Unix-land is the variety of ways to set user environment variables: .zshenv, .bash_profile, .profile, .xinitrc, and so forth. Depending on where you set them, your variables may be visible in interactive shells, login shells, X applications, or some strange mixture. Even worse is that some, but not all, environment variables are set when interpreting your crontab, so that has yet another set of rules you have to remember.

I’ve been gradually working on setting up my home directory so that it contains everything (barring system packages) that I need to work, so that migrating to a new computer is just a matter of installing packages and then copying my $HOME. This only works if I have a consistent way to set environment variables, so that they are visible to every program that runs under my account, regardless of how I logon. And I think I’ve finally got it.

This process requires changing a couple of system-wide settings, but only in special circumstances:

  • If you care about having environment variables enabled when you login via TTY or SSH, make the following change to /etc/pam.d/login: find the line

     session       required readenv=1 envfile=/etc/default/locale

    and change it to read

     session       required readenv=1 user_readenv=1 envfile=/etc/default/locale

    (In the default config, pam_env is loaded only when you login via your display manager: MDM on Mint, GDM on Gnome, etc.)

  • If you want to set LD_LIBRARY_PATH then you have to make a change to the Xsession settings: add a file 90preserve_ld_library_path to /etc/X11/Xsession.d/ containing


    This is only necessary because X normally starts ssh-agent which clears `LDLIBRARYPATH for security reasons. Another option is to disable loading of ssh-agent entirely.

  • If you want your environment variables to apply to your crontab (and you do), edit /etc/pam.d/cron and find the line

     session       required envfile=/etc/default/locale


     session       required user_readenv=1 envfile=/etc/default/locale

After making that change(s), create a file ~/.pam_environment and add your variables to it like this:

# HOME is not defined when this file is executed, and BOOST_ROOT needs 
# to be set somewhere, so...
HOME       DEFAULT=/home/@{PAM_USER}

# Setup path
PATH DEFAULT=${HOME}/bin:${HOME}/bin/bin:${PATH}
PATH DEFAULT=${HOME}/bin/rakudo:${PATH}
PATH DEFAULT=/opt/texlive/2016/bin/x86_64-linux:${PATH}
PATH DEFAULT=/opt/teyjus:${PATH}
PATH DEFAULT=${HOME}/.cabal/bin:${PATH}
PATH DEFAULT=${HOME}/.local/bin:${PATH}
PATH DEFAULT=/opt/cabal/1.20/bin:/opt/ghc/7.8.4/bin:${PATH}
PATH DEFAULT=${HOME}/bin/tmsu-x86_64-0.6.1/bin:${PATH}
PATH DEFAULT=${HOME}/bin/boost:${PATH}

# Python paths
PYTHONPATH DEFAULT=${HOME}/.local/lib/python/site-packages:${PYTHONPATH}
PYTHONPATH DEFAULT=${HOME}/.local/lib/python2.7/site-packages:${PYTHONPATH}

# Manpaths
MANPATH DEFAULT=/opt/texlive/2014/texmf/doc/man:${MANPATH}

# Infopaths
INFOPATH DEFAULT=/opt/texlive/2016/texmf/doc/info:${INFOPATH}

# Library/link paths
LD_LIBRARY_PATH DEFAULT=/usr/local/lib:${BOOST_ROOT}/stage/lib:${HOME}/lib:${HOME}/lib/sfml:${LD_LIBRARY_PATH}
LIBRARY_PATH    DEFAULT=${HOME}/lib:${HOME}/lib/sfml:${BOOST_ROOT}/stage/lib:${LIBRARY_PATH}

# Include paths

# TeX paths
TEXINPUTS DEFAULT=.:${HOME}/.texmf/tex/:
BIBINPUTS DEFAULT=.:${HOME}/.texmf/tex/:

This is my actual .pam_environment. Setting environment variables that refer to other, existing variables (e.g., extending $PATH) uses a fancy syntax:


Within the value, you can use ${...} to expand environment variables, and use @{...} to expand PAM_ITEMs (as I did to get the user’s login name, because $USER isn’t set yet).

After that, it’s basically a matter of setting things the way you want. Note that if you aren’t using any expansions, you can write just


With this setup, all of my environment variables are available to any process that runs under my account, including cron jobs! No more fighting with my editor to get it to use my $PATH when it runs my build tools, and no more hard-coded paths in crontab, etc.