pgmonkey (5/8) — Environment Variables and Secrets
Summary: Keep credentials out of your config files using pgmonkey’s environment variable interpolation — inline
${VAR}substitution, structuredfrom_envandfrom_filereferences, and sensitive key protection that prevents accidental fallback passwords.
| Key | Value |
|---|---|
| pgmonkey version | 4.0.0 |
| Python version | 3.12 |
| Config file | pgmonkey_config.yaml |
| Database name | tutorial_db |
| Database user | app_user |
0. Prerequisites
- Completed Part 1 (pgmonkey installed, config file working)
- A terminal where you can set environment variables
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=Trueflag in Python (or--resolve-envon 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_defaultsin 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_fileis 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
| Environment | Password approach | Config safe to commit? |
|---|---|---|
| Local dev | ${PGPASSWORD} with export in your shell | Yes — no secrets in file |
| Docker | ${PGPASSWORD} injected via docker run -e or docker-compose.yml | Yes |
| Kubernetes | from_file: /var/run/secrets/db/password mounted from a Secret | Yes |
| CI/CD | from_env: CI_DB_PASSWORD set in pipeline variables | Yes |
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 keysfrom_env— structured reference to an environment variablefrom_file— structured reference to a file (ideal for Kubernetes secrets)- Sensitive keys reject defaults by default — opt in explicitly with
allow_sensitive_defaults redact_configmasks secrets for safe logging- All interpolation is opt-in via
resolve_env=Trueor--resolve-env
Next up: pgmonkey (6/8) — The CLI Toolbox covers config generation, connection testing, code generation, server recommendations, and live server auditing.
pgmonkey — All Parts
- 1 pgmonkey (1/8) — Getting Started
- 2 pgmonkey (2/8) — Connection Types
- 3 pgmonkey (3/8) — Securing Connections with SSL/TLS
- 4 pgmonkey (4/8) — Connection Pooling
- 5 pgmonkey (5/8) — Environment Variables and Secrets You are here
- 6 pgmonkey (6/8) — The CLI Toolbox
- 7 pgmonkey (7/8) — CSV Import and Export
- 8 pgmonkey (8/8) — Production Patterns
