Configuration & Secrets

[!NOTE] This module explores the core principles of Configuration & Secrets, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.

1. The 12-Factor App: Config

One of the core principles of modern software engineering is strictly separating config from code.

You should never rebuild your Docker image just to change a database password or API URL. Instead, you inject these values at runtime using Environment Variables.


2. Injecting Variables

1. The environment Key

The most direct way. Good for non-sensitive defaults.

services:
  web:
    image: my-app
    environment:
      - DEBUG=true
      - APP_ENV=production

2. The env_file Key

Load variables from a file. This is cleaner and keeps your docker-compose.yaml tidy.

services:
  web:
    image: my-app
    env_file:
      - .env

Content of .env:

DEBUG=false
DB_HOST=10.0.0.5
API_KEY=secret123

3. Variable Substitution

You can use variables inside your docker-compose.yaml file. Docker substitutes them from your shell environment or a .env file in the same directory.

services:
  db:
    image: "postgres:${POSTGRES_VERSION:-15}" # Default to 15 if unset
    ports:
      - "${HOST_PORT}:5432"

3. Interactive: Variable Flow

See how variables travel from your machine to the container process.

Shell Env
export FOO=shell
.env File
FOO=file
Compose YAML
environment: FOO=yaml
Container Process
env | grep FOO
FOO=???

4. Best Practices

  1. Git Ignore .env: Never commit secrets to Git. Add .env to your .gitignore.
  2. Use .env.example: Commit a template file with dummy values so developers know what variables are needed.
  3. Variable Expansion: Use ${VAR:-default} syntax in Compose to provide fallbacks.

5. Reading Env Vars in Code

Go

package main

import (
    "fmt"
    "os"
)

func main() {
    // Read environment variable
    dbHost := os.Getenv("DB_HOST")

    // Set default if empty
    if dbHost == "" {
        dbHost = "localhost"
    }

    fmt.Printf("Connecting to database at %s\n", dbHost)
}

Java

public class Config {
    public static void main(String[] args) {
        // Read environment variable
        String dbHost = System.getenv("DB_HOST");

        // Handle null (default value)
        if (dbHost == null) {
            dbHost = "localhost";
        }

        System.out.println("Connecting to database at " + dbHost);
    }
}