You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Some time ago we were performing updates to all our packages, including the dotenv, where we went from v2 to v5. And later we noticed that the CPU usage and all request times have increased substantially, even though we use pretty beefy instances. On 8 core instances, CPU usage increased by >20%.
After some investigation, we found out that the dotenv load times have skyrocketed and memory consumption has gone up quite a lot too. The primary cause of regression was pinned down to this method https://github.com/vlucas/phpdotenv/blob/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7/src/Parser/EntryParser.php#L46-L60
Where now, to parse one .env line, it needs to create a bunch of classes and then the final entry is at least 2 classes that are kept in memory. Compared to a simple array<string, string> in v2.
Our .env is 2k lines long of which around 400 are empty lines and another 300 are comments.
So we downgraded back to v2 and the difference is huge, especially during peak hours.
For example, in one simple and fast route, during peak hours, the difference between v2 and v5 is around 80ms per request. On a route that has around 65k req/h (1m+ req/day) that makes more than 80 minutes of extra processing time in an hour, just on this route.
Here's an example of that route after the downgrade performed on the 8th of Feb. No other changes to the code or infrastructure were done.
I know, the obvious solution would be some sort of caching. But is there really a need for so much more processing and objectifying?
Thanks for getting in touch. I am aware that v5 is slower than v2, and one reason for this is they have different features, and v2 has bugs that could only be sanely resolved by using a slower approach. I would be interested to know what PHP version you are using and what the env files are you are loading are looking like. I doubt the use of objects is what is the cause of the slow-down here, but I would certainly consider a PR that improves performance without introducing bugs, along with evidence.
In general, I would not recommend running this package on every request. Best practice is to use this package to parse your env file, one-time, then cache that across requests. v5 has a modular architecture, enabling this. For example, in Laravel, this package is used to compile a cached configuration file, one time, then that file is "required" by PHP at runtime, so we can make use of opcache, effectively meaning that here is zero IO on each request to load env and config.
I'd also be interested to know if you are using opcache. Loading 100 classes into fpm should make zero difference to performance, other than on the first request.
We are running PHP 7.4, and yes opcache is enabled.
what the env files are you are loading are looking like
Could you please elaborate on this one?
Just one regular .env file, with a bunch of simple string entries, less than 10 variables that have references, no multi-level references, and no multi-line entries.
The upcoming v5.7.0 will resolve this. The parser and loader were accumulating their results in a way that made loading O(n²) in the number of entries, which is why large files like yours were hit so much harder than the small ones in our benchmarks, and on top of that, plain and simply quoted values now skip the token state machine entirely. On a 2,000 line file in the same shape as yours (blanks, comments, a handful of references), loading is around 4x faster than v5.6 and scaling is now linear, which closes most of the gap to v2 while keeping all of the v5 parser's correctness fixes. Worth knowing: the memory side of the report didn't reproduce, since load() retains only the returned array, and with opcache the class loading costs a fraction of a millisecond per request. The general advice from earlier in this thread still stands, though: parse once and cache your config rather than reading .env on every request. Thanks for the detailed report.
Reacted by Max Slotov, Martins Briedis and Serhii Fomenko
Activity
GrahamCampbell commentedon Feb 14, 2023
Thanks for getting in touch. I am aware that v5 is slower than v2, and one reason for this is they have different features, and v2 has bugs that could only be sanely resolved by using a slower approach. I would be interested to know what PHP version you are using and what the env files are you are loading are looking like. I doubt the use of objects is what is the cause of the slow-down here, but I would certainly consider a PR that improves performance without introducing bugs, along with evidence.
In general, I would not recommend running this package on every request. Best practice is to use this package to parse your env file, one-time, then cache that across requests. v5 has a modular architecture, enabling this. For example, in Laravel, this package is used to compile a cached configuration file, one time, then that file is "required" by PHP at runtime, so we can make use of opcache, effectively meaning that here is zero IO on each request to load env and config.
GrahamCampbell commentedon Feb 14, 2023
I'd also be interested to know if you are using opcache. Loading 100 classes into fpm should make zero difference to performance, other than on the first request.
makstech commentedon Feb 15, 2023
We are running PHP 7.4, and yes opcache is enabled.
Could you please elaborate on this one?
Just one regular .env file, with a bunch of simple string entries, less than 10 variables that have references, no multi-level references, and no multi-line entries.
GrahamCampbell commentedon Aug 23, 2026
The upcoming v5.7.0 will resolve this. The parser and loader were accumulating their results in a way that made loading O(n²) in the number of entries, which is why large files like yours were hit so much harder than the small ones in our benchmarks, and on top of that, plain and simply quoted values now skip the token state machine entirely. On a 2,000 line file in the same shape as yours (blanks, comments, a handful of references), loading is around 4x faster than v5.6 and scaling is now linear, which closes most of the gap to v2 while keeping all of the v5 parser's correctness fixes. Worth knowing: the memory side of the report didn't reproduce, since
load()retains only the returned array, and with opcache the class loading costs a fraction of a millisecond per request. The general advice from earlier in this thread still stands, though: parse once and cache your config rather than reading.envon every request. Thanks for the detailed report.