Daniel Hoelbling-Inzko talks about programming

Securely managing database.yml when deploying with Capistrano

Posted by Daniel Hölbling on September 25, 2011

The more I venture into Ruby land the more magic Unicorns I find on the way. The wonders of SSH still seem totally outlandish to someone used to do deployments by RDPing into a server and xcopying a directory structure into your IIS folder.

But here I am and learning the ways of Capistrano and how deployments to multiple servers really should work.

Naturally I ran into issues I'll detail a bit later, but one of my major problems with my Rails deployment was the different database.yml between my production and my dev environment. Since the repository is in a shared location I could not put the production server mysql password into the config as it would be available to anyone with read access to the repository. This may be something you can get away with in a corporate environment, but if you plan on ever open-sourcing your project you should make sure you don't put production passwords into your repository :).

My solution to that problem is quite simple: I ssh'd into my server and put a "production" database.yml into the home directory of my deployment user and added the following task to my Capfile:

namespace :db do
  task :db_config, :except => { :no_release => true }, :role => :app do
    run "cp -f ~/database.yml #{release_path}/config/database.yml"

after "deploy:finalize_update", "db:db_config"

The after statement tells Capistrano to run the db_config task right before finishing the code update, but before running any migrations in case you run cap deploy:migrations (capistrano process). And during every deployment I overwrite the database.yml from the repo with the one on the server.

I also added a assets:precompile task since Capistrano won't run the precompilation of Rails assets out of the box (you need RVM integration for this though):

  task :precompile, :role => :app do
    run "cd #{release_path}/ && rake assets:precompile"
after "deploy:finalize_update", "deploy:precompile"

Et voilá: I can now run cap deploy:migrations from my dev machine and it will automatically connect to my release server, pull the code out of the git repository, compile the assets and migrate the database to a new version. And it will even roll-back to the old version if something goes wrong along the way.

Ps: I also struggled at one point with the SSH keys for the git repository. Since the deployment user on the server has no own private key I was inclined to generate one and add it to my git server's allowed keys list. But that's apparently the wrong way to go about things. The right thing to do here is to simply enable agent forwarding so the server will forward any questions about keys to your dev machine that should have the appropriate set of keys available.

ssh_options[:forward_agent] = true

Filed under rails, ruby, tools
comments powered by Disqus