Rails Version 4.2+
Last Updated On February 21st, 2015 - 5:00pm EST
Notes Works with other versions of Rails with small modifications.

Introduction

As you are probably aware, Ruby on Rails has 3 distinct environments it runs in. The first, development, is used for writing application code. It typically has full debugging support enabled along with a number of useful tricks that makes it easier to build the application. The next environment, test, is used for running unit/behavioral testing. The final environment, production, is used when the application is running on a production web server. It's optimized for speed and almost all debugging features are turned off.

In this article we will show you how to create your own customized environment. For the purposes of this example we will create a staging environment. Staging environments typically mimic production as close as possible and allows us to quickly fix issues that may arise in an actual production environment. Let's get started.

Creating a Custom Environment

To create a custom Rails environment we must perform a couple of different steps. First, we must create a config file for the new Rails environment. Next, we must modify our secrets.yml and add a secret key base for the new environment. Next, we must modify our database.yml to add database credentials for the new environment. Next, we may have to perform additional steps such as compiling assets if asset debugging is disabled. Finally, we have to run the Rails server and or console under the new environment.

To get started, let's first create a copy of /config/environments/production.rb. We will call this file staging.rb and it will go in the same path.

config/environments/staging.rb:

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

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Enable Rack::Cache to put a simple HTTP cache in front of your application
  # Add `rack-cache` to your Gemfile before enabling this.
  # For large-scale production use, consider using a caching reverse proxy like
  # NGINX, varnish or squid.
  # config.action_dispatch.rack_cache = true

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?

  # Compress JavaScripts and CSS.
  config.assets.js_compressor = :uglifier
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true

  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  # config.force_ssl = true

  # Use the lowest log level to ensure availability of diagnostic information
  # when problems arise.
  config.log_level = :debug

  # Prepend all log lines with the following tags.
  # config.log_tags = [ :subdomain, :uuid ]

  # Use a different logger for distributed setups.
  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)

  # Use a different cache store in production.
  # config.cache_store = :mem_cache_store

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.action_controller.asset_host = 'http://assets.example.com'

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false
end

Now we need to modify our secrets.yml file and add a new entry for staging. To do this, first we will need to set a new environment variable containing our secret. Run the command below to generate that secret, then copy it to the clipboard.

Terminal Commands:

rake secret

Next, open up the .bash_profile file in your home directory. In OS-X you can typically type open -e ~/.bash_profile and it will open up the file in TextEdit. If you are using Linux, you can use your favorite text editor (example: vim ~/.bash_profile) to edit the file. Once the file is open, add the line listed below to the bottom of the file, replacing "YOUR SECRET KEY" with the secret key you copied earlier.

~/.bash_profile:

export SECRET_KEY_BASE="YOUR SECRET KEY"

Then, type source ~/.bash_profile to load the changes you made.

Next we need to modify our config/secrets.yml file. Open up your secrets.yml file and modify it so that it looks like the code listed below.

config/secrets.yml:

# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

test:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

You'll notice that we not only added an entry for staging, but we also switched the other environments to use the secret key base stored in our environment variable. This file could be cleaned up even more, but for the purposes of this article we will leave as is.

Next, we need to create an entry in our database.yml that tells Rails where to get the database config. In this example application we are using SQLite 3 so we simply copy paste productions and change it from production to staging.

config/database.yml:

# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

staging:
  <<: *default
  database: db/staging.sqlite3

production:
  <<: *default
  database: db/production.sqlite3

Now if you start your rails server (use rails s -e staging to specify the staging environment) and try to use your app you'll notice that the stylesheets and javascript files aren't working properly. This is because we need to compile our assets. Close your rails server and run the following command to compile your assets.

Terminal Command:

rake assets:precompile RAILS_ENV=production

If you start your rails server, you'll notice that the scripts and stylesheets are now working as they should. Great!

What if we want to conditionally load a gem for staging? It's pretty simple actually, simply use the :staging rails group like below.

Gemfile:

group :development, :test, :staging do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'

  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

Then run the following command to install the staging gems.

Gemfile:

RAILS_ENV=staging bundle

Final Notes

  1. For this staging example, modifying pages won't be reflected on a page reload, instead you'll have to stop and restart. the Rails Server.
  2. You can also optionally set an environment variable called RAILS_ENV which corresponds to the Rails environment you wish to use. This saves some typing.
  3. You can open the rails console by typing rails c <environment>. For example: rails c staging.

That's it! Thanks for reading!