Search code examples
ruby-on-railscapistranopassengerrbenv

How to access environment variables during Capistrano deploy?


I have my rails (4.2) app running through Passenger (5.0.28) + Apache (2.4.7) on an Ubuntu (14.02) system, ruby (2.3.0) managed with rbenv . I deploy with Capistrano (3.4.0).

All my environment variables are set in a very simple profile.d script.

#!/bin/sh
export VAR1=VAL1
export VAR2=VAL2

This works like a charm. My app ENV has all the correct variables, Secrets.yml is properly populated... everything works EXCEPT for when deploying with Capistrano over ssh.

In my deploy.rb I have the following that I think is relavant:

set :ssh_options, {
forward_agent: true,
paranoid: true,
keys: "~/.ssh/id_rsa.pub"
}

Capistrano docs being incredibly limited and ssh\server config not my strong point I can't seem to figure out why my ENV variables aren't seen by Capistrano. If I run puts ENV.inspect during the deploy flow, I get things such as "TERM_PROGRAM"=>"Apple_Terminal" and my local machine user info and whatnot. Why isn't Capistrano using the remote environment? How can I amend my configuration either server side or in my deploy script to fix this?

Thanks for the help.


Solution

  • First I think some clarification of terminology and Capistrano's execution model is needed.

    Capistrano is a program that runs on your local machine. So ENV within Capistrano sees your local environment, not the server's. There is no way for Capistrano to "see" the remote ENV with plain Ruby code because the Ruby code that makes up Capistrano is not executing there.

    What Capistrano does do is use SSH to send commands to the server to be executed there. Commands like mkdir, bundle install, and so on.

    To see this illustrated, add a Capistrano task to your deployment flow that does this:

    task :puts_remote_env do
      on roles(:all) do
        remote_env = capture("env")
        puts remote_env
      end
    end
    

    This will run the env command on the remote server, capture the result, and print it to your console.

    I hope this makes it more clear how Capistrano works.


    So, as you can see from the puts_remote_env output, the variables defined in your profile.d script are not there. Why?

    It is because Capistrano is using a non-login, non-interactive SSH session. In that SSH session, your profile.d script is not being evaluated. This is explained in detail in the Capistrano FAQ: http://capistranorb.com/documentation/faq/why-does-something-work-in-my-ssh-session-but-not-in-capistrano/

    You need to find another way to set those variables other than profile.d script.

    You could specify them in your Capistrano configuration itself (e.g. production.rb) like this:

    set :default_env, { var1: "val1", var2: "val2" }
    

    Capistrano will then explicitly set up that environment when it executes SSH commands.

    Or you could use a tool like dotenv, which allows Rails to read variables variables from a special file instead of relying on the execution environment.

    Or you could experiment with different dot file locations to see if there are some that are still evaluated even in a non-login, non-interactive session. On Ubuntu, I've had success exporting variables at the very top of ~/.bashrc.