{"id":991738,"date":"2025-05-12T04:18:44","date_gmt":"2025-05-12T04:18:44","guid":{"rendered":"https:\/\/mynavitechtus.com\/?p=991738"},"modified":"2025-07-25T03:32:51","modified_gmt":"2025-07-25T03:32:51","slug":"when-env-is-no-longer-env-tricks-from-the-runtime-environment","status":"publish","type":"post","link":"https:\/\/mynavitechtus.com\/ja\/when-env-is-no-longer-env-tricks-from-the-runtime-environment\/","title":{"rendered":"When ENV is no longer ENV &#8211; Tricks from the runtime environment"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"991738\" class=\"elementor elementor-991738\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9f9f9c8 e-flex e-con-boxed e-con e-parent\" data-id=\"9f9f9c8\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7b94591 elementor-widget elementor-widget-text-editor\" data-id=\"7b94591\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><span style=\"font-weight: 400;\">Last week, my old team (all members who used to work on SDK API projects) suddenly reunited&#8230; not to reminisce about the old days, but because they were all hitting <\/span><b>the same &#8220;weird&#8221; dotenv bug<\/b><span style=\"font-weight: 400;\"> while deploying a new project with Laravel.<\/span><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-721753c e-flex e-con-boxed e-con e-parent\" data-id=\"721753c\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-860e17c elementor-widget elementor-widget-text-editor\" data-id=\"860e17c\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h2><b>A peculiar situation with Larave<\/b><\/h2><p><span style=\"font-weight: 400;\">Th\u00e0nh (Back-end Engineer) ran into a bug: the first request could read values via <\/span><span style=\"font-weight: 400; color: #008000;\">env()<\/span><span style=\"font-weight: 400;\">, but the second request&#8230; just failed.<\/span><\/p><p><span style=\"font-weight: 400;\">H\u00f9ng (Front-end Engineer) noticed a similar issue after deployment: his config values didn&#8217;t match what he had locally.<\/span><\/p><p><span style=\"font-weight: 400;\">After some head-scratching and debugging, it turned out both issues were <\/span><i><span style=\"font-weight: 400;\">deeply related<\/span><\/i><span style=\"font-weight: 400;\">.<\/span><\/p><h2><b>Laravel, dotenv, and the trickery called <\/b><span style=\"color: #008000;\"><b>config:cache<\/b><\/span><\/h2><p><span style=\"font-weight: 400;\">Here&#8217;s the root of the problem:<\/span><\/p><ul><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Laravel <\/span><b>doesn\u2019t recommend<\/b><span style=\"font-weight: 400;\"> calling <\/span><span style=\"font-weight: 400; color: #008000;\">env()<\/span><span style=\"font-weight: 400;\"> directly in your app logic after the app has booted.<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">In production, Laravel strongly recommends (and practically requires) running <\/span><span style=\"font-weight: 400; color: #008000;\">php artisan config:cache<\/span><span style=\"font-weight: 400;\">, which compiles a static config file for faster performance.<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">After running <\/span><span style=\"font-weight: 400; color: #008000;\">config:cache<\/span><span style=\"font-weight: 400;\">, direct <\/span><span style=\"font-weight: 400; color: #008000;\">env()<\/span><span style=\"font-weight: 400;\"> calls won\u2019t work reliably anymore. You might get <\/span><span style=\"font-weight: 400; color: #008000;\">null<\/span><span style=\"font-weight: 400;\"> or inconsistent results, especially under concurrent requests.<\/span><\/li><\/ul><p><b>Th\u00e0nh\u2019s bug<\/b><span style=\"font-weight: 400;\">:<\/span><span style=\"font-weight: 400;\"><br \/><\/span><span style=\"font-weight: 400;\"> He called <\/span><span style=\"font-weight: 400; color: #008000;\">env()<\/span><span style=\"font-weight: 400;\"> during runtime, not in the initial config \u2192 so on the second request, the value was gone.<\/span><\/p><p><b>Why Laravel behaves this way<\/b><span style=\"font-weight: 400;\">:<\/span><span style=\"font-weight: 400;\"><br \/><\/span><span style=\"font-weight: 400;\"> Laravel wants you to freeze all config values at deploy time using <\/span><span style=\"font-weight: 400; color: #008000;\">config:cache<\/span><span style=\"font-weight: 400;\"><span style=\"color: #008000;\">,<\/span> so the app doesn&#8217;t rely on ENV at runtime.<\/span><\/p><h2><b>Switching gears to AWS Lambda and NodeJS&#8230;<\/b><\/h2><p><span style=\"font-weight: 400;\">Flashback to a project from a year ago using AWS Lambda (NodeJS). AWS deprecated Node v6 and suggested upgrading to v8. Sounds simple, but after the upgrade, we hit a <\/span><b>timezone bug<\/b><span style=\"font-weight: 400;\">:<\/span><\/p><p><span style=\"font-weight: 400;\">We expected it to print in Japan time (JST) \u2192 It printed in UTC.<\/span><\/p><p><span style=\"font-weight: 400;\">After investigation, here&#8217;s what we found:<\/span><\/p><ul><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The app used <\/span><span style=\"font-weight: 400; color: #008000;\">process.env.TZ = &#8216;Asia\/Tokyo&#8217;<\/span><span style=\"font-weight: 400;\"> inside the code &#8211; before initializing the app.<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">But this didn\u2019t work anymore.<\/span><\/li><\/ul><p><span style=\"font-weight: 400;\">Turns out, this was a <\/span><b>trap<\/b><span style=\"font-weight: 400;\">:<\/span><\/p><p><span style=\"font-weight: 400;\">In NodeJS, once <\/span><span style=\"font-weight: 400; color: #008000;\">new Date()<\/span><span style=\"font-weight: 400;\"> is called, the timezone is cached based on the <\/span><span style=\"font-weight: 400; color: #008000;\">TZ<\/span><span style=\"font-weight: 400;\"> environment variable.<\/span><\/p><p><span style=\"font-weight: 400;\">In Lambda, AWS might call <\/span><span style=\"font-weight: 400; color: #008000;\">new Date() <\/span><i><span style=\"font-weight: 400;\">before<\/span><\/i><span style=\"font-weight: 400;\"> your code runs (e.g., for logging or internal init) \u2192 meaning your late-assigned <\/span><span style=\"font-weight: 400; color: #008000;\">TZ<\/span><span style=\"font-weight: 400;\"> doesn\u2019t work.<\/span><\/p><p><b>Fix<\/b><span style=\"font-weight: 400;\">: Set <\/span><span style=\"font-weight: 400; color: #008000;\">TZ<\/span><span style=\"font-weight: 400;\"> directly in Lambda\u2019s environment settings, not in your code.<\/span><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-64ebaf4 e-flex e-con-boxed e-con e-parent\" data-id=\"64ebaf4\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0767cbb elementor-widget elementor-widget-text-editor\" data-id=\"0767cbb\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h2><b>Key Lessons<\/b><\/h2><p><span style=\"font-weight: 400;\">No matter what framework you use &#8211; Laravel, NodeJS, or anything else &#8211; how you handle ENV affects the <\/span><b>stability and predictability<\/b><span style=\"font-weight: 400;\"> of your app.<\/span><\/p><p><b>Golden rules:<\/b><\/p><ul><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Read ENV variables <\/span><b>only once<\/b><span style=\"font-weight: 400;\">, as early as possible (ideally before the app starts).<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Never scatter <\/span><span style=\"font-weight: 400; color: #008000;\">env()<\/span><span style=\"font-weight: 400;\"> calls throughout your code. Centralize them in config files or constants.<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">If your framework supports config caching (like Laravel with <\/span><span style=\"font-weight: 400; color: #008000;\">config:cache<\/span><span style=\"font-weight: 400;\">), <\/span><b>always use it in production<\/b><span style=\"font-weight: 400;\">.<\/span><\/li><li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Avoid using dotenv in production &#8211; even the library author says so in the README.<\/span><\/li><\/ul><h2><b>Small things that cause big headaches: options &amp; config<\/b><\/h2><p><span style=\"font-weight: 400;\">A lot of bugs don&#8217;t come from the core logic itself, but rather from not fully understanding the config or simply not reading the documentation carefully enough.<\/span><\/p><p><span style=\"font-weight: 400;\">On StackOverflow, you see tons of correct answers that are literally just adding one line of config.<\/span><\/p><p><span style=\"font-weight: 400;\">&#8220;A friend once told me: seasoned folks really like to dive into the source code of libraries.&#8221;<\/span><\/p><p><span style=\"font-weight: 400;\">My simple thought is that they just don&#8217;t completely trust the ReadMe anymore \ud83d\ude02<\/span><\/p><h2><b>One last \u201cRed Flag\u201d\u2026<\/b><\/h2><p><span style=\"font-weight: 400;\">One day, our project team found a nasty bug: AWS SDK for Greengrass IoT <\/span><b>automatically reset<\/b><span style=\"font-weight: 400;\"> the <\/span><span style=\"font-weight: 400; color: #008000;\">NODE_TLS_REJECT_UNAUTHORIZED<\/span><span style=\"font-weight: 400;\"> variable. So even if we set it to skip TLS checks, it silently reverted.<\/span><\/p><p><b>Lesson<\/b><span style=\"font-weight: 400;\">: ENV vars aren&#8217;t always immutable. Libraries can override them <\/span><i><span style=\"font-weight: 400;\">behind your back<\/span><\/i><span style=\"font-weight: 400;\">.<\/span><\/p><h2><b>\u7d50\u8ad6<\/b><\/h2><p><span style=\"font-weight: 400;\">If you&#8217;re working in backend, <\/span><b>don\u2019t underestimate your environment variables<\/b><span style=\"font-weight: 400;\">.<\/span><\/p><p><span style=\"font-weight: 400;\">Understand your runtime &#8211; whether it\u2019s Laravel, NodeJS, or AWS Lambda &#8211; because each one handles ENV differently.<\/span><\/p><p><span style=\"font-weight: 400;\">ENV isn\u2019t just config. It\u2019s <\/span><b>a hidden power<\/b><span style=\"font-weight: 400;\"> that can shape your app\u2019s behavior in surprising ways.<\/span><\/p><p style=\"text-align: right;\"><em><span style=\"font-weight: 400;\">Credit: Ton That Bach\uff08Mynavi TechTus Vietnam\u306e\u30c6\u30af\u30cb\u30ab\u30eb\u30ea\u30fc\u30c0\u30fc\uff09<\/span><\/em><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-318509c e-flex e-con-boxed e-con e-parent\" data-id=\"318509c\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Last week, my old team (all members who used to work on SDK API projects) suddenly reunited&#8230; not to reminisce about the old days, but because they were all hitting the same &#8220;weird&#8221; dotenv bug while deploying a new project with Laravel. A peculiar situation with Larave Th\u00e0nh (Back-end Engineer) ran into a bug: the first request could read values via env(), but the second request&#8230; just failed. H\u00f9ng (Front-end Engineer) noticed a similar issue after deployment: his config values didn&#8217;t match what he had locally. After some head-scratching and debugging, it turned out both issues were deeply related. Laravel, dotenv, and the trickery called config:cache Here&#8217;s the root of&#8230;<\/p>","protected":false},"author":1,"featured_media":991730,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[75],"tags":[105,102,103],"class_list":["post-991738","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blogs","tag-dotenv","tag-env","tag-laravel"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/posts\/991738","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/comments?post=991738"}],"version-history":[{"count":11,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/posts\/991738\/revisions"}],"predecessor-version":[{"id":991925,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/posts\/991738\/revisions\/991925"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/media\/991730"}],"wp:attachment":[{"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/media?parent=991738"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/categories?post=991738"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mynavitechtus.com\/ja\/wp-json\/wp\/v2\/tags?post=991738"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}