Skip to content

Configuration

View Markdown

Vertique configuration is one merged tree with typed access and no required files:

  • Zero files to start. No configuration file is required. Framework defaults apply and the application still starts.
  • One merged tree. JSON and properties files, config stores, environment variables, system properties, and the --conf overlay combine in a fixed precedence order, and a higher source always wins.
  • Typed at the Dagger boundary. Your modules parse a section of the tree into a typed record through ConfigParser and do not read raw scalars. The parser’s coercion-lenient mapper is isolated from your API’s own JSON behavior.
  • Secrets never touch a file. A ${key} placeholder resolves at runtime against Vault, AWS Secrets Manager, AWS SSM Parameter Store, or Azure Key Vault, and no resolved value is written to disk.
  • Predictable resolution. A relative config directory resolves against the running process’s own working directory. This page covers the rule in detail because it is the most common first-run surprise.

This page explains where a generated Vertique application’s configuration comes from, how to consume it as a typed record inside your own Dagger modules, the order in which configuration sources combine, and how to route secrets into that tree without writing them into a file.

Where configuration lives in a generated application

Section titled “Where configuration lives in a generated application”

The REST application generated in Quickstart ships one configuration file, config/application.json, at the project root:

{
"http": { "port": 8080, "idleTimeoutSeconds": 60 },
"management": { "port": 9090, "enabled": true }
}

Vertique reads every *.json and *.properties file from each configured directory. The directory is config/ by default, or the directories named by the VERTX_CONFIG_LOCATIONS environment variable, a comma-separated list. Later directories override earlier ones, and within one directory *.properties overrides *.json. No configuration file is required: when no config/ directory exists next to the running process, framework defaults apply and the application still starts. To override a default, edit config/application.json under the application’s own working directory. For the quickstart application, that is rest-app/config/application.json.

Each relative configured directory is resolved against the running process’s own current working directory. It is not resolved against the compiled classpath. An absolute VERTX_CONFIG_LOCATIONS entry is used as-is. Starting the generated application with mvn -ntp exec:java from rest-app/ (as in Quickstart) therefore reads rest-app/config/application.json. Starting the same application from another directory does not, unless VERTX_CONFIG_LOCATIONS names the directory. A file under src/main/resources/config/ is on the classpath only and is not read as startup configuration.

Typed configuration at the Dagger boundary

Section titled “Typed configuration at the Dagger boundary”

Your own Dagger modules read configuration by injecting the framework’s ConfigParser and parsing a section of the root @VertxConfig tree into a typed record. Do not call JsonObject.mapTo or read a raw @Named scalar at the Dagger boundary:

@Provides
HelloConfig helloConfig(@VertxConfig JsonObject config, ConfigParser parser) {
return parser.parse(JsonConfigPaths.navigateObject(config, "hello"), HelloConfig.class);
}

ConfigParser is provided by ConfigParsingModule. Each of the three application starters (vertique-starter-core, vertique-starter-rest, and vertique-starter-services) already includes it transitively through CoreApplicationModule, so you do not list it again yourself when building on one of them. A capability starter such as vertique-starter-postgresql supplies no config parser on its own. See Application model for the full starter table and which starters must be paired with an application starter. The parser uses an isolated, coercion-lenient object mapper independent of the mapper your REST layer uses for request and response bodies, so a config-parsing decision does not leak into your API’s JSON behavior.

Resilience policy is configured at the consumer boundary. REST and Services use the canonical annotations from dev.vertique.resilience.annotation for timeout, retry, circuit-breaker, and bulkhead declarations. A concrete bean uses @Resilient as the method-level anchor; a service contract or REST-client interface can use the declaration annotations directly. Typed JSON configuration can override those declarations for an environment, but configuration alone does not activate resilience for an operation without an annotation or anchor. maxRetries is bounded from 0 through 100, and timeout fields use explicit units such as timeoutMs or timeoutSeconds according to the owning module’s reference. Retry policy must match the operation’s replay and idempotency guarantees. Framework adapters do not expose bulkhead configuration in this release, although programmatic pipelines in vertique-resilience can use a bulkhead explicitly.

Use @Resilient(policy = "payments") when several operations should share an environment-specific policy. The tier lives under resilience.policies and contains only the concerns it overrides:

{
"resilience": {
"policies": {
"payments": {
"retry": {
"maxRetries": 5,
"delayMs": 100,
"maxDelayMs": 1000
}
}
}
}
}

The annotation selects the tier and does not copy configuration into source code. Resolution uses operation and transport overrides first, then the named tier, then declaration values and family defaults. A named tier can supply the whole active policy when the anchor has no inline declaration. An unknown tier is a startup error, and a tier that defines no resilience concern is invalid. The full record shape and supported bounds are in the vertique-resilience module reference.

Fatal JVM Error values are rethrown. They are not retried or counted as ordinary failures. The runtime accepts one consequence of Vert.x cleanup: fatal cleanup can reset accumulated closed-state breaker failures and close a half-open breaker before the same fatal value is rethrown.

Vertique merges configuration from several sources into one tree, in this order from lowest to highest precedence:

  1. *.json files in each config directory
  2. *.properties files in each config directory
  3. Declared config.stores entries
  4. Environment variables
  5. System properties
  6. The --conf overlay (highest precedence; always wins)

A value declared in a higher-precedence source always overrides the same key supplied by a lower one.

Vertique publishes four secret providers. Three resolve secrets as property sources: your application’s own configuration declares a ${key} placeholder reference, and the property source resolves it against declared entries under config.propertySources. The fourth, AWS SSM Parameter Store, is a config.stores entry that eagerly merges a whole parameter subtree directly into the config tree (see Configuration source precedence above).

Provider Artifact Model
HashiCorp Vault (KV v2) vertique-config-vault Eager property source. Every declared path is read once at bootstrap
AWS Secrets Manager vertique-config-aws-secrets Eager property source. Every declared secret is fetched once at bootstrap
AWS SSM Parameter Store vertique-config-aws-ssm Config store, not a property source. It merges a whole parameter subtree into the tree
Azure Key Vault vertique-config-azure-keyvault On-demand property source. Only keys referenced by a placeholder are fetched

Each of the three property-source providers supports the same self-reference idiom for pulling a secret into the tree without writing its value into any file: declare the placeholder under its own key, and the recursive tree probe falls through to the declared source instead of looping.

{
"db": {
"password": "${db.password}"
},
"config": {
"propertySources": [
{
"name": "vault-app",
"type": "vault",
"address": "http://vault:8200",
"paths": [{ "path": "secret/app", "prefix": "db." }]
}
]
}
}