Config.js vs config.production.json

So I’m using Docker-Compose to setup Ghost with the rest of my stuff. Finally have a working setup using a container for ghost, a container for mysql, a container for nginx, a container that handles my backups, and using s3 for image storage. I do a custom build from the ghost:latest container to add in my s3 bits.

What caught me completely off guard was that I couldn’t use a config.js file for my configuration. This would’ve allowed me access to environment variables to dynamically build my config. I had to use the config.production.json file (which hated my database config, that I had to leave in a separate .env file).

Looking over the docs this looks like a recent change. Just wondering if I could’ve done something differently? Anyone else run into this?

2 Likes

Just ran into this. Looks like the offending code is in ghost/core/server/config/index.js. It uses nconf to build out the config, and uses nconf.file in stead of require(myConfigFile). That means that JS in a config file wouldn’t be executed.

Here’s my workaround, which I run before booting ghost:
require(‘dotenv’).config({silent: true})

const     fs = require('fs')
const path = require('path')

/**
 * Ghost only accepts JSON files for configuration, which is really annoying
 * because you can't dynamically set paths or add secrets. So, we'll build
 * out our configs here, write them, and let ghost find them.
 */
const env = process.env.NODE_ENV || 'development'

const allConfigs = {
  development: {
    "database": {
        "client": "sqlite3",
        "connection": {
            "filename": path.resolve(__dirname, 'ghost-dev.db')
        },
        "debug": false
    },
    "url": `${process.env.BASE_URL}/learn`,
    "paths": {
      "contentPath": path.resolve(__dirname, 'ghost-content')
    }
  },
  production: {}
}

const myConfig = allConfigs[env]

fs.writeFileSync(`config.${env}.json`, JSON.stringify(myConfig, null, 4), 'utf8')

Short version:

In Ghost 0.x, config was provided via a single config.js file with keys for each env.
In Ghost 1.0, config is provided via multiple config..json files, with built-in support for supplying both environment variables & command line arguments instead of values in the file.

The documentation is here: https://docs.ghost.org/docs#section-running-ghost-with-config-env-variables.


Long version:

We constantly ran into issues with our made-up config format in Ghost 0.x. This format caused a lot of confusion as users would often edit the wrong section. Also it resulted in inconsistent naming conventions in use across the community for environment variables, as you’d see people change the config file to include process.env.MY_NAME_HERE. Finally we ran into problems with the files being big, easy to break, and hard to tell what was configurable and what wasn’t (we override some properties behind the scenes).

In Ghost 1.0 we solved these problems by switching to using nconf, and leveraging pre-built tools for handling multiple configuration files. There are now clearly separated defaults and overrides. Each individual file is small and in an easy to validate format.

It’s also far more flexible, because you can supply absolutely any config value as either an environment variable or an argument, using nconf’s naming convention as outlined in the docs: https://docs.ghost.org/docs#section-running-ghost-with-config-env-variables.

The biggest issue I think we have here is this option is a bit buried in the docs, and the naming convention is tricky to guess. We’re waiting on nconf to ship first-class upper-case env var support so that we can add this cleanly: conf.get('UPPERCASE') with lowerCase option used to work in 0.8.4 but not in 0.8.5 · Issue #271 · indexzero/nconf · GitHub.


Hope this helps you get your configuration setup the way you’d like.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.