Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve error "Missing `secret_key_base` for 'production' environment" (Rails 4.1)

I had the same problem and solved it by creating an environment variable to be loaded every time I logged in to the production server, and made a mini-guide of the steps to configure it:

I was using Rails 4.1 with Unicorn v4.8.2 and when I tried to deploy my application it didn't start properly and in the unicorn.log file I found this error message:

app error: Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml` (RuntimeError)

After some research I found out that Rails 4.1 changed the way to manage the secret_key, so if you read the secrets.yml file located at exampleRailsProject/config/secrets.yml you'll find something like this:

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

This means that Rails recommends you to use an environment variable for the secret_key_base in your production server. In order to solve this error you should follow these steps to create an environment variable for Linux (in my case Ubuntu) in your production server:

  1. In the terminal of your production server execute:

    $ RAILS_ENV=production rake secret
    

    This returns a large string with letters and numbers. Copy that, which we will refer to that code as GENERATED_CODE.

  2. Login to your server

    • If you login as the root user, find this file and edit it:

      $ vi /etc/profile
      

      Go to the bottom of the file using Shift+G (capital "G") in vi.

      Write your environment variable with the GENERATED_CODE, pressing i to insert in vi. Be sure to be in a new line at the end of the file:

      $ export SECRET_KEY_BASE=GENERATED_CODE
      

      Save the changes and close the file using Esc and then ":x" and Enter for save and exit in vi.

    • But if you login as normal user, let's call it "example_user" for this gist, you will need to find one of these other files:

      $ vi ~/.bash_profile
      $ vi ~/.bash_login
      $ vi ~/.profile
      

      These files are in order of importance, which means that if you have the first file, then you wouldn't need to edit the others. If you found these two files in your directory ~/.bash_profile and ~/.profile you only will have to write in the first one ~/.bash_profile, because Linux will read only this one and the other will be ignored.

      Then we go to the bottom of the file using Shift+G again and write the environment variable with our GENERATED_CODE using i again, and be sure add a new line at the end of the file:

      $ export SECRET_KEY_BASE=GENERATED_CODE
      

      Having written the code, save the changes and close the file using Esc again and ":x" and Enter to save and exit.

  3. You can verify that our environment variable is properly set in Linux with this command:

    $ printenv | grep SECRET_KEY_BASE
    

    or with:

    $ echo $SECRET_KEY_BASE
    

    When you execute this command, if everything went ok, it will show you the GENERATED_CODE from before. Finally with all the configuration done you should be able to deploy without problems your Rails application with Unicorn or some other tool.

When you close your shell and login again to the production server you will have this environment variable set and ready to use it.

And that's it! I hope this mini-guide helps you solve this error.

Disclaimer: I'm not a Linux or Rails guru, so if you find something wrong or any error I will be glad to fix it.


I'm going to assume that you do not have your secrets.yml checked into source control (ie. it's in the .gitignore file). Even if this isn't your situation, it's what many other people viewing this question have done because they have their code exposed on Github and don't want their secret key floating around.

If it's not in source control, Heroku doesn't know about it. So Rails is looking for Rails.application.secrets.secret_key_base and it hasn't been set because Rails sets it by checking the secrets.yml file which doesn't exist. The simple workaround is to go into your config/environments/production.rb file and add the following line:

Rails.application.configure do
    ...
    config.secret_key_base = ENV["SECRET_KEY_BASE"]
    ...
end

This tells your application to set the secret key using the environment variable instead of looking for it in secrets.yml. It would have saved me a lot of time to know this up front.


Add config/secrets.yml to version control and deploy again. You might need to remove a line from .gitignore so that you can commit the file.

I had this exact same issue and it just turned out that the boilerplate .gitignore Github created for my Rails application included config/secrets.yml.


This worked for me.

SSH into your production server and cd into your current directory, run bundle exec rake secret or rake secret, you will get a long string as an output, copy that string.

Now run sudo nano /etc/environment.

Paste at the bottom of the file

export SECRET_KEY_BASE=rake secret
ruby -e 'p ENV["SECRET_KEY_BASE"]'

Where rake secret is the string you just copied, paste that copied string in place of rake secret.

Restart the server and test by running echo $SECRET_KEY_BASE.


While you can use initializers like the other answers, the conventional Rails 4.1+ way is to use the config/secrets.yml. The reason for the Rails team to introduce this is beyond the scope of this answer but the TL;DR is that secret_token.rb conflates configuration and code as well as being a security risk since the token is checked into source control history and the only system that needs to know the production secret token is the production infrastructure.

You should add this file to .gitignore much like you wouldn't add config/database.yml to source control either.

Referencing Heroku's own code for setting up config/database.yml from DATABASE_URL in their Buildpack for Ruby, I ended up forking their repo and modified it to create config/secrets.yml from SECRETS_KEY_BASE environment variable.

Since this feature was introduced in Rails 4.1, I felt it was appropriate to edit ./lib/language_pack/rails41.rb and add this functionality.

The following is the snippet from the modified buildpack I created at my company:

class LanguagePack::Rails41 < LanguagePack::Rails4

  # ...

  def compile
    instrument "rails41.compile" do
      super
      allow_git do
        create_secrets_yml
      end
    end
  end

  # ...

  # writes ERB based secrets.yml for Rails 4.1+
  def create_secrets_yml
    instrument 'ruby.create_secrets_yml' do
      log("create_secrets_yml") do
        return unless File.directory?("config")
        topic("Writing config/secrets.yml to read from SECRET_KEY_BASE")
        File.open("config/secrets.yml", "w") do |file|
          file.puts <<-SECRETS_YML
<%
raise "No RACK_ENV or RAILS_ENV found" unless ENV["RAILS_ENV"] || ENV["RACK_ENV"]
%>

<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
          SECRETS_YML
        end
      end
    end
  end

  # ...

end

You can of course extend this code to add other secrets (e.g. third party API keys, etc.) to be read off of your environment variable:

...
<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  third_party_api_key: <%= ENV["THIRD_PARTY_API"] %>

This way, you can access this secret in a very standard way:

Rails.application.secrets.third_party_api_key

Before redeploying your app, be sure to set your environment variable first: Setting SECRET_KEY_BASE in Heroku Dashboard

Then add your modified buildpack (or you're more than welcome to link to mine) to your Heroku app (see Heroku's documentation) and redeploy your app.

The buildpack will automatically create your config/secrets.yml from your environment variable as part of the dyno build process every time you git push to Heroku.

EDIT: Heroku's own documentation suggests creating config/secrets.yml to read from the environment variable but this implies you should check this file into source control. In my case, this doesn't work well since I have hardcoded secrets for development and testing environments that I'd rather not check in.