Setting environment variables via launchd.conf no longer works in OS X Yosemite/El Capitan/macOS Sierra/Mojave?
Asked Answered
C

9

204

It looks like the launchd.conf does not load my environment variable anymore. Has anyone else noticed that?

Is there another solution to permanently set environment variables?

Croce answered 19/8, 2014 at 14:24 Comment(1)
Does this answer your question? Setting environment variables on OS XLangevin
G
185

Create an environment.plist file in ~/Library/LaunchAgents/ with this content:

<?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>my.startup</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>
    launchctl setenv PRODUCTS_PATH /Users/mortimer/Projects/my_products
    launchctl setenv ANDROID_NDK_HOME /Applications/android-ndk
    launchctl setenv PATH $PATH:/Applications/gradle/bin
    </string>

  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>

You can add many launchctl commands inside the <string></string> block.

The plist will activate after system reboot. You can also use launchctl load ~/Library/LaunchAgents/environment.plist to launch it immediately.

[Edit]

The same solution works in El Capitan too.

Xcode 7.0+ doesn't evaluate environment variables by default. The old behaviour can be enabled with this command:

defaults write com.apple.dt.Xcode UseSanitizedBuildSystemEnvironment -bool NO

[Edit]

There a couple of situations where this doesn't quite work. If the computer is restarted and "Reopen windows when logging back in" is selected, the reopened windows may not see the variables (Perhaps they are opened before the agent is run). Also, if you log in via ssh, the variables will not be set (so you'll need to set them in ~/.bash_profile). Finally, this doesn't seem to work for PATH on El Capitan and Sierra. That needs to be set via 'launchctl config user path ...' and in /etc/paths.

Grapeshot answered 27/10, 2014 at 11:5 Comment(16)
No need to reboot! You can do "launchctl start environment.plist" and restart the app you need to get the new env vars ;)Leonardaleonardi
This wouldn't work with the PATH variable for me. So in addition to this approach for setting other variables, I set the PATH variable in my ~/.bash_profile. This might well not work for every case but so far I haven't got a problem.Reckless
@djule5 The PATH works for me, I use this line to add gradle to the path: launchctl setenv PATH $PATH:/Applications/gradle/binGrapeshot
This also worked to create new environment variables in Yosemite. Thanks!Schoolman
I also had to restart the Mac for this to work. It was not enough to do the launchctl start command followed by a restart of the GUI App (Eclipse).Pack
Figured it out: To work without reboot, it should be "launchctl load environment.plist", not startPack
This method works better for me than the one from ruario, as I have a boxen + homebrew setup - variable pointers for GUI apps is better to use plist than using individual apple script files.Rem
funny... for me restarting didnt work, only the launchctl did... back to looking for a way to set this at startup >.<Anent
Worked. Just had a minor problem: if you have the terminal window open, then close it and restart. Strange, but necessary in my case.Mauromaurois
@IkerJamardoZugaza not strange - the env has to be there before the app ie terminal.app startsInseminate
Ah, yes. Nothing like an obscure configuration setting that appears exactly 9 times in the entire internet (google UseSanitizedBuildSystemEnvironment).Saphena
@Grapeshot I used this to point to new version of Qt(5.5 instead of 4.8.7) . Did the following -: <string> launchctl setenv QTDIR /usr/local/Cellar/qt5/5.5 </string> and then launchctl load ~/Library/LaunchAgents/environment.plist . On doing qmake --version now, I still see QMake version 2.01a Using Qt version 4.8.7 in /usr/local/Cellar/qt/4.8.7/lib - what am I doing wrong ? I am on yosemite 10.10.5.Metage
This worked for me (El Capitan): - Add environment variable to XML (as explained in the answer) - Execute launchctl unload ~/Library/LaunchAgents/environment.plist - Execute launchctl load ~/Library/LaunchAgents/environment.plist - Exit Terminal - Relaunch Terminal and the new environment variable should be available (you can list all the variables with printenv) Note: Since this is an XML, your variables must follow the XML syntax rules. So, for example, the variable's value cannot contain an ampersand (&).Pother
Works on Sierra as wellThreegaited
this is the most sane & up to date solution between hundreds others. should be in some documentation or something.Edaphic
sudo launchctl config user path $PATH works for MojaveFarriery
H
67

