Skip to Content

Load environment variables from the target server into Capistrano

Posted on

I was working with an application that needed to run a couple of command line scripts during deploy time. Those scripts required certain environment variables to be set from a separate file.

That means you have to make sure to include that environment file with every command. But if you’re making use of plugins or are otherwise unable to change the commands, it makes sense to load these into Capistrano’s :default_env variable so they will be present whenever a command is executed on the remote target.

From Capistrano’s documentation:

:default_env
• default: {}
• Default shell environment used during command execution.
• Can be used to set or manipulate specific environment variables (e.g. $PATH and such).

Sounds perfect. So let’s create a separate task first to grab the environment file from the server and parse it. In my example, the environment file is located at /var/www/domain.com/private/.environment and looks like this:

export DB_NAME=foo
export DB_HOST=localhost
export DB_USER=foo
export DB_PASS="bar"

I’ve created a small envvars:load Capistrano task in lib/capistrano/tasks/envvars.rake to grab and parse these variables:

namespace :envvars do
    desc 'Load environment variables'
    task :load do
        # Grab the current value of :default_env
        environment = fetch(:default_env, {})

        on roles(:app) do
            # Read in the environment file 
            lines = capture("cat /var/www/domain.com/private/.environment")
            lines.each_line do |line|
                # Remove the "export " keyword, we have no use for that here 
                line = line.sub /^export /, ""
                # Clean up the input by removing line breaks, tabs etc
                line = line.gsub /[\t\r\n\f]+/, ""

                # Grab the key and value from the line
                key, value = line.split("=")

                # Remove surrounding quotes if present
                value = value.slice(1..-2) if value.start_with?('"') and value.end_with?('"')

                # Store the value in our :default_env copy
                environment.store(key, value)
            end

            # Finally, update the global :default_env variable again
            set :default_env, environment
        end
    end
end

If you want to test the task, add puts capture(:env) after the set :default_env, environment line and run it:

bundle exec cap production envvars:load

You will get a list of all the environment variables from the remote shell session, including the ones defined in your environment file on the server.

To finish up, add the hook to your deploy.rb file in order to call this task after the :starting event:

namespace :deploy do
    after  :starting, 'envvars:load'
end

Now every deploy run will first grab these environment variables and make them available to all subsequent commands.

Note that :default_env won’t be be passed to execute for raw command strings! This seems to be done by design.

comments powered by Disqus