# Dev, Staging, Prod: One Config File, No Copy-Paste ![banner](https://articles.aux4.blog/data/posts/aux4/one-config-file-for-every-environment/banner.png) ## Introduction Dev, staging, and prod settings have a way of sprawling — the same host, port, and database name copy-pasted across a dozen scripts, drifting apart until a deploy points at the wrong database. This post keeps every environment in one `config.yaml` and selects it at runtime with `--config`, so a single command behaves correctly everywhere with no duplication. ## Install the package ```bash aux4 aux4 pkger install aux4/config ``` ## Step 1: one file, one section per environment Everything lives under a top-level `config:` key, with a named section per environment. Create `config.yaml`: ```yaml config: dev: host: localhost port: 3000 database: app_dev staging: host: staging.internal port: 3000 database: app_staging prod: host: db.example.com port: 5432 database: app_prod ``` ## Step 2: read values `aux4 config get` reads a whole section as JSON, or a single value by path (nested paths use `/`): ```bash aux4 config get --file config.yaml dev ``` ```json { "host": "localhost", "port": 3000, "database": "app_dev" } ``` ```bash aux4 config get --file config.yaml prod/port ``` ```text 5432 ``` `aux4 config set --file config.yaml --name staging/host --value staging.example.com` writes a value back (creating nested paths as needed). ## Step 3: the payoff — one command, every environment Here's where it pays off. When a command runs with `--configFile` and `--config`, aux4 loads that section and **auto-fills any command variable whose name matches a key** — no glue code. Create `.aux4`: ```json { "profiles": [ { "name": "main", "commands": [ { "name": "connect", "execute": [ "log:connecting to ${database} at ${host}:${port}" ], "help": { "text": "Connect to a database server", "variables": [ { "name": "host", "text": "The server host" }, { "name": "port", "text": "The server port" }, { "name": "database", "text": "The database name" } ] } } ] } ] } ``` The command declares `host`, `port`, and `database` but sets none of them. `--config` picks the section that fills them: ```bash aux4 connect --configFile config.yaml --config dev ``` ```text connecting to app_dev at localhost:3000 ``` ```bash aux4 connect --configFile config.yaml --config prod ``` ```text connecting to app_prod at db.example.com:5432 ``` Same command, same variables — the environment is the only thing that changes. There's no `if dev … elif prod` anywhere; `--config prod` simply hands the command prod's values. ## Step 4: shared defaults, defined once Settings common to every environment shouldn't be repeated three times. Put them in a `defaults.json`: ```json { "sslmode": "require", "poolSize": 10 } ``` Then merge them into each section (`config merge` reads JSON on stdin; `--save` writes the result back): ```bash cat defaults.json | aux4 config merge --file config.yaml --name dev --save cat defaults.json | aux4 config merge --file config.yaml --name staging --save cat defaults.json | aux4 config merge --file config.yaml --name prod --save ``` Add `${sslmode}` and `${poolSize}` (with matching variables) to the `connect` command, and every environment now carries the shared defaults on top of its own values: ```bash aux4 connect --configFile config.yaml --config prod ``` ```text connecting to app_prod at db.example.com:5432 (sslmode=require, pool=10) ``` Change a default once and re-merge — every environment stays in sync. ## Conclusion `aux4/config` collapses your environment sprawl into one file: sections for dev, staging, and prod, selected with `--config`, feeding a command's variables automatically. No copy-paste, no environment branching, and no more deploys pointed at the wrong database — just one config, everywhere. ## See Also - [aux4/config](https://hub.aux4.io/r/public/packages/aux4/config)