[Original answer]: You can still use launchctl setenv variablename value to set a variable so that is picked up by all applications (graphical applications started via the Dock or Spotlight, in addition to those started via the terminal).

Obviously you will not want to do this every time you login.

[Edit]: To avoid this, launch AppleScript Editor, enter a command like this:

do shell script "launchctl setenv variablename value"

(Use multiple lines if you want to set multiple variables)

Now save (+s) as File format: Application. Finally open System SettingsUsers & GroupsLogin Items and add your new application.

[Original answer]: To work around this place all the variables you wish to define in a short shell script, then have a look at this previous answer about how to run a script on MacOS login. That way the the script will be invoked when the user logs in.

[Edit]: Neither solution is perfect as the variables will only be set for that specific user but I am hoping/guessing that may be all you require.

If you do have multiple users you could either manually set a Login Item for each of them or place a copy of com.user.loginscript.plist in each of their local Library/LaunchAgents directories, pointing at the same shell script.

Granted, neither of these workarounds is as convenient as /etc/launchd.conf.

[Further Edit]: A user below mentions that this did not work for him. However I have tested on multiple Yosemite machines and it does work for me. If you are having a problem, remember that you will need to restart applications for this to take effect. Additionally if you set variables in the terminal via ~/.profile or ~/.bash_profile, they will override things set via launchctl setenv for applications started from the shell.

