Skip to main content

Configuration mechanics

Learn how configuration works across all services.

How to configure

You can configure our services using configuration files, environment variables, or a combination of both. You define the files to use and in which order. When you start the service the system merges these elements together following precedence rules to build the runtime configuration.

Example setup

Here's an example setup in which we have multiple configuration files and an environment variables file for two services.

procivis-one-deployment
├── conf
│ └── core
│ ├── core_config_1.yml
│ ├── core_config_2.yml
│ ├── core_config_3.yml
│ └── bff
│ ├── bff_config_1.yml
│ ├── bff_config_2.yml
├── env
│ └── core.env
│ └── bff.env
.

If you are starting the services using Docker Compose, you can set command line tags to define which configuration files to use, and define env_file to set which environment files to use:

services:
core:
env_file:
- ./env/.core.env # Defines where to find env variables
command:
- --config # Define a configuration
- ./conf/core_config_1.yml # This one is merged first
- --config # Define another configuration
- ./conf/core_config_2.yml # This one is merged next

At startup the system builds the runtime configuration by starting with the configuration files you specify, in the order you specify them, and continuing with the environment variables files you specify, also in the order you specify, always overriding values with the latest value. Below is a diagram showing the order of precedence with a generic setup that uses multiple configuration and environment variable files:

Example merge

Let's look at an example of a single configuration object — the app entry of Core configuration — being merged across two configuration files and an environment variables file to produce a runtime configuration.

  1. core_config_1

app is used for configuring deployment. In this example we decide to focus the first configuration file on the basic elements of formats and protocols and reserve deployment settings for later in the process:

# does not appear
  1. core_config_2

Next we decide to use this second configuration file to set up our local development environment. Here we configure app for local domain resolution, use a placeholder authentication token, enable all endpoints, and allow insecure HTTP:

app:
databaseUrl: "mysql://core:{{DB_PASSWORD}}@localhost/core"
authToken: "test"
coreBaseUrl: "http://0.0.0.0:3000"
serverIp: "0.0.0.0"
serverPort: 3000
traceJson: false
traceLevel: "debug,hyper=error,sea_orm=info,sqlx::query=error,reqwest=error"
allowInsecureHttpTransport: true
insecureVcApiEndpointsEnabled: true
enableOpenApi: true
enableExternalEndpoints: true
enableManagementEndpoints: true
  1. .core.env

Now we want to define variables for a productive environment, and let us suppose we do this using environment variables. For app this means we want to turn off insecure transport and endpoints, set a real authentication token, point to our live domain, and set the server's IP address:

ONE_app__authToken="{{32-BYTE-RAND-HEX}}"
ONE_app__coreBaseUrl="http://example.domain"
ONE_app__serverIp="1.234.5.678"
ONE_app__allowInsecureHttpTransport="false"
ONE_app__insecureVcApiEndpointsEnabled="false"
  1. Resulting runtime configuration:

If the Docker Compose looks like this:

services:
core:
env_file:
- ./env/.core.env # Defines where to find env variables
command:
- --config # Define a configuration
- ./conf/core_config_1.yml # This one is merged first
- --config # Define another configuration
- ./conf/core_config_2.yml # This one is merged next

then the resulting runtime configuration of app is:

app:
databaseUrl: "mysql://core:{{DB_PASSWORD}}@localhost/core"
authToken: "{{32-BYTE-RAND-HEX}}"
coreBaseUrl: "http://example.domain"
serverIp: "1.234.5.678"
serverPort: 3000
traceJson: false
traceLevel: "debug,hyper=error,sea_orm=info,sqlx::query=error,reqwest=error"
allowInsecureHttpTransport: false
insecureVcApiEndpointsEnabled: false
enableOpenApi: true
enableExternalEndpoints: true
enableManagementEndpoints: true

Environment variable pathing

To override any configuration value using environment variables, convert the YAML path to environment variable format using these rules:

  1. Add the prefix ONE_

  2. Convert dots to double underscore: replace each . in the flattened path with __

  3. End the path with an =

Given this configuration:

app:
authToken: "abc123"
keyStorage:
AZURE_VAULT:
params:
private:
vaultUrl: "https://example.com"

The corresponding environment variables would be:

ONE_app__authToken="abc123"
ONE_keyStorage__AZURE_VAULT__params__private__vaultUrl="https://example.com"

Environment customization

Deployments can follow one of several patterns.

  1. Environment-specific config files:

procivis-one-deployment
├── config
│ └── base.yml # Shared defaults
│ └── development.yml # Development overrides
│ └── staging.yml # Staging overrides
│ └── prod.yml # Production overrides
.
  1. Single config + environment variables:

procivis-one-deployment
├── config.yml
├── env
│ └── development.env
│ └── staging.env
│ └── prod.env
.
  1. Hybrid approach

procivis-one-deployment
├── config
│ └── base.yml
│ └── development.yml
│ └── staging.yml
│ └── prod.yml
├── env
│ └── core.env
│ └── bff.env
.

With any of these setups you can run a Docker Compose for each environment:

docker-compose.dev.yml       # Development overrides
docker-compose.staging.yml # Staging overrides
docker-compose.prod.yml # Production overrides