We Are Forking dotenvy into dotenv-ng | SecretSpec<br>Skip to content
We Are Forking dotenvy into dotenv-ng
Aug 15, 2026<br>Domen Kožar
We have released dotenv-ng 1.0, a<br>modern Rust implementation for loading and rendering .env files. It began as<br>a fork of dotenvy after its parser<br>changed a secret while reading it.
That may sound contradictory. SecretSpec is still on a mission to eliminate<br>environment variables as a secrets<br>interface, and we have written about<br>where .env went wrong. It should not be the<br>final home of a secret.
But migrating away from .env starts with reading it<br>correctly.
Why fork dotenvy?<br>Section titled “Why fork dotenvy?”
The immediate failure was SecretSpec issue<br>#73. A dotenv file contained a<br>value with bcrypt fragments:
TEST="foo:$2a$10$TWoviNHS27HJMw1PKe4tBeIMlms6tWdYS9hKoHANKCQhluDlEt/gu"
The file was intact. Reading it through the dotenv provider returned a<br>different value because dotenvy treated the dollar-prefixed fragments as<br>variable substitutions. The failure appeared later as an authentication error,<br>not a parse error.
An upstream request to make substitution configurable had been open since<br>2024. A pull<br>request arrived in 2026 but<br>targeted an unreleased API. A migration tool cannot require users to recognize<br>and escape parser syntax inside their secrets.
The maintenance gap<br>Section titled “The maintenance gap”
The original Rust dotenv crate stopped releasing in 2020 and was eventually<br>marked unmaintained by<br>RustSec, which listed<br>dotenvy as an alternative.
Dotenvy’s description still calls it “a well-maintained fork.” Its latest<br>published version, 0.15.7, was released on March 22,<br>2023. A Rust forum<br>discussion<br>noted the two-year release gap in 2025. By the time the bcrypt bug blocked<br>SecretSpec, it was more than three years.
There is an uncomfortable irony in a maintained fork repeating its upstream’s<br>release problem. Its maintainers do not owe us a release, but SecretSpec needed<br>breaking fixes on a schedule we control.
What does dotenv-ng improve upon?<br>Section titled “What does dotenv-ng improve upon?”
We first considered a small patch. Auditing the parser uncovered more problems<br>around JSON, Windows paths, Unicode names, precedence, and partial environment<br>mutation.
dotenv-ng therefore starts from dotenvy 0.15.7 but deliberately breaks<br>compatibility where correctness requires it. Version 1.0 adds:
a source-aware parser with structured errors;
literal dollar signs by default, with substitution available only when a<br>caller explicitly enables it;
a broader key grammar that supports dashes, leading digits, leading dots,<br>and Unicode;
a renderer that adds only the quoting and escaping needed to parse a value<br>back unchanged;
validation before process-environment mutation; and
an explicit unsafe boundary around that mutation.
Property tests exercise arbitrary Unicode and syntax-heavy values, check that<br>quoting is used only when necessary, and round-trip complete documents. The<br>parser and renderer, the core of the rewrite, both have 100% line coverage.
The complete compatibility and API changes are recorded in the dotenv-ng<br>1.0 changelog.
Try dotenv-ng 1.0<br>Section titled “Try dotenv-ng 1.0”
The package is available on crates.io.<br>Applications can keep the familiar dotenv crate name with a dependency<br>alias:
[dependencies]
dotenv = { package = "dotenv-ng", version = "1" }
Starting in SecretSpec 0.20, dotenv-ng powers dotenv parsing and rendering<br>throughout SecretSpec.<br>SecretSpec 0.19: Moving and importing secrets between providers