Volga
Home
API Docs
GitHub
  • English
  • Русский
Home
API Docs
GitHub
  • English
  • Русский
  • Home
  • Getting Started

    • Quick Start
    • Route Parameters
    • Query Parameters
    • Route Groups
  • Requests & Responses

    • Headers
    • Handling JSON
    • Handling Form Data
    • Working with Files
    • Cookies
  • Middleware & Infrastructure

    • Basic Middleware
    • Custom Middleware
    • Parameterized Middleware
    • Response Compression
    • Request Decompression
    • CORS (Cross-Origin Resource Sharing)
    • Static Files
    • Rate Limiting
    • Configuration Files
  • Security & Access

    • Authentication and Authorization
  • Reliability & Observability

    • Global Error Handling
    • Tracing & Logging
    • Request cancellation
  • Protocols & Realtime

    • HTTP/1 and HTTP/2
    • HTTPS
    • WebSockets
    • Server-Sent Events (SSE)
  • Advanced Patterns

    • Dependency Injection
    • Custom Handling of HEAD, OPTIONS, and TRACE Methods

Configuration Files

Volga supports file-based application configuration. You can define settings in a TOML or JSON file, bind sections to strongly-typed Rust structs, and access them in request handlers via the Config<T> extractor. Hot-reload is also supported.

Prerequisites

Dependencies

If you're not using the full feature set, you need to enable the config feature in your Cargo.toml:

[dependencies]
volga = { version = "...", features = ["config"] }
serde = { version = "1", features = ["derive"] }

serde with the derive feature is required for deserializing config sections into your structs.

Supported Formats

Volga supports two configuration file formats (detected by file extension):

  • TOML (.toml) — recommended
  • JSON (.json)

Quick Start

Create a config file app_config.toml in your project root:

[server]
host = "0.0.0.0"
port = 3000

[handler]
msg = "World"

Define a struct for your custom section and bind it during app setup:

use volga::{App, Config, ok};
use serde::Deserialize;

#[derive(Deserialize)]
struct HandlerConfig {
    msg: String,
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new().with_config(|cfg| {
        cfg.with_file("app_config.toml")
            .bind_section::<HandlerConfig>("handler")
    });

    app.map_get("/hello", |cfg: Config<HandlerConfig>| async move {
        ok!(fmt: "Hello, {}!", cfg.msg)
    });

    app.run().await
}

Now a GET /hello request returns Hello, World!.

Loading Configuration

Volga provides three ways to load a config file:

Default Config

The simplest option — with_default_config() automatically discovers app_config.toml or app_config.json in the current working directory:

let app = App::new().with_default_config();

Warning

with_default_config() panics at startup if neither app_config.toml nor app_config.json is found.

Builder Closure

For full control, use with_config() which gives you a ConfigBuilder:

let app = App::new().with_config(|cfg| {
    cfg.with_file("config/prod.toml")
        .bind_section::<Database>("database")
        .bind_section::<Cache>("cache")
        .reload_on_change()
});

If you omit with_file(), the builder falls back to the same default file discovery as with_default_config() — it looks for app_config.toml or app_config.json in the current working directory. This is useful when you want the default file but also need to bind custom sections or enable hot-reload:

let app = App::new().with_config(|cfg| {
    cfg.bind_section::<Database>("database")
        .reload_on_change()
});

Standalone Builder

You can also create a ConfigBuilder separately and pass it via set_config():

use volga::{App, ConfigBuilder};

let config = ConfigBuilder::from_file("config/prod.toml")
    .bind_section::<Database>("database")
    .reload_on_change();

let app = App::new().set_config(config);

Binding Sections

Required Sections

Use bind_section::<T>(key) to register a required section. If the section is missing or malformed, the application panics at startup:

#[derive(Deserialize)]
struct Database {
    url: String,
}

let app = App::new().with_config(|cfg| {
    cfg.with_file("app_config.toml")
        .bind_section::<Database>("database")
});

Corresponding app_config.toml:

[database]
url = "postgres://localhost/mydb"

Optional Sections

Use bind_section_optional::<T>(key) for sections that may be absent. If the section is missing, Config<T> will not be available, but the app won't panic:

#[derive(Deserialize)]
struct Cache {
    ttl: u64,
}

let app = App::new().with_config(|cfg| {
    cfg.with_file("app_config.toml")
        .bind_section_optional::<Cache>("cache")
});

Accessing Config in Handlers

Use the Config<T> extractor to access a bound section in any request handler. It performs one atomic load + Arc::clone per request — no deserialization at runtime:

use volga::{App, Config, ok};
use serde::Deserialize;

#[derive(Deserialize)]
struct Database {
    url: String,
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new().with_config(|cfg| {
        cfg.with_file("app_config.toml")
            .bind_section::<Database>("database")
    });

    app.map_get("/db-url", |db: Config<Database>| async move {
        ok!(db.url.as_str())
    });

    app.run().await
}

Built-in Sections

Volga automatically recognizes and applies certain reserved sections from the config file. These sections configure framework internals and do not require bind_section():

SectionFeature FlagFields
[server](always)host, port, body_limit_bytes, max_header_count, max_connections
[tls]tlsTLS certificate configuration
[tracing]tracingTracing/logging configuration
[openapi]openapiOpenAPI specification settings
[cors]middlewareCORS policy configuration

For example, the [server] section lets you configure the host and port directly in the config file:

[server]
host = "0.0.0.0"
port = 8080
body_limit_bytes = 1048576
max_connections = 1000

Tips

Built-in sections are applied at startup only. They are not affected by hot-reload.

Hot Reload

Enable automatic config reloading with reload_on_change(). Volga will poll the config file every 5 seconds and update all bound sections:

let app = App::new().with_config(|cfg| {
    cfg.with_file("app_config.toml")
        .bind_section::<HandlerConfig>("handler")
        .reload_on_change()
});

Reload behavior:

  • Required sections — if a required section disappears from the file or becomes malformed during reload, the previous value is retained.
  • Optional sections — if an optional section disappears, Config<T> becomes unavailable.
  • Built-in sections (server, tls, etc.) are not reloaded — they are startup-only.

JSON Format Example

You can use JSON instead of TOML. Create app_config.json:

{
  "server": {
    "host": "0.0.0.0",
    "port": 3000
  },
  "database": {
    "url": "postgres://localhost/mydb"
  }
}
let app = App::new().with_config(|cfg| {
    cfg.with_file("app_config.json")
        .bind_section::<Database>("database")
});

Full Example

For a complete working example, see the config example in the Volga repository.

Last Updated: 3/27/26, 8:54 PM
Prev
Rate Limiting