|

pgmonkey (5/8) — Environment Variables and Secrets

← Previous pgmonkey (5/8) Next →

Summary: Keep credentials out of your config files using pgmonkey’s environment variable interpolation — inline ${VAR} substitution, structured from_env and from_file references, and sensitive key protection that prevents accidental fallback passwords.

KeyValue
pgmonkey version4.0.0
Python version3.12
Config filepgmonkey_config.yaml
Database nametutorial_db
Database userapp_user

0. Prerequisites


1. The Problem with Hardcoded Credentials

The config file from Part 1 has the password in plain text.

connection_settings:
  user: 'app_user'
  password: 'my_secret_password'




Code language: JavaScript (javascript)

This works for local development, but it becomes a liability the moment the file is committed to version control, shared with a colleague, or deployed to a server. pgmonkey solves this with opt-in environment variable interpolation — your config file contains references instead of values, and pgmonkey resolves them at runtime.

Note: Interpolation is opt-in. Without the resolve_env=True flag in Python (or --resolve-env on the CLI), pgmonkey treats ${VAR} strings as literal text. Existing configs with no variable references are unaffected.


2. Inline Variable Substitution

Replace a value with ${VAR_NAME} to read from an environment variable.

connection_settings:
  user: '${PGUSER}'
  password: '${PGPASSWORD}'
  host: '${PGHOST}'
  port: '${PGPORT}'
  dbname: '${PGDATABASE}'




Code language: JavaScript (javascript)

Set the variables in your shell.

export PGUSER=app_user
export PGPASSWORD=my_secret_password
export PGHOST=localhost
export PGPORT=5432
export PGDATABASE=tutorial_db




Code language: JavaScript (javascript)

Test the connection with --resolve-env.

pgmonkey pgconfig test --connconfig pgmonkey_config.yaml --resolve-env




Code language: CSS (css)
Testing connection...
Connection successful!




Without --resolve-env, the literal string ${PGHOST} would be passed to psycopg as the hostname, which would fail.


3. Default Values with Fallbacks

Use ${VAR:-default} to provide a fallback when the variable is not set.

connection_settings:
  user: '${PGUSER:-app_user}'
  host: '${PGHOST:-localhost}'
  port: '${PGPORT:-5432}'
  dbname: '${PGDATABASE:-tutorial_db}'
  password: '${PGPASSWORD}'




Code language: JavaScript (javascript)

If PGHOST is not set, pgmonkey uses localhost. If PGPASSWORD is not set, pgmonkey raises an error immediately — no silent fallback to an empty password.

This pattern is ideal for local development: non-sensitive settings have sensible defaults, while credentials must be explicitly provided.


4. Sensitive Key Protection

pgmonkey blocks ${VAR:-default} on sensitive keys by default.

connection_settings:
  password: '${PGPASSWORD:-devpass}'




Code language: JavaScript (javascript)
pgmonkey pgconfig test --connconfig pgmonkey_config.yaml --resolve-env




Code language: CSS (css)
Error: Default values are not allowed for sensitive key 'password'.




Code language: JavaScript (javascript)

This is intentional. A fallback password in a config file is dangerous in production — if the environment variable is missing, the application silently connects with the fallback instead of failing. pgmonkey prevents this by default.

Sensitive keys: password, sslkey, sslcert, sslrootcert, and any key containing token, secret, or credential.

To opt in for local development, pass --allow-sensitive-defaults.

pgmonkey pgconfig test --connconfig pgmonkey_config.yaml --resolve-env --allow-sensitive-defaults




Code language: CSS (css)

In Python:

from pgmonkey import PGConnectionManager

manager = PGConnectionManager()
conn = manager.get_database_connection(
    'pgmonkey_config.yaml',
    'normal',
    resolve_env=True,
    allow_sensitive_defaults=True
)




Code language: JavaScript (javascript)

Warning: Only use allow_sensitive_defaults in local development. In production, sensitive keys should always come from environment variables or secret files — never from defaults.


5. Structured References — from_env

For clarity, you can use a structured from_env reference instead of inline ${VAR}.

connection_settings:
  user: '${PGUSER:-app_user}'
  password:
    from_env: PGMONKEY_DB_PASSWORD
  host: '${PGHOST:-localhost}'
  dbname: '${PGDATABASE:-tutorial_db}'




