Configuration for Rails, the Right Way

Posted on by in Everything Else

I still see people promoting various gems and plugins to handle miscellaneous configuration elements for your application. One little known secret is that Rails 3 allows you to define your own configuration elements trivially.

In this case, I wanted to use the nifty wkhtmltopdf utility to create a PDF. I was able to call the binary just fine with Homebrew on OSX but found that I had to use a custom binary checked into git for our production environment on Heroku. So I created a configuration variable to store where wkhtmltopdf could be found in the current environment.

First, we define a default value for all environments in config/application.rb:

module Configurator
  class Application < Rails::Application
    # By default, let OSX resolve the path to the binary
    config.wkhtmltopdf = "wkhtmltopdf"

Then we override the default setting as necessary in config/environments/:

Configurator::Application.configure do
  # Settings specified here will take precedence over those in config/application.rb

  # Point Heroku explicitly to the binary we need to use
  config.wkhtmltopdf = "#{Rails.root}/bin/wkhtmltopdf"

Lastly, we access the configuration element in our code:

  cmd = [Configurator::Application.config.wkhtmltopdf, url, tmpfile.path]

Yes, that’s it. Just use Rails’s environment support and config to store your own configuration elements. They’re trivial to set, trivial to access and require no third-party gems or custom text files.


  Comments: 26

  1. One issue with this is that I find myself wanting to group my config elements together. E.g., say I’m writing an app that uses Urban Airship, and I have my own client object that encapsulates their API. I want to configure our app key and secret for dev and prod environments. Using this method I *could* do:

    config.urban_airship_application_key = “abcdefg”
    config.urban_airship_master_secret = “0123456”

    But what I’d rather do is something mirroring, e.g., ActionMailer configuration:

    config.urban_airship.application_key = “abcdefg”
    config.urban_airship.master_secret = “0123456”

    Yeah, I know, it’s just a dot instead of an underscore, but conceptually it’s tidier.

    That doesn’t work as is, so what I’ve ended up doing is defining cattrs on classes that I want to configure, and then doing this:

    config.after_initialize do
    UrbanAirshipClient.application_key = “abcdefg”
    UrbanAirshipClient.master_secret = “0123456”

    Then inside the class, I define the cattrs and use them:

    class UrbanAirshipClient
    cattr_accessor :application_key, :master_secret

    # code just refers to @@application_key and @@master_secret

    Keeps config params bundled with the classes they affect.

    That’s my solution so far; would love to hear better/alternate solutions.

  2. When I started a green-field Rails 3 project a year ago, everyone was explaining how Configuration was an enormous and scary problem to be solved. I was getting pitched everything from Gems, to NoSQL DB’s, to RDBMS’s, etc. In looking at what they had in place, I mentally pieced together the chronology of their former solutions. In its first incarnation, years back, it was simply a SQL table of key-value pairs. (I was pleased to see key-value approach rather than eternally growing columns.) The problem was there was no RAM persistence. This was getting hit every time a Config value was referenced in code. Some time elapses, and someone realized how crippling that was to their app, and decided to solve the problem with MemCacheD! [MemCacheD itself was not the problem. It was how it was incorrectly applied.] They loaded all the config values from SQL into MemCacheD upon app startup. They implement yet-another, entirely separate persistence mechanism to manage and maintain. This was horribly confusing to every single dev that ever worked on the project. Naturally, new devs would find themselves changing values in the Config DB, but seeing no change after they restarted the application. I can’t count the # of hours wasted on that alone. That system is still in place, and I witnessed the same thing happen to 2 new devs this year.

    Anyway, to all these various “solutions” people were pitching me, I replied, “The most reliable, leanest, and fastest data-storage mechanism already exists in every programming language…. [dramatic pause] RAM, and an object.”

    All I could think was, “Are developers aware that one prime function of an object is to hold data?” They’ve done that since objects were invented in the 80’s. Even without objects, RAM itself has been the easiest thing to access since the Eniac.

    I wanted things in YAML so it was a little more human readable, and could be edited with the ease of editing a file. I wanted to make it polymorphic based on enviro. And I wanted to have dynamic portions [Rails.root, etc], so I pass it through the ERB parser. Here’s config/config.yml:

    # All environments:
    defaults: &defaults
    some_key: some_value
    another_key: another_value
    upload_dir: /media

    # Enviro-Specific:
    <<: *defaults
    www_user: _www
    www_group: adm
    upload_dir: /public/uploads

    <<: *defaults
    www_user: www-data
    www_group: admin

    Then I wrote a tiny initializer to load this and make it a global object:

    class Settings
    raw_config ="#{::Rails.root.to_s}/config/config.yml")
    erb_config =
    settings = YAML.load(erb_config)[::Rails.env]

    if settings
    settings.each { |name, value|
    instance_variable_set("@#{name}", value)
    self.class.class_eval { attr_reader name.intern }

    That’s it. Now all config values are available everywhere as:


    Sits in RAM. Super-fast. Loaded once. No MemCacheD to magically know exists and somehow know to restart. No SQL.

  3. I’ve been doing much the same thing, but rather than creating my own Configurator module, I used the existing Rails app’s module, so I get:


    Feels slightly cleaner.

    • I prefer an extra level to make collisions with future Rails development less probable:


      Think of the “x” as a prefix similar to extra mail headers.

      • …/gems/railties-3.1.3/lib/rails/railtie/configuration.rb:78:in `method_missing’: undefined method `x’ for # (NoMethodError)

  4. This doesn’t feel clean to me for a couple of reasons:
    * Everything else you configure in application.rb is kinda standard Rails configuration, so I wouldn’t want to include my application-specific stuff in there.
    * I want to be able to specify passwords and other sensitive information and not check those in to my version control system.
    * I prefer defining my configs in YAML rather than code, so even non-technical people could edit them.
    * I don’t want my configs to be split up in several files.

    I wrote my own app config (like Configatron) a while ago and have been improving and reusing it ever since. It sits in lib/app_config.rb and loads config/app_config.yml.

  5. I like using Settings Logic ( I put all config into a YAML file (which is setup with dev, test and prod blocks) and then load it up. Then I can just call “Settings.facebook_app_id”, or whatever.

    I like this b/c all of my config is in one file that I can choose to keep out of version control if I need to. I can even load more than one file if I need to.

    And, adding a single gem as a dependency isn’t an issue…my normal rails apps use lots of gems anyways.

    However, I do appreciate Mike pointing out the ability to use the built-in Rails config, never thought about that approach :)

  6. What would you do with a configuration string that contains non-ascii characters, e.g. the name of the CEO? I guess you could set “encoding: utf-8” for the application.rb file but I don’t like the idea of having files with different encoding specifications in my project.

  7. Hi Mike,
    Good call. I’ve tried this approach and liked it, except for dealing with accidental code checkins of secret api keys etc. Like others, I too have written my own gem, which has worked well for my team.

    • Chris, I love the mapping in Constantinople from top-level namespace to yml filename: -> config/foo.yml

      Simple but powerful idea.

  8. I feel having app-specific configs available through the user environment under which the app runs makes a lot more sense. Heroku got this right. If you’re using Chef or Puppet to prep servers for app deployment (you are, right?), that’s the only place where you want the actual values stored. An app is no longer this single Rails instance which contains all its configs, but a myriad of small services that work independently and require to know only so much. With this approach, if you change a value and re-cook the servers, they make the new values available instantly, not even a USR2 required.

  9. I used the example from iq9. That worked pretty good. But the settings.rb in the “config/initializers” directory is called AFTER application.rb, environment.rb, development.rb, test.rb and production.rb. If I want to use the in one of this files, it didn’t worked. I just put the Settings class directly into the “environment.rb” before “MyApp::Application.initialize!”. In that way I can use the in all environments files, too.

  10. Nice article! Thanks Mike.

  11. スーパーコピーブランド格安販売店はこちらへ!品々の激安価格に持ったスーパーコピーブランド 代引きの新作はお客様に提供されます。安心、迅速、確実、お客様の手元にお届け致しま

    スーパーコピーブランド格安販売店はこちらへ!品々の激安価格に持ったスーパーコピーブランド 代引きの新作はお客様に提供されます。安心、迅速、確実、お客様の手元にお届け致します。★弊社は9年の豊富な経験と実績を持っております。★一流の素材を選択し、精巧な作り方でまるで本物のようなな製品を造ります。★品質を重視、納期も厳守、お客様第一主義を貫きは当社の方針です。★驚きの低価格で商品をお客様に提供致します!★早速に購入へようこそ!

  12. 人気スーパーコピーブランド時計激安通販専門店私達は長年の実体商店の販売経験を持って、先進とプロの技術を持って、高品質のスーパーコピー時計づくりに 取り組んでいます。最高品

    財布コピー、バッグコピー、腕時計コピー、ベルトコピー靴コピーネックレスコピー、手帳コピー、小物コピー、SS品、N品、価格激安、品質の保証,2015人気ブランド偽物,歓迎光臨楽天★送料無料(日本全国)典雅気質!シャネルバッグCHH67723(*^^*)11月シャネル手作り新作(*^^*)時流の先端快適美品!シャネルブーツCH783283四季向け「 シャネル靴」最高な選択!ブランドコピー 代引きコピーブランド 代引きスーパーコピー 代引きスーパーコピーブランドバッグルイヴィトン コピーシャネル コピー
    人気スーパーコピーブランド時計激安通販専門店私達は長年の実体商店の販売経験を持って、先進とプロの技術を持って、高品質のスーパーコピー時計づくりに 取り組んでいます。最高品質のロレックス時計コピー、カルティエ時計コピー、IWC時計コピー、ブライトリング時計コピー、パネライ時計コピー激安販売中商品の数量は多い、品質はよい。海外直営店直接買い付け!★ 2015年注文割引開催中,全部の商品割引10% ★ 在庫情報随時更新! ★ 実物写真、付属品を完備する。 ★ 100%を厳守する。 ★ 送料は無料です(日本全国)!★ お客さんたちも大好評です★ 経営方針: 品質を重視、納期も厳守、信用第一!税関の没収する商品は再度無料にして発送します

Your feedback