Why Export JSON Config to .env?
JSON is a great format for structured, hand-edited config. Nested, typed, comment-free, and every language has a parser. But at runtime, every 12-factor app and almost every container platform (Docker, Kubernetes, Vercel, Fly, Railway) wants a flat key-value soup via environment variables, not a nested JSON blob. So teams end up either hand-maintaining both representations (which drift) or generating one from the other.
Generating .env from JSON is the right direction: JSON stays the source of truth, .env is a deployment artefact. Changes happen once, in a readable file. The exporter handles the three fiddly parts: flattening nested keys, choosing a case convention, and quoting values with special characters.
Example: Nested Config to Flat ENV
A typical app config:
{
"app": {
"name": "promptspace",
"port": 3000,
"debug": false
},
"db": {
"host": "db.prod.example.com",
"port": 5432,
"user": "readonly",
"password": "s0meP@ss word!"
},
"redis": {
"url": "redis://cache.prod:6379"
},
"features": {
"newCheckout": true,
"allowedOrigins": ["https://app.example.com", "https://admin.example.com"]
}
}Flattened to .env (default options):
APP_NAME=promptspace
APP_PORT=3000
APP_DEBUG=false
DB_HOST=db.prod.example.com
DB_PORT=5432
DB_USER=readonly
DB_PASSWORD="s0meP@ss word!"
REDIS_URL=redis://cache.prod:6379
FEATURES_NEWCHECKOUT=true
FEATURES_ALLOWEDORIGINS_0=https://app.example.com
FEATURES_ALLOWEDORIGINS_1=https://admin.example.comNotice three things:
DB_PASSWORDis quoted because the value contains a space.APP_DEBUG=falseis unquoted — dotenv will deliver it as the string"false", so your code needs explicit coercion. See the Pitfalls section.allowedOriginsbecame two numbered keys. If you prefer a single JSON-encoded string, switch the array mode.
Quoting Rules
The dotenv format is deceptively simple. The real spec lives in each parser, and they don't fully agree. The safe rules the tool follows:
- Unquoted when the value is alphanumeric with no spaces or special characters. Dot, dash, slash, colon, equals and underscore are permitted bare.
- Double-quoted when the value contains spaces, hash (
#), dollar ($), backtick, or embedded quotes. Internal double quotes are escaped with backslash. - Newlines in values are emitted as the two-character sequence
\\ninside the double-quoted string. dotenv parses this back to a real newline when loading. For multi-line values with literal content (like PEM keys), use single-quoted strings instead — the tool offers this as an option.
Nested Key Strategies
- Flatten with underscores (default):
db.host→DB_HOST. Universal — every env parser handles this. - Keep dot notation:
db.host→db.host. Only works with parsers that explicitly support dots. Spring Boot does. Node dotenv does not. - JSON-serialize nested objects:
db.hostanddb.portare replaced by a singleDB='{"host":...,"port":...}'value. The receiving code mustJSON.parseit. Good for feature flag structs with complex schemas; bad for human-readable configs.
Array Handling
Environment variables are strings, not arrays. There is no spec-compliant way to pass an array. Two conventions dominate:
- Index-flatten:
TAGS_0=ai,TAGS_1=ml. Easy for shell loops:for i in $(seq 0 9); do echo "$TAGS_$i"; done. - Comma-separated string:
TAGS=ai,ml. Easy to split in application code. Breaks if any value contains a comma. - JSON-serialize:
TAGS='["ai","ml"]'. Robust against any value content, but you lose the human-readable flatness.
Platform Variants
The same key-value pairs render slightly differently per platform. The tool offers one-click conversion:
- .env (dotenv):
KEY=value, one per line. Comments start with#. - docker-compose: Same .env format when used via
env_file:. Inline inenvironment:uses YAML list syntax. - Kubernetes ConfigMap: YAML object under
data:. Values are indented strings. Multiline strings use the|block scalar. - Shell export:
export KEY=valueper line. Sourced withsource .env. Values with spaces need shell-quoting rules — the tool escapes with single quotes.
Common Pitfalls
- Everything is a string.
APP_DEBUG=falsearrives in your app as the string"false", which is truthy in JavaScript. Always coerce:const debug = process.env.APP_DEBUG === 'true'. - Interpolation. dotenv v15+ expands
$$OTHER_VARreferences inside values. If your JSON has literal$$signs (e.g. a password), the tool escapes them to\\$$. Verify after convert. - Length limits. Linux environment variables are limited to about 128 KB total per process. A large JSON config that serializes to a dense env block can hit this on container platforms. Split by concern (
DB_*vsAPP_*) or move secrets to a dedicated secret store. - Secrets in .env. Never commit a .env with real secrets to git. Add
.envto.gitignore, use.env.examplewith placeholders, and inject real values from a secret manager at deploy time. - Case folding on Windows. Windows environment variables are case-insensitive.
Db_HostandDB_HOSTrefer to the same variable. Always UPPER_SNAKE_CASE for portability.
Related JSON Tools
- JSON Formatter — validate the config before exporting.
- JSON Flattener — flatten keys to dot-paths before converting; sometimes easier to review.
- JSON to YAML — convert to YAML for Kubernetes ConfigMaps directly without going through .env.
- JSON Merge Online — merge base config with environment overrides before export.