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:
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:
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!