Skip to content

Loading

Pavex configuration system is hierarchical and profile-based.
Pavex provides a utility, ConfigLoader, to load configuration when the application starts.

Configuration profile

Different runtime envinroments require different configuration values—e.g. you don't want to connect to your production database when running the application on your development machine.

In a Pavex application, each runtime environment is modeled as a configuration profile. The profile is loaded from the PX_PROFILE environment variable, unless explicitly specified.

Configuration profiles are conventionally defined in the server crate. They must implement the ConfigProfile trait, and they are often modelled as enums:

src/base/profile.rs
pub use pavex::config::ConfigProfile;

#[derive(ConfigProfile)] // (1)!
pub enum Profile {
    #[pavex(profile = "dev")]
    Development,
    #[pavex(profile = "prod")]
    Production,
}
  1. The ConfigProfile trait can be derived using the ConfigProfile derive macro. By default, the name of the profile is the snake_case representation of the enum variant name, but it can be customized via the #[pavex(profile = "...")] attribute.

Customize your profiles

Profiles should be specific to your application's needs.
The project scaffolded by pavex new comes with two configuration profiles: dev and prod. Feel free to extend this list as needed—e.g. a staging profile for your staging environment.

Configuration sources

Pavex combines multiple configuration sources to assemble the configuration values that are ultimately used to drive the application.

For a given profile, Pavex combines the following sources:

  1. Environment variables (PX_*)
  2. Profile-specific configuration file ({configuration_dir}/{profile}.yml)
  3. Base configuration file ({configuration_dir}/base.yml)

The list above is ordered by precedence: environment variables take precedence over profile-specific configuration files, which in turn take precedence over the base configuration file.

Environment variables

Only environment variables prefixed with PX_ are considered when loading configuration values.
This is meant to reduce the risk of misconfiguration—e.g. loading a value from an unrelated environment variable that's been set on the underlying system.

The naming convention for environment variables is PX_{CONFIGURATION_KEY}__{FIELD_NAME}, where:

  • {CONFIGURATION_KEY} is the key you passed to Blueprint::config when you registered the config option.
  • {FIELD_NAME} is the name of the field you are trying to set within that configuration entry.

As an example, consider this configuration type:

src/server.rs
// [...]
#[derive(Debug, Clone, serde::Deserialize)]
pub struct ServerConfig {
    pub port: u16,
    pub host: String,
}

registered via:

src/server.rs
    // [...]
    bp.config("server", t!(self::ServerConfig));

You would have to set PX_SERVER__PORT to configure its port field via environment variables.

Nested fields

The naming convention supports nested fields too: add a double underscore (__) as separator every time you access a nested field.

As an example, consider this configuration type:

src/postgres.rs
use secrecy::Secret;

#[derive(serde::Deserialize, Debug, Clone)]
pub struct PostgresConfig {
    pub pool: PoolConfig,
    pub connection: ConnectionConfig,
}

#[derive(serde::Deserialize, Debug, Clone)]
pub struct ConnectionConfig {
    pub username: String,
    pub password: Secret<String>,
    pub host: String,
    pub database_name: String,
    pub require_ssl: bool,
}

#[derive(serde::Deserialize, Debug, Clone)]
pub struct PoolConfig {
    pub min_size: u32,
    pub max_size: u32,
}

registered via:

src/postgres.rs
    // [...]
    bp.config("postgres", t!(self::PostgresConfig));

You would have to set PX_POSTGRES__POOL__MAX_SIZE to configure the max_size field via environment variables.

Configuration files

Configuration files are expected to be in the YAML format.
The default configuration directory is named configuration. It is specified as a relative path. Pavex will start looking for it as a subdirectory of the current working directory; if it doesn't exist, it will look in the parent directory, and so on, recursively, until it reaches the root directory. It stops on the first matching directory.

You can also customize the configuration directory path if needed.