Environment Variables and Why You Shouldn't Use Them

Using environment variables is a somewhat common practice during Development but it is actually not a healthy practice to use with Production. While there are several reasons for this, one of the main reasons is that using environment variables can cause unexpected persistence of variable values. For example:

When using environment variables with Unicorn, hot restarts don't pick up changes to environment variables. This is because the previous master process starts the new master process, which retains the same environment variables. To pick up new environment variable values, you must fully terminate the old Unicorn master process and start a new one using a wrapper script (which sets the environment variables to the new values). Unfortunately, this causes downtime, so deploys only use the hot restart method.

While choosing between no downtime and stale environment variables is problematic, the bigger issue is using environment variables in the first place.

Note: Ruby programmers will be familiar with the using the "ENV" hash to access environment variables; other languages may use different methods, however the concept is the same.

Environment variables not recommended for every situation

Environment variables provide a good way to set application execution parameters that are used by processes that you do not have direct control over. However, environment variables should not be used for configuration values within your own dynamic applications.

Environment variables are global variables. A best practice for most programming languages and design paradigms is to avoid the use of global variables. Furthermore, there is a security risk in setting tokens in environment variables. All gems used and sub-processes launched have access to all variable values, so if any of them log or transmit the output of 'export' or ‘env’, your private data can be exposed.

Another challenge to environment variables is scrubbed environments. For security reasons, cron and monit don't start processes with the environment variables provided by the user's login profile. If you're relying on environment variables, it can lead to frustration and confusion when the process works when you start in manually, but not when cron or monit does.

Instead of environment variables, we recommend that you either use a YAML configuration file or a shared data source.

YAML files as an alternative to environment variables

Instead of environment variables, it’s best practice to handle configuration parameters in a YAML file that is stored in /data/<app>/shared/config and linked to /data/<app>/current/config during the deploy process, using the before_migrate.rb deploy hook. For more information, see Use Deploy Hooks.

Tip: Try using the settingslogic gem to provide the YAML settings logic. It neatly wraps up all the functions needed. Restarting the app will pick up the changes and can be done gracefully (ie using a hot restart with no downtime).

Shared data source as an alternative to environment variables

If you find yourself needing to change these values often, we recommend a shared data source, such as a parameters table on your existing DB; if you need something faster, we recommend a Redis or Riak source. A custom recipe or admin tool can be written to populate these values and changed as needed without deploying or restarting the app.

Ensure data availability

One of the nice things about environment variables is that they are always available, right from the start. However, you can have the same benefits when using loaded data. You just need to read it early using initializers. This article shows several ways to accomplish this. We often recommend using config/environment.rb to load the config before Rails invokes the other initializers. However, the recommended method may vary depending on your situation.

Comments

0 comments

Article is closed for comments.