Question

I've switched to using environment variables for configuration and it works very well - except when I have to deploy or run tasks with capistrano.

Capistrano 3 seems to execute each command prefixed with /usr/bin/env which erases any environment variables I've set through .bashrc.

EDIT - on doing some more reasearch, this might not be the issue, the issue might be because capistrano executes as a non-login, non-interactive shell and does not load .bashrc or .bash_profile. Still stuck, though.

What would be the best way of making sure the environment vars are set when capistrano executes its tasks?

Was it helpful?

Solution

You might be best looking at the difference between ENVIRONMENT VARIABLES and SHELL VARIABLES

When you fire SSH, your app will load the SHELL variables which are defined in your .bashrc file. These only exist for the life of the shell, and therefore, we don't use them as much as ENV vars

You may be better putting the ENV vars in:

/etc/environment

Like this:

export ENVIRONMENT_VAR=value

This will make the variables available throughout the system, not just in different shell sessions


Update

Have you tried

Capistrano: Can I set an environment variable for the whole cap session?

set :default_env, { 
  'env_var1' => 'value1',
  'env_var2' => 'value2'
}

OTHER TIPS

Although this has been answered, I'm going to leave this here in case anyone else is in the same situation I was.

Capistrano does load .bashrc. But if you'll notice at the top of the file there is this:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

The solution was simply to put any setup above this and Capistrano works how I want.

This solution was also noted at this GitHub issue.

In order to debug the issue update config/deploy.rb with a simple task:

namespace :debug do
  desc 'Print ENV variables'
  task :env do
    on roles(:app), in: :sequence, wait: 5 do
      execute :printenv
    end
  end
end

now run cap staging debug:env. You should be able to see effective configuration of ENV variables.

The order and names of files are dependent on your distribution, e.g. on Ubuntu the sourcing sequence is following:

  1. /etc/environment
  2. /etc/default/locale
  3. /etc/bash.bashrc
  4. ~/.bashrc

When ~/.bashrc contains first lines like this, any code afterwards won't be sourced:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

To understand how capistrano loads ENV variables this chart (source) might be helpful.

Most likely the ~/.bash* file is not loaded due to non-interactive session.

capistrano env variables loading

You need to set environment variables in the /etc/environment file to make them available to all users and process within a system. Environment variables in the .bashrc or .bash_profile files are only available within a shell session and not for automatically spawned processes and services.

I made a Capistrano library (capistrano-env_config) some time ago for managing and syncing environment variables across a cluster which works exactly by modifying the /etc/environment file. It's easy to use and is similar to how you can set environment variables with the Heroku toolbelt. Here are some examples:

cap env:list
cap env:get[VARIABLE_NAME, VARIABLE_NAME, ...] 
cap env:unset[VARIABLE_NAME, VARIABLE_NAME, ...] 
cap env:set[VARIABLE_NAME=VALUE, VARIABLE_NAME=VALUE, ...] 
cap env:sync

The solution I settled on was:

  1. Enable the PermitUserEnvironment option in the /etc/ssh/sshd_config of all servers I need to deploy to.
  2. Add a ~/.ssh/environment file for each user's home dir I deploy to with env vars in the form of KEY=VALUE pairs (I deploy every app and service via it's own user to that user's home dir).

Reference: http://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#.7E.2F.ssh.2Fenvironment

It's actually worse than that. I use Upstart to manage Puma/Rails, and need the env vars set there as well. So, after days of experimentation, I ended up on the following complete but horrific solution:

  1. Set my env vars in the user's .bashrc using "export KEY=VALUE". (So they exist when I SSH in interactively.)
  2. Set my env vars in the user's .ssh/environment file using "KEY=VALUE". (So they exist when Capistrano SSHs in.)
  3. Set my env vars in /etc/init/puma.conf's "script" section. (So they exist when Puma/Rails starts.)

It's a pain in the ass maintaining the same list of env vars in multiple files/templates and in multiple formats (with export, without export...). Luckily it's made slightly easier/more reliable by using Puppet to manage the configuration of the node before Capistrano is used to deploy to it...

I really hate the entire domain of linux shells, initialization, and dotfiles. It's time for a complete reboot.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top