Code language: JavaScript (javascript)
export PGMONKEY_DB_PASSWORD=my_secret_password
pgmonkey pgconfig test --connconfig pgmonkey_config.yaml --resolve-env




Code language: JavaScript (javascript)

from_env reads the named environment variable and replaces the entire password value with it. If the variable is not set, pgmonkey raises an error at startup — not on the first query.

The advantage over inline ${VAR} is readability: the intent is explicit, and the variable name can be anything (not constrained to PG prefix conventions).


6. Structured References — from_file

For Kubernetes and container environments, secrets are often mounted as files. Use from_file to read them.

connection_settings:
  user: '${PGUSER:-app_user}'
  password:
    from_file: /var/run/secrets/db/password
  host: '${PGHOST:-db-service}'
  dbname: '${PGDATABASE:-tutorial_db}'




Code language: JavaScript (javascript)

pgmonkey reads the file at the specified path and trims the trailing newline — matching the Kubernetes Secret convention. If the file does not exist, pgmonkey raises an error immediately.

Tip: from_file is also useful outside Kubernetes. You can store passwords in files with restricted permissions (chmod 600) as an alternative to environment variables.


7. Mixing Approaches

You can mix inline ${VAR}, from_env, and from_file in the same config.

connection_settings:
  user: '${PGUSER:-app_user}'
  password:
    from_file: /var/run/secrets/db/password
  host: '${PGHOST:-db-service}'
  port: '${PGPORT:-5432}'
  dbname: '${PGDATABASE:-appdb}'
  sslmode: 'verify-full'
  sslrootcert:
    from_file: /var/run/secrets/db/ca.crt




Code language: JavaScript (javascript)

Each reference type is resolved independently. This config reads the password and CA certificate from files, reads the host from an environment variable with a default, and uses literal values for the rest.


8. Using Interpolation in Python

Pass resolve_env=True to get_database_connection().

from pgmonkey import PGConnectionManager

manager = PGConnectionManager()
conn = manager.get_database_connection(
    'pgmonkey_config.yaml',
    'pool',
    resolve_env=True
)

with conn as c:
    with c.cursor() as cur:
        cur.execute('SELECT current_user;')
        print(cur.fetchone())




Code language: JavaScript (javascript)

You can also load and resolve the config separately using load_config.

from pgmonkey import load_config

config = load_config('pgmonkey_config.yaml', resolve_env=True)
print(config['connection_settings']['host'])




Code language: JavaScript (javascript)

To log the resolved config without leaking secrets, use redact_config.

from pgmonkey import load_config, redact_config

config = load_config('pgmonkey_config.yaml', resolve_env=True)
print(redact_config(config))




Code language: JavaScript (javascript)
{'connection_settings': {'user': 'app_user', 'password': '***REDACTED***', 'host': 'localhost', ...}}




Code language: JavaScript (javascript)

redact_config masks password, sslkey, sslcert, sslrootcert, and any key containing token, secret, or credential.


9. Deployment Patterns

EnvironmentPassword approachConfig safe to commit?
Local dev${PGPASSWORD} with export in your shellYes — no secrets in file
Docker${PGPASSWORD} injected via docker run -e or docker-compose.ymlYes
Kubernetesfrom_file: /var/run/secrets/db/password mounted from a SecretYes
CI/CDfrom_env: CI_DB_PASSWORD set in pipeline variablesYes

In every case, the config file is safe to commit to version control. The secrets are injected at runtime by the environment.


Summary

You learned four ways to keep credentials out of your pgmonkey config files.

  • ${VAR} — inline substitution from environment variables
  • ${VAR:-default} — substitution with a fallback for non-sensitive keys
  • from_env — structured reference to an environment variable
  • from_file — structured reference to a file (ideal for Kubernetes secrets)
  • Sensitive keys reject defaults by default — opt in explicitly with allow_sensitive_defaults
  • redact_config masks secrets for safe logging
  • All interpolation is opt-in via resolve_env=True or --resolve-env

Next up: pgmonkey (6/8) — The CLI Toolbox covers config generation, connection testing, code generation, server recommendations, and live server auditing.

Similar Posts

Leave a Reply