Use Rails 4 secrets.yml for database config
Don't spread your configuration across multiple files. Here's how to leverage secrets.yml for your database config too.
- Rename
config/secrets.yml
toconfig/secrets.yml.example
- Commit
cp config/secrets.yml.example config/secrets.yml
- Update
.gitignore
to includeconfig/secrets.yml
-
Add the a database section to
config/secrets.yml
Note the explicit use of symbols.development: database: :host: localhost :name: my_project_development :username: root :password: password # add database section to all envs...
-
Update
config/database.yml
to look like something like this.default: &default host: <%= Rails.application.secrets[:database][:host] %> adapter: postgresql encoding: UTF8 database: <%= Rails.application.secrets[:database][:name] %> pool: 10 reaping_frequency: 30 username: <%= Rails.application.secrets[:database][:username] %> password: <%= Rails.application.secrets[:database][:password] %> development: *default test: *default production: *default
Written by Nathan Hopkins
Related protips
14 Responses
But why ? We have database.yml for that propuse.
This is what I like using Dotenv for, configuration files reference environment variables then they can be configured on the server or a .env file.
I was just chatting on Twitter about this exact topic. What timing. The advantages of this are stubbing and working with a clear API. Having ENV variables spread throughout the codebase can be hard to reason with. I'd like it if it were possible to add validation rules though ala a Yaml-backed ActiveModel record to mitigate malicious or simply invalid ENV variables.
I wouldn't limit this to Database configuration though.
@dvito I think the advantage of this vs just modifying database.yml is single source of truth though.
Excellent work.
We use this technique for the simplicity of having a single source of truth for all app configuration that's easy to manage & reason about.
We deploy with our apps with Docker & use Chef to provision the metal.
- The canonical source of secrets.yml lives in our Chef setup
- Chef copies the secrets.yml file to all servers in the cluster
- Docker containers mount the directory where secrets.yml is kept
- On startup, the containers copy secrets.yml to APP/config/secrets.yml then start the app
It's working nicely for us & removes the cognitive load of having configs spread across multiple files & environment variables. YMMV
You can load all your ENV vars to the application in the same place: But I tend to agree with 12 factors in that things should be based on ENV vars so that you can deploy without worrying about config files. This also works really well from things like Docker.
Symfony2 uses something called Incenteev ParameterHandler which generates config (yml files) on the fly. I've ported it to ruby: https://github.com/khasinski/fillparams
You basically ship .dist (like .sample) files and during build it will ask for missing parameters. Useful for development (new developer doesn't have to guess where to look for config files and what else is there to configure). It copies missing parameters when you run it in non-interactive environment.
I ran into a lot of trouble getting this working in docker until I finally read all of the details around how nginx deals with environmental variables. In my scenario I could see all of my variables but when nginx started passenger, passenger would default to a local database and not see the environmental variables that I was passing.
https://github.com/phusion/passenger-docker#setting-environment-variables-in-nginx
You'll want to change out the secrets in secrets.yml unless you are ok with those going out to the interweb
how to use secret.yml to production mode if you add that in .gitignore?
Do you still use this approach? I'm trying it in Rails 4.2 and it seems like the secrets aren't available at the time the database.yml is parsed. I'm going to look into it some more but wanted to check in with you as well.
Whoops - that was my mistake. I'm dynamically building the secrets.yml file and was doing it in an initializer. Initializers fire after database.yml gets parsed so the secrets weren't available to database.yml.
i will try it