Capsitrano Environment variables
This is the story of an epic fight. Me (regular guy) vs the server (Rails 3, deployed via Capistrano to a Passenger ā Nginx hosted Ubuntu server, using Mandrill transactional mail service). not a fair fight.
If you already know the deal (or just donāt want to hear me whine) jump there for the Capistrano task thatāll magically expand your environment variables in the config.
First if you follow Mandrillās guide (spoiler: you should not), this is what youād set up in your config/production.rb :
config.action_mailer.smtp_settings = {
:address => "smtp.mandrillapp.com",
:port => 587,
:enable_starttls_auto => true,
:user_name => "MANDRILL_USERNAME",
:password => "MANDRILL_API_KEY",
:authentication => 'login'
}
I know your extra picky security cerebrum already picked up the issue with this. hardcoding the username and password in your config file cannot be a good idea, especially if youāre sharing your code with many people. A better way is to use environment variables.
:user_name => ENV["MANDRILL_USERNAME"],
:password => ENV["MANDRILL_API_KEY"],
you then have to set these variables on your prod server, in /etc/profile for example :
export MANDRILL_USERNAME=foo
export MANDRILL_API_KEY=bar
Now, it canāt be that simple, right ? right. Whereas my mails were not sent, the logger did not give me any errors, so first things first, I figured Iād turn the failed mail deliveries warning on in config/production.rb :
config.action_mailer.raise_delivery_errors = true
if youāre doing this live on your production server (and you should) you have to restart Passenger :
touch tmp/restart.txt
And I could now see a beautiful Net::SMTPServerBusy, Relay access denied error
. First I believed it came from a blocked 587 port, got postfix to work, and everything worked fin, hmmm. Heck, it even worked when I launched a thin server on the prod !
This Stack Overflow entry showed me the way to the problem : Passenger doesnāt set your environment variables when you fire it up.
One solution is to load your variables in the wrapper around Ruby interpreter that Passenger uses (more info on this here). I donāt like this idea much, it feels very hacky, whatāll happen when Iāll upgrade my Ruby for example ?
My solution is to expand the variables in your config file during the deploy (meaning replacing ENV[āMANDRILL_USERNAMEā] with the actuall username value, copied from the environment variable). This amazing SO answer helped a lot, I tweaked it a bit to fit a Capistrano deploy task. It will expand ANY of your environment variables, how great is that ?!
Warning: this is some mad-ass syntax, even the built-in highlighter canāt figure it out. Thereās a LOT of escaping, between sed syntax and rubyās ā¦
desc "Replace environment variables with hardcoded values in config file"
task :replace_env_vars, roles: :app do
run "mv #{release_path}/config/environments/production.rb #{release_path}/config/environments/production.before_sed.rb"
run 'env | sed \'s/[\%]/\\&/g;s/\([^=]*\)=\(.*\)/s%ENV\\\[\\\"\1\\\"\\\]%\"\2\"%/\' > ' + "#{release_path}/script/expand_env_vars.sed.script"
run "cat #{release_path}/config/environments/production.before_sed.rb | sed -f #{release_path}/script/expand_env_vars.sed.script > #{release_path}/config/environments/production.rb"
end
after "deploy:update_code", "deploy:replace_env_vars"
Note : this will only replace strings that use double quotes like ENV[āJESSICA_ALBA_PHONE_NUMBERā]
(I gather you wouldnāt share that piece of intel with any other developer)
Hey, lucky you ! Thereās a last step, and itās so easy it feels good. By default, the SSH session opened by Capistrano doesnāt execute your init scripts (~/.profile, ~/.bashrc, not even /etc/profile/). These are DanM instructions (cheers !) to load them up. Create ~/.ssh/environment
and reput the export instructions, or you can even load your whole /etc/profile
file, even though I have no idea what the security implications are. just donāt if you are ignorant like me.
Tell your SSH server to load this file : add this line in /etc/ssh/sshd_config
:
PermitUserEnvironment yes
and restart it with
sudo /etc/init.d/ssh restart
That should make your day. If it does not, feel free to let out your sorrow in the comments.
- Next: Taskqueues tips