Configuration
Typed settings that vary by environment
A config is a typed value your code reads, such as limits, timeouts and feature flags. You declare its type in Python and write its value in a TOML or JSON file beside the code, with overlays that change it for some environments only. Encore checks every value against the type before your service starts, and under encore run you can change a value without restarting anything.
For sensitive data, use secrets instead. A config can read secrets but never holds them in its files.
Declaring a config
Describe the config's value with a dataclass, and declare the config as a module-level variable:
orders/settings.pyfrom dataclasses import dataclass, field
from datetime import timedelta
from typing import Annotated
from encore.config import Config, Max, Min
@dataclass
class Checkout:
max_items: Annotated[int, Min(1), Max(500)]
currencies: list[str] = field(default_factory=lambda: ["USD"])
payment_timeout: timedelta = timedelta(seconds=90)
checkout = Config[Checkout]("checkout")
Then write its value in a file named after it, beside the service's encore.service.json:
orders/checkout.config.tomlmax_items = 100
payment_timeout = "2m"
Read the value with .value. It's an instance of your type:
from orders.settings import checkout
def validate_cart(items: list[str]) -> None:
limit = checkout.value.max_items
if len(items) > limit:
raise ValueError(f"at most {limit} items per order")
Encore reads config declarations from the source, so:
- Declare configs at module level.
- The name must be a string literal and an identifier: a letter or underscore, then letters, digits and underscores.
- The type argument is required:
Config[Checkout]("checkout"). Without it, reading.valueraisesTypeError. - Use the config by reading
.valueor calling.on_change(). Encore records which services read which configs from these calls, so it rejects passing the config object itself around.
Supported types
The config's type is a dataclass, a TypedDict or an Enum, built from:
str,int,float,boolLiteral[...],Optional[...],Enumsubclasseslist[...]anddict[str, ...]- nested dataclasses and
TypedDicts, including generic ones typing.Anydatetime.timedelta, written as a duration string such as"30s","250ms"or"1h30m"Secretfields
A dataclass field with a default, or an Optional field, may be left out of the files. A union of dataclasses is supported when each has a Literal tag field that tells them apart. Encore reports a type it can't describe as an error at the part of the type that's the problem.
Constraints
Narrow what a field accepts with Annotated markers from encore.config:
Encore checks values against these before the service sees them. At run time the markers do nothing.
Config scopes
As with secrets, the class you declare a config with decides who it belongs to:
from dataclasses import dataclass
from encore.config import SharedConfig
@dataclass
class Flags:
new_checkout: bool = False
flags = SharedConfig[Flags]("flags")
Environment-specific values
A config's value is merged from several files. Each layer overrides the ones before it, using JSON Merge Patch semantics:
- The defaults in your type.
<name>.config.toml: the value in every environment.<name>.<type>.config.toml: environments of one type, where the type islocal(encore run),test(encore test),development,previeworproduction.<name>.@<env>.config.toml: the environment named<env>.<name>.user.config.toml: your own overrides, applied to yourencore runonly. These files are gitignored (*.user.config.*) and never read in tests.
Each file can be .toml or .json. For example:
orders/checkout.config.tomlmax_items = 100
orders/checkout.production.config.tomlmax_items = 500
payment_timeout = "30s"
orders/checkout.@prod-eu.config.tomlcurrencies = ["EUR", "SEK"]
Each file is checked as a partial value, so a mistake is reported in the file that makes it, and the merged value is then checked in full. A file whose name doesn't match a declared config is ignored, unless its name is close to one, which is reported as a likely typo.
Reacting to changes
Under encore run, editing a config file updates the value in running services without a rebuild or restart. An invalid edit is reported, and services keep the last valid value.
Each read of .value returns the newest value. Keep the result in a local variable when you want one consistent value for the duration of an operation:
settings = checkout.value # one snapshot for this whole request
To act when the value changes, register a callback with on_change. It runs on a background thread, so keep it quick. It returns a function that stops the calls:
import logging
log = logging.getLogger(__name__)
def log_change(value: Checkout) -> None:
log.info("checkout config changed", extra={"max_items": value.max_items})
stop = checkout.on_change(log_change)
An exception raised by the callback is logged, and later changes still reach it.
Secrets in config
A field of type Secret[str] or Secret[bytes] reads a secret instead of holding a value. Its default names the secret. The secret must also be declared in code, with the config's scope: a service's own Secret for a Config, a SharedSecret for a SharedConfig, and a GlobalSecret for a GlobalConfig.
orders/settings.pyfrom dataclasses import dataclass
from encore.config import Config
from encore.secrets import Secret
stripe_key = Secret[str]("StripeKey")
@dataclass
class Payments:
provider_url: str
api_key: Secret[str] = Secret[str]("StripeKey")
payments = Config[Payments]("payments")
def auth_header() -> str:
return f"Bearer {payments.value.api_key.value}"
The field's type is always Secret, whatever the config's scope. Secret fields can't be written in config files, and can't be Optional or be inside lists, dicts or unions.
Inspecting values with the CLI
The encore config commands show and edit config values:
$ encore config list # the app's configs, and whether each value is valid$ encore config show orders.checkout # the merged value, and which file each part comes from$ encore config show orders.checkout --env-type production$ encore config validate # check every file, for every environment type$ encore config schema orders.checkout # the JSON Schema Encore derived from the type
Name a service's config as <service>.<name>, and a shared or global one by its name alone.
encore config set and encore config unset change one value in one file, keeping the rest of the file as it was. They write your user override by default; pass --base, --env-type <type> or --env <name> to change another layer:
$ encore config set orders.checkout max_items 20$ encore config set orders.checkout max_items 500 --env-type production$ encore config unset orders.checkout max_items
Editor support
Run encore config link to name each config file's schema in the file: a #:schema comment in TOML, or a "$schema" key in JSON. Editors with JSON Schema support then check and complete what you write. The schemas are served by the local Encore daemon. Run it again after adding a file.
Testing
Under encore test, a config has the value its test overlay gives it: <name>.test.config.toml over the base file. User overrides are never applied in tests.