Hagy answered 18/9, 2014 at 12:33 Comment(7)
As far as I can tell, one downside of this technique is that the variable(s) will not be set for any other applications started at login. So for example if you open Terminal, the variable will be set, but if you log off and back on again, having Terminal automatically restart, the variable will be unset...Pedometer
I have tried this solution and it didn't work for me either. But I am specifically expecting my Java IDE (IntelliJ) to pick up my path modifications and it is not. Everything works just fine from terminal. It might be a bug in IntelliJ. Still frustrating that Apple removed this functionality. I called Apple and they weren't very helpful.Awake
This is working for me, but do you know what to do to also add the environment variables to sudo ?Faa
Freaking fantastic, thank you. Worked for me on Yosemite to pass env variables to a ruby app which is executed via launchd and rvm.Zaccaria
This will work in general, however there is a bug in Yosemite (10.10.0 and 10.10.1 at least) where setting the $PATH does not work this way. Apple is aware of the bug. Currently (as of 10.10.1) there is no known way to set a system-wide $PATH for GUI apps.Untangle
After you use one of the above mentioned methods and reboot your laptop - Make sure you explicitly reopen the applications (such as iTerm, terminal, Eclipse, IDEA or whatever you're using). If you don't explicitly restart them and If while rebooting OSx the checkbox was checked to "Restart windows when logging back in" (which is the default) - those programs will not read the fresh environment variables.Arabel
1. launchctl setenv variablename valu and then 2. restart shell - worked for meOphiology
P
21

It is possible to set environment variables on Mac OS X 10.10 Yosemite with 3 files + 2 commands.

Main file with environment variables definition:

$ ls -la /etc/environment 
-r-xr-xr-x  1 root  wheel  369 Oct 21 04:42 /etc/environment
$ cat /etc/environment
#!/bin/sh

set -e

syslog -s -l warn "Set environment variables with /etc/environment $(whoami) - start"

launchctl setenv JAVA_HOME      /usr/local/jdk1.7
launchctl setenv MAVEN_HOME     /opt/local/share/java/maven3

if [ -x /usr/libexec/path_helper ]; then
    export PATH=""
    eval `/usr/libexec/path_helper -s`
    launchctl setenv PATH $PATH
fi

osascript -e 'tell app "Dock" to quit'

syslog -s -l warn "Set environment variables with /etc/environment $(whoami) - complete"

Service definition to load environment variables for user applications (terminal, IDE, ...):

$ ls -la /Library/LaunchAgents/environment.user.plist
-rw-------  1 root  wheel  504 Oct 21 04:37 /Library/LaunchAgents/environment.user.plist
$ sudo cat /Library/LaunchAgents/environment.user.plist
<?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>environment.user</string>
    <key>ProgramArguments</key>
    <array>
            <string>/etc/environment</string>
    </array>
    <key>KeepAlive</key>
    <false/>
    <key>RunAtLoad</key>
    <true/>
    <key>WatchPaths</key>
    <array>
        <string>/etc/environment</string>
    </array>
</dict>
</plist>

The same service definition for root user applications:

$ ls -la /Library/LaunchDaemons/environment.plist
-rw-------  1 root  wheel  499 Oct 21 04:38 /Library/LaunchDaemons/environment.plist
$ sudo cat /Library/LaunchDaemons/environment.plist
<?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>environment</string>
    <key>ProgramArguments</key>
    <array>
            <string>/etc/environment</string>
    </array>
    <key>KeepAlive</key>
    <false/>
    <key>RunAtLoad</key>
    <true/>
    <key>WatchPaths</key>
    <array>
        <string>/etc/environment</string>
    </array>
</dict>
</plist>

And finally we should register these services:

$ launchctl load -w /Library/LaunchAgents/environment.user.plist
$ sudo launchctl load -w /Library/LaunchDaemons/environment.plist

What we get:

  1. The only place to declare system environment variables: /etc/environment
  2. Instant auto-update of environment variables after modification of /etc/environment file - just relaunch your application

Issues / problems:

In order your env variables were correctly taken by applications after system reboot you will need:

  • either login twice: login => logout => login
  • or close & re-open applications manually, where env variables should be taken
  • or do NOT use feature "Reopen windows when logging back".

This happens due to Apple denies explicit ordering of loaded services, so env variables are registered in parallel with processing of the "reopen queue".

But actually, I reboot my system only several times per year (on big updates), so it is not a big deal.

Pauly answered 21/10, 2014 at 1:23 Comment(7)
Great idea. I have tried it and it works for most environment variables (like JAVA_HOME), but not for the PATH variable (see my question on ask different).Retardation
PATH should be set with /etc/paths file. Just add your custom path to the end of this file.Pauly
I'm not that familiar with launchd, but wouldn't it be possible to load those Daemons at boot (i.e. before login)? That should circumvent all the issues you mention.Timeous
I love the approach above but have a weird issue to manage. After reboot a genet VARNAME returns me the right value but echo $VARNAME returns nothing. What might be a reason for this? I posted that to #27045637 as well and hope that anyone here has an ideaWilhelminawilhelmine
Make sure the file permissions of /etc/environment are as described above.Manaus
I wanted to chime in and mention that I tried various methods for setting custom environment variables for OSX Yosemite and this solution seems to work the best. None of these solutions seem very "clean" to me, but this was the most simple and worked for all of my programs.Quin
In my question I outline how to use the above approach to re-use my shell exports to have only one true source for environment variables, i.e., in my case my .zshrc. I consider this an extension of the above described approach.Notion
I
10

Cited from

Apple Developer Relations 10-Oct-2014 09:12 PM

After much deliberation, engineering has removed this feature. The file /etc/launchd.conf was intentionally removed for security reasons. As a workaround, you could run launchctl limit as root early during boot, perhaps from a LaunchDaemon. (...)

Solution:

Put code in to /Library/LaunchDaemons/com.apple.launchd.limit.plist by bash-script:

#!/bin/bash

echo '<?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>eicar</string>
        <key>ProgramArguments</key>
        <array>
                <string>/bin/launchctl</string>
                <string>limit</string>
                <string>core</string>
                <string>unlimited</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceIPC</key>
        <false/>
</dict>
</plist>' | sudo tee /Library/LaunchDaemons/com.apple.launchd.limit.plist
Imperil answered 11/10, 2014 at 6:20 Comment(4)
Can you explain this a bit more? I can't see how the 'Solve Issue' relates to the initial problem!Hautegaronne
Not the OP, but I think the gist here is: put this plist into /Library/LaunchDaemons, and instead of telling launchctl to run the limit command, tell it to run the setenv command with PATH and a path string as arguments. launchd should pick it up automatically on startup and get sort of self-modified almost immediately.Buttock
The xml is incompletely copied. The doctype line should read <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">Superfecundation
@Imperil what part of that plist actually sets an environment variable?Brenneman
D
5

Here are the commands to restore the old behavior:

# create a script that calls launchctl iterating through /etc/launchd.conf
echo '#!/bin/sh

while read line || [[ -n $line ]] ; do launchctl $line ; done < /etc/launchd.conf;
' > /usr/local/bin/launchd.conf.sh

# make it executable
chmod +x /usr/local/bin/launchd.conf.sh

# launch the script at startup
echo '<?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>launchd.conf</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>/usr/local/bin/launchd.conf.sh</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
' > /Library/LaunchAgents/launchd.conf.plist

Now you can specify commands like setenv JAVA_HOME /Library/Java/Home in /etc/launchd.conf.

Checked on El Capitan.

Darindaring answered 6/10, 2015 at 9:6 Comment(1)
this is a nice solution. I think the setenv commands only apply in the caller's context -- so this would work for LaunchAgents, and a separate file would be needed for LaunchDaemons?Foreclose
A
2

What did work for me (inspired from aax' thanks) :

Paste this into /Library/LaunchDaemons/com.apple.launchd.limit.plist then reboot :

<?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>eicar</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/launchctl</string>
    <string>limit</string>
    <string>maxfiles</string>
    <string>16384</string>
    <string>16384</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>ServiceIPC</key>
  <false/>
</dict>
</plist>

If you need it step by step :

  • Launch terminal
  • Type sudo su then enter your password to log in as root
  • Type vi /Library/LaunchDaemons/com.apple.launchd.limit.plist
  • When into the vi editor, press the key i to enter insert mode then paste the exact code content above (⌘+v). This will force the limit to 16384 files per process and 16384 files total
  • Save your file and quit using esc then :wq
  • Reboot your system, and check that it is working using the command launchctl limit

I hope this helped you.

Ashcan answered 21/10, 2014 at 6:41 Comment(1)
What does this solution have to do with setting environment variables?Brenneman
E
2

You can give https://github.com/ersiner/osx-env-sync a try. It handles both command line and GUI apps from a single source and works withe the latest version of OS X (Yosemite).

You can use path substitutions and other shell tricks since what you write is regular bash script to be sourced by bash in the first place. No restrictions.. (Check osx-env-sync documentation and you'll understand how it achieves this.)

I answered a similar question here where you'll find more.

Ecclesiastes answered 7/9, 2015 at 19:9 Comment(0)
U
-4

The solution is to add your variable to /etc/profile. Then everything works as expected! Of course you MUST do it as a root user with sudo nano /etc/profile. If you edit it with any other way the system will complain with a damaged /etc/profile, even if you change the permissions to root.

Uttica answered 18/10, 2014 at 12:49 Comment(1)
Adding environment variables to profile is very much inferior since it only affects shell processes.Superfecundation
F
-6

I added the variables in the ~/.bash_profile in the following way. After you are done restart/log out and log in

export M2_HOME=/Users/robin/softwares/apache-maven-3.2.3
export ANT_HOME=/Users/robin/softwares/apache-ant-1.9.4
launchctl setenv M2_HOME $M2_HOME
launchctl setenv ANT_HOME $ANT_HOME
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/robin/softwares/apache-maven-3.2.3/bin:/Users/robin/softwares/apache-ant-1.9.4/bin
launchctl setenv PATH $PATH

NOTE: without restart/log out and log in you can apply these changes using;

source ~/.bash_profile
Forzando answered 17/1, 2015 at 23:23 Comment(4)
Note that you don't have to log out and back in. Just use the source command i.e. source .bash_profile.Halfwitted
Also, the problem with this method is that you still have to open a terminal before the environment variables would be available. Better to do what is in the first answer so they are available without having to open a terminal.Halfwitted
This doesn't work on apps loaded via SpotLight. #136188Estremadura
Using the bash config files is of limited help since it presumes that you've always got bash as an ancestor of the process who's environment your trying to effect. Spotlight, the finder, emacs, xcode, cronjobs, launchd agents, any IDE, source control browsers, etc. etc. all aren't going to have bash as an ancestor. The only process that can span these consistently is launchd.Portly

© 2022 - 2024 — McMap. All rights reserved.