You’re ready to launch your first production app, and it’s time to get it talking to some external services. You still have to get everything hooked up. So what’s the best way to configure your services in production, without making things more complicated on your dev machine?

Set up your Environment

To configure production apps, today’s best practice is to use environment variables (those ENV["REDIS_HOST"]-looking things).

But why?

  • It’s harder to accidentally commit your production keys.

    If you’re not paying attention, you might git push a file with important secret keys in it. And that could be an expensive mistake.

  • Configuration is what environment variables are there for.

    Environment variables are a common way to configure apps on almost every kind of system. Many other programs (like Ruby) use environment variables for configuration, so it only makes sense to try in your own app.

  • Environment variables are easy to set up in production.

    Heroku has a a web UI and a command line tool for easily setting environment variables. And if you’re building your own server, server management tools like Chef and Docker make setting environment variables easy.

What does it look like on the Rails side?

This is how an app that depends on environment variables could configure itself:

  host: <%= ENV["MY_SERVICE_HOST"] %>
  port: <%= ENV["MY_SERVICE_PORT"] %>
my_service_config = Rails.application.config_for(:my_service)

my_service =["host"], my_service_config["port"])

The initializer uses Rails 4.2’s config_for method to find the right .yml file and pick the right environment.

Then, config_for runs the ERB code inside my_service.yml, and grabs MY_SERVICE_HOST and MY_SERVICE_PORT out of the environment. It passes those values along to MyService.

You could also just have the initializer read from ENV["MY_SERVICE_HOST"] directly. But I prefer to keep them in .yml files, for reasons you’ll see in a minute.

Your app’s configuration in development

Environment variables are fine for production. But once you set up your production config, how do you handle development and test mode?

You have a few options. But I usually follow the convention in Rails’ config/secrets.yml: use environment variables in production, and hardcode non-secret values in development and test.

With the development and test environments, config/my_service.yml could look like this:

  host: <%= ENV["MY_SERVICE_HOST"] %>
  port: <%= ENV["MY_SERVICE_PORT"] %>
  host: localhost
  port: 8081
  host: localhost
  port: 8081

Awesomely enough, the initializer can stay exactly the same. The values in this file will be used in the development and test environments, and the production environment will get its values from the environment variables.

But why would you hardcode these values?

  • The configuration values are easier to see and change.

    You can tweak your config as you experiment with new features, which is something you want in development, but not so much in production.

  • It’s easier for someone new to get started.

    If all the sample config you need is checked into your git repository, a new dev just has to clone and run your app. They won’t need to muck around with setting just the right values to get the app working.

  • You don’t have to worry about conflicting environment variables.

    You’ll probably work on more apps on your dev machine than you’ll ever deploy to a single production machine. If you used system-wide environment variables to configure all those apps, there’s a good chance two of them will stomp on each other.

So, try using environment variables in production, and hardcoded .yml config in development. It’s easy, it’s readable, and Rails has built-in support for dealing with exactly those kinds of config files.

Another option for development

There’s another way to handle configuration in development mode: dotenv. It looks neat, but I haven’t tried it in an app of my own yet.

With dotenv, you can put environment variables in a file named .env in your Rails app’s root directory, and those values will get picked up by your app. This is nice, because your development environment acts more like your production environment. That’s a good way to avoid bugs that only ever happen in production.

It’s something I’ll try someday. But for now, I haven’t found anything more convenient than .yml and config_for.

Most production apps need some kind of configuration. So when you deploy your next app, try using .yml files, populated by environment variables in production, to configure it. You’ll get the flexibility, the simplicity, and the reliability you’re hoping for.

Do you have a different way you like to configure your production apps? Leave a comment, I’d love to hear about it!

Did you like this post? You should read these:

Finished another Rails tutorial and still don’t know how to start?

Have you slogged through the same guide three times and still can't retain enough to write apps on your own?

In my free 7-part course, you’ll discover the fastest way to learn and remember new Rails ideas, so you can use them when you need them. And you'll learn to use what you already know to build your own Rails project.