Skip to main content

Config Inheritance (extends)

Runix supports config file inheritance via the extends field. This lets you share common configuration across environments while overriding specific values.

Usage​

# base.yaml
defaults:
restart_policy: on-failure
max_restarts: 10

processes:
api:
runtime: go
entrypoint: ./cmd/api
env:
LOG_LEVEL: info
# runix.yaml (production override)
extends: ./base.yaml

defaults:
restart_policy: always
max_restarts: 50

processes:
api:
env:
LOG_LEVEL: warn
DATABASE_URL: postgres://prod-db:5432/myapp

Merge Behavior​

The extends system performs a deep merge:

TypeMerge Strategy
Scalars (strings, numbers, bools)Child value overrides parent
Maps (env, processes)Merged — child keys override parent keys, parent keys are preserved
Arrays (args, depends_on, tags)Child array replaces parent array entirely
defaultsDeep merged — child overrides specific fields

Example Merge​

Parent (base.yaml):

defaults:
restart_policy: on-failure
max_restarts: 10

processes:
api:
runtime: go
entrypoint: ./cmd/api
env:
PORT: "8080"
LOG_LEVEL: info
args: ["--verbose"]

Child (runix.yaml):

extends: ./base.yaml
processes:
api:
env:
LOG_LEVEL: debug
ENABLE_CACHE: "true"
args: ["--debug"]

Result (effective config):

defaults:
restart_policy: on-failure # from parent
max_restarts: 10 # from parent

processes:
api:
runtime: go # from parent
entrypoint: ./cmd/api # from parent
env:
PORT: "8080" # from parent
LOG_LEVEL: debug # overridden by child
ENABLE_CACHE: "true" # added by child
args: ["--debug"] # replaced by child (arrays don't merge)

Chain Depth​

Config files can extend other files that extend further files. The chain depth is limited to prevent infinite recursion.

Validation​

The merged config is validated after all extends are resolved. This means validation errors reference the final effective config, not individual files.

What's Next​