Figment: one configuration value assembled from many sources
A hierarchical configuration library so con-free, it's unreal.
At a glance
- What is it?
- Figment is a Rust configuration library that merges file and environment providers into a single value which is then extracted into a serde struct. The providers are feature gated so you only compile the formats you use, and the crate is dual licensed under Apache-2.0 or MIT.
- Who is it for?
- Figment suits a Rust service whose configuration comes from several places at once, a file for defaults and an environment variable for whatever the deployment overrides, and who wants the result deserialised straight into a struct. It does not suit a project whose configuration is genuinely dynamic at run time, since the merged value is fixed once extracted, and it does not suit a team that wants one file format only and no layering.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 26 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- SergioBenitez/Figment917 ★ · +1
| Repository | 2026-10-22 | 2026-11-07 |
|---|---|---|
| SergioBenitez/Figment | 916 | 917 |
1 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The problem of configuration that comes from several places
Figment is described in the README as a semi-hierarchical configuration library for Rust, and the repository description is a pun about how little configuration it involves.
The problem it addresses is the one every long-running service eventually hits. Configuration starts as a file, then the deployment needs to override one value with an environment variable, then a feature flag arrives from somewhere else, and each of those sources has its own parsing code, its own precedence rules and its own error messages. The result is usually a function called load_config with a hundred lines of if statements and a habit of silently keeping the default when a variable name is misspelled.
Figment's answer is to treat configuration as a value with a type and a provenance. You create a Figment, merge providers into it, and then extract the result into a struct you already defined with serde's Deserialize:
use serde::Deserialize;
use figment::{Figment, providers::{Format, Toml, Json, Env}};
#[derive(Deserialize)]
struct Package {
name: String,
authors: Vec<String>,
publish: Option<bool>,
// ... and so on ...
}The type is the point. Once the configuration is a Config struct with a package field of type Package, everything downstream is ordinary Rust with no configuration code left in it. The struct also documents the expected shape, and the Option fields mark exactly which values are allowed to be absent.
The audience is therefore Rust developers with a service to configure, not people looking for a general settings format. The library has no opinion about how your configuration file is named or where it lives; it has opinions about how several sources combine into one value.
Providers: a file, a prefix, a raw list
The example in the README shows four providers in one chain, and they are four different shapes of provider rather than four formats:
let config: Config = Figment::new()
.merge(Toml::file("Cargo.toml"))
.merge(Env::prefixed("CARGO_"))
.merge(Env::raw().only(&["RUSTC", "RUSTDOC"]))
.join(Json::file("Cargo.json"))
.extract()?;Toml::file and Json::file are format providers that read a file. Env::prefixed("CARGO_") is an environment provider that takes every environment variable with that prefix and maps it into a nested key structure, which is how a flat namespace of variables becomes a tree. Env::raw().only takes the opposite approach, naming specific variables such as RUSTC and RUSTDOC and passing their values through without the prefix machinery.
The Format trait is what lets one set of code handle the file formats. The README imports it alongside the concrete providers, which is the pattern for writing a provider of your own or for using a format that is not built in.
That last point leads to the extension story. The README lists third-party providers, currently one: figment_file_provider_adapter, which wraps existing providers and, for any key ending in _FILE, emits a key without the _FILE suffix whose value is the contents of the file whose path was the original value. That is the docker-compose style of pointing a configuration key at a file path, and the adapter is a separate crate rather than something baked into Figment.
The README invites pull requests to add other libraries to that list, which is a sign that the provider interface is meant to be extended rather than kept closed.
Ordering, and the difference between merge and join
The order of the chain is the precedence rule, and it is visible in the example. The TOML file is merged first, then the prefixed environment variables, then the two raw variables, and finally the JSON file is joined.
That last call is the one to look at twice. The example deliberately uses join for one provider and merge for the others, which tells you the library distinguishes two ways of combining values rather than having a single overwrite rule. The documentation is where the difference between them is spelled out, and the README's own usage of both is the hint that the choice depends on what happens when two sources describe the same key.
This is the design that most configuration libraries get wrong in one direction or the other. If every merge silently replaces, then a file that defines a list and an environment variable that appends to it cannot be expressed. If every merge concatenates, then an override cannot be expressed. Providing both and letting the provider order decide which one you want is the sensible resolution, and it is why the example uses two different operations rather than one.
The other thing the chain demonstrates is the difference between a provider that reads a file and one that reads the process environment. Environment variables are strings, so any provider that reads them has to decide how to interpret a value that might be a boolean, a number or a nested structure. The prefixed provider and the raw provider are two different answers to that question, which is why both appear in the same example.
Feature flags and the cost of each format
Figment ships as a Cargo library with the format parsers behind feature flags, and the dependency block in the manifest makes the cost explicit:
[dependencies]
figment = { version = "0.10", features = ["toml"] }The feature list in Cargo.toml is json, which enables serde_json, test, which enables tempfile and parking_lot, toml, which enables toml_edit, yaml, which enables serde_yaml, env, which does nothing and is kept for backwards compatibility, and preserve_order, which enables indexmap and the serde_json preserve_order feature.
Three observations follow. The default feature set is env only, so a project that wants TOML has to ask for it, and the README's usage section says to enable the desired built-in providers. The parser crates are optional dependencies, so a binary that only reads TOML does not link a YAML parser. And the env feature is now an empty placeholder kept so that existing Cargo.toml files that list it keep working, which is the polite way to retire a feature flag.
The preserve_order feature deserves a mention because it is a real behavioural switch rather than a dependency toggle. Turning it on pulls in indexmap, which preserves insertion order of keys, so a configuration value keeps the order the file declared rather than the order a hash map happens to produce. That matters if you care about how a generated document looks.
The dependency on uncased at 0.9.10 is the other one that shapes behaviour. uncased is a crate for case-insensitive string handling, and in a configuration library that is the difference between a key spelled DATABASE_URL and one spelled database_url matching.
Extraction is where the types pay off
The chain ends with extract, and that call is where the merged value becomes your own type. The return type is Result, with the question mark in the example, so a malformed file or a value that does not fit the struct is a normal error rather than a panic.
The struct definitions in the example show the conventions. Package has name as a String, authors as a Vec of String and publish as an Option of Bool. Config has a package field of type Package and rustc as an Option of String.
The pattern in those definitions is the practical guidance. A field that must exist in every deployment, name in this case, is a plain type and a missing value is an error. A field that is genuinely optional in some environments, publish and rustc, is an Option, and the extraction succeeds when it is absent. The authors field is a Vec, so it is always present but may be empty.
That is a design choice with a consequence: it forces you to decide, at the type level, which values are mandatory and which are not, and it makes the answer visible in the source rather than in a documentation file that nobody reads.
The library also exposes the intermediate steps, since the extraction is not the only entry point. The Figment value can be inspected and modified before it is extracted, and the documentation site is where the provider trait, the metadata model and the error types are described in full. The README is deliberately short here and points at docs.rs/figment for the detailed usage guide.
Licensing, release practice and where the library stops
Figment is licensed under either of the following, at your option: the Apache License, Version 2.0, or the MIT License. The tree carries both licence files, LICENSE-APACHE and LICENSE-MIT, and the manifest declares MIT OR Apache-2.0. The dual licence is the standard Rust convention, and it is chosen so a consuming project can take whichever terms suit its distribution model.
The manifest reports version 0.10.19, and the README's dependency snippet uses 0.10. The repository has no GitHub releases, so the crates.io version is the version history. The repository is not archived and the last push was on 2026-10-13.
Now the honest limits of the category. Figment is a layered configuration reader, not a configuration management system. The merged value is fixed at the moment you extract it, and changing configuration at run time means merging again, which means the code has to decide what to do with the parts of the configuration that already exist in a running process.
The other boundary is the provider model itself. The built-in providers cover files and environment variables, and the extension mechanism is a trait, but every provider still has to solve the same problem of turning strings into typed values. A provider that reads from a remote service, or one that prompts the user, brings its own failure modes, and the library does not abstract those away.
For most services, though, the layering is exactly right, and the discipline it imposes is the value: a config value that is not in the struct does not exist, and a struct field that is not an Option has to be provided by someone.
Editorial conclusion
Figment suits a Rust service whose configuration comes from several places at once, a file for defaults and an environment variable for whatever the deployment overrides, and who wants the result deserialised straight into a struct. It does not suit a project whose configuration is genuinely dynamic at run time, since the merged value is fixed once extracted, and it does not suit a team that wants one file format only and no layering. Before adopting, add the dependencies with the features you actually use, because the toml, json and yaml features each pull in their own parser, and write one struct with an Option for every field that might come from the environment, since that is where the layering shows up first.
Frequently asked questions
What is Figment and what does it do?
Figment is a semi-hierarchical configuration library for Rust. You merge providers such as TOML, JSON, YAML and environment variables into a Figment value and then extract the result into your own serde Deserialize struct.
How do I add Figment to a Rust project?
Add it to Cargo.toml with the features for the providers you need, for example figment = { version = "0.10", features = ["toml"] }. The default features are env only, and the json, toml and yaml features each pull in their own parser.
How do environment variables become nested configuration in Figment?
Env::prefixed takes a prefix such as CARGO_ and maps the matching environment variables into a nested key structure, while Env::raw().only names specific variables such as RUSTC and RUSTDOC and passes them through without the prefix handling.
Can I write my own Figment provider?
Yes, the library exposes a Format trait so a provider can be implemented for a format or source that is not built in. The README lists third-party providers, currently figment_file_provider_adapter, which turns any key ending in _FILE into a key whose value is the contents of that file.
What licence does Figment use?
Figment is licensed under either the Apache License, Version 2.0 or the MIT License, at your option, with both LICENSE-APACHE and LICENSE-MIT in the repository and MIT OR Apache-2.0 declared in the manifest.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sergiobenitez-figment)Star history chart for your README
[](https://hysenlabs.com/projects/sergiobenitez-figment)Was this useful?