[{"data":1,"prerenderedAt":807},["ShallowReactive",2],{"blog-post:en:\u002Fen\u002F2025-04-25-env-files":3},{"id":4,"title":5,"body":6,"description":799,"extension":800,"meta":801,"navigation":321,"path":803,"seo":804,"stem":805,"__hash__":806},"content\u002Fen\u002F2025-04-25-env-files.md",".env files",{"type":7,"value":8,"toc":788},"minimark",[9,14,18,21,24,27,31,39,46,64,67,77,80,83,85,89,92,95,101,107,110,120,123,130,133,135,139,142,152,164,167,170,172,176,187,197,200,232,235,241,244,246,253,259,262,271,274,276,280,289,751,757,760,762,766,769,772,781,784],[10,11,13],"h2",{"id":12},"managing-secrets-without-the-pain","Managing Secrets Without the Pain",[15,16,17],"p",{},"Secure storage of secrets — and the proper use of this “sensitive” data in environment variables — has been something on my mind for quite a while.",[15,19,20],{},"Over time I tried several approaches. Some worked… until they didn’t.",[15,22,23],{},"This post is about how I used to manage secrets, the problems that came with it, and how I recently simplified the whole process.",[25,26],"hr",{},[10,28,30],{"id":29},"the-old-approach-local-encryption","The Old Approach: Local Encryption",[15,32,33,34,38],{},"For years my go-to solution was ",[35,36,37],"strong",{},"local encryption",".",[15,40,41,42,45],{},"I relied on good old ",[35,43,44],{},"openssl",", which is available on almost any system. The idea was straightforward:",[47,48,49,53,61],"ol",{},[50,51,52],"li",{},"Keep a keyfile somewhere safe (in my case, usually a hidden Google Doc).",[50,54,55,56,60],{},"Encrypt ",[57,58,59],"code",{},".env"," files using that key.",[50,62,63],{},"Store the encrypted files in the repository.",[15,65,66],{},"The resulting files would look something like this:",[68,69,74],"pre",{"className":70,"code":72,"language":73},[71],"language-text",".env.dev.enc\n.env.stage.enc\n.env.prod.enc\n","text",[57,75,72],{"__ignoreMap":76},"",[15,78,79],{},"When needed, I would decrypt them locally, update variables, and encrypt them again.",[15,81,82],{},"Simple enough — at least in theory.",[25,84],{},[10,86,88],{"id":87},"the-problem-with-this-workflow","The Problem With This Workflow",[15,90,91],{},"In practice, things became painful pretty quickly.",[15,93,94],{},"Every small change required going through the full cycle:",[68,96,99],{"className":97,"code":98,"language":73},[71],"decrypt → edit → encrypt\n",[57,100,98],{"__ignoreMap":76},[15,102,103,104,38],{},"And this had to be repeated ",[35,105,106],{},"for every environment",[15,108,109],{},"After doing this enough times, it became tempting to take shortcuts.",[15,111,112,113,119],{},"More often than I’d like to admit, I ended up using a ",[35,114,115,116,118],{},"single shared ",[57,117,59],{}," file"," across multiple applications just to make life easier (though who was I really fooling?).",[15,121,122],{},"The consequences were predictable.",[15,124,125,126,129],{},"Sometimes backend environment variables would end up sitting right next to frontend ones — including things like ",[35,127,128],{},"database connection credentials"," 🤪.",[15,131,132],{},"Not exactly ideal.",[25,134],{},[10,136,138],{"id":137},"another-issue-separate-secret-storage","Another Issue: Separate Secret Storage",[15,140,141],{},"There was also a structural problem.",[15,143,144,145,147,148,151],{},"Encrypted ",[57,146,59],{}," files usually lived in a ",[35,149,150],{},"separate repository",", which meant:",[153,154,155,158,161],"ul",{},[50,156,157],{},"secrets were updated independently",[50,159,160],{},"application changes and secret changes were not synchronized",[50,162,163],{},"mistakes became easier to make",[15,165,166],{},"Any extra moving part in a deployment process increases the chance that something goes wrong.",[15,168,169],{},"And this definitely added a few more.",[25,171],{},[10,173,175],{"id":174},"discovering-doppler","Discovering Doppler",[15,177,178,179,186],{},"Recently I came across ",[180,181,185],"a",{"href":182,"rel":183},"https:\u002F\u002Fwww.doppler.com\u002F",[184],"nofollow","Doppler",".\nIt turned out to be a really nice way to manage secrets.",[15,188,189,190,192,193,196],{},"Instead of juggling encrypted ",[57,191,59],{}," files, secrets are stored and managed through a ",[35,194,195],{},"clean web interface",", and injected directly when running commands.",[15,198,199],{},"For example:",[68,201,205],{"className":202,"code":203,"language":204,"meta":76,"style":76},"language-bash shiki shiki-themes github-light github-dark","doppler run -- npm run build\n","bash",[57,206,207],{"__ignoreMap":76},[208,209,212,216,220,224,227,229],"span",{"class":210,"line":211},"line",1,[208,213,215],{"class":214},"sScJk","doppler",[208,217,219],{"class":218},"sZZnC"," run",[208,221,223],{"class":222},"sj4cs"," --",[208,225,226],{"class":218}," npm",[208,228,219],{"class":218},[208,230,231],{"class":218}," build\n",[15,233,234],{},"The application simply runs with the correct configuration profile.",[15,236,237,238,240],{},"No ",[57,239,59],{}," files required.",[15,242,243],{},"Even better, Doppler provides integrations with most modern deployment tools.",[25,245],{},[10,247,249,250,252],{"id":248},"why-i-still-keep-env-files","Why I Still Keep ",[57,251,59],{}," Files",[15,254,255,256,38],{},"That said, I don’t love being ",[35,257,258],{},"completely dependent on a single tool",[15,260,261],{},"If the tool disappears tomorrow, I still want my projects to work.",[15,263,264,265,267,268,38],{},"So instead of abandoning ",[57,266,59],{}," files entirely, I decided to ",[35,269,270],{},"generate them automatically using Doppler",[15,272,273],{},"This keeps things portable while still benefiting from centralized secret management.",[25,275],{},[10,277,279],{"id":278},"automating-setup-with-taskfile","Automating Setup With Taskfile",[15,281,282,283,288],{},"To make things smoother, I created a small ",[180,284,287],{"href":285,"rel":286},"https:\u002F\u002Ftaskfile.dev\u002F",[184],"Taskfile"," for initializing projects with the correct environment variables:",[68,290,294],{"className":291,"code":292,"language":293,"meta":76,"style":76},"language-yaml shiki shiki-themes github-light github-dark","# yaml-language-server: $schema=https:\u002F\u002Ftaskfile.dev\u002Fschema.json\nversion: \"3\"\n\ntasks:\n  install:\n    cmds:\n      - curl -Ls --tlsv1.3 --proto \"=https\" https:\u002F\u002Fcli.doppler.com\u002Finstall.sh | sh\n\n  login:\n    cmds:\n      - doppler login\n  \n  get-env:\n    vars:\n      DOPPLER_PROJECT: '{{default \"help\" .DOPPLER_PROJECT}}'\n      DOPPLER_CONFIG: '{{default \"dev\" .DOPPLER_CONFIG}}'\n      FILEPATH: '{{default \"\" .FILEPATH}}'\n    cmds:\n      - doppler secrets download --project {{.DOPPLER_PROJECT}} --config {{.DOPPLER_CONFIG}} --no-file --format env > {{.FILEPATH}}\n  \n  setup-infra-secrets:\n    cmds:\n      - task: get-env\n        vars: { DOPPLER_PROJECT: \"infra\", DOPPLER_CONFIG: \"{{.DOPPLER_CONFIG}}\", FILEPATH: \".env\" }\n\n  setup-frontend-secrets:\n    cmds:\n      - task: get-env\n        vars: { DOPPLER_PROJECT: \"frontend\", DOPPLER_CONFIG: \"{{.DOPPLER_CONFIG}}\", FILEPATH: \"apps\u002Ffrontend\u002F.env\" }\n  \n  setup-backend-secrets:\n    cmds:\n      - task: get-env\n        vars: { DOPPLER_PROJECT: \"backend\", DOPPLER_CONFIG: \"{{.DOPPLER_CONFIG}}\", FILEPATH: \"apps\u002Fbackend\u002F.env\" }\n\n  setup-secrets:\n    deps: [setup-frontend-secrets, setup-infra-secrets, setup-backend-secrets]\n\n  initial-setup:\n    cmds:\n      - task: install\n      - task: login\n      - task: setup-secrets\n","yaml",[57,295,296,302,316,323,332,340,348,357,362,370,377,385,391,399,407,418,429,440,447,455,460,468,475,488,529,534,542,549,560,593,598,606,613,624,657,662,670,695,700,708,715,727,739],{"__ignoreMap":76},[208,297,298],{"class":210,"line":211},[208,299,301],{"class":300},"sJ8bj","# yaml-language-server: $schema=https:\u002F\u002Ftaskfile.dev\u002Fschema.json\n",[208,303,305,309,313],{"class":210,"line":304},2,[208,306,308],{"class":307},"s9eBZ","version",[208,310,312],{"class":311},"sVt8B",": ",[208,314,315],{"class":218},"\"3\"\n",[208,317,319],{"class":210,"line":318},3,[208,320,322],{"emptyLinePlaceholder":321},true,"\n",[208,324,326,329],{"class":210,"line":325},4,[208,327,328],{"class":307},"tasks",[208,330,331],{"class":311},":\n",[208,333,335,338],{"class":210,"line":334},5,[208,336,337],{"class":307},"  install",[208,339,331],{"class":311},[208,341,343,346],{"class":210,"line":342},6,[208,344,345],{"class":307},"    cmds",[208,347,331],{"class":311},[208,349,351,354],{"class":210,"line":350},7,[208,352,353],{"class":311},"      - ",[208,355,356],{"class":218},"curl -Ls --tlsv1.3 --proto \"=https\" https:\u002F\u002Fcli.doppler.com\u002Finstall.sh | sh\n",[208,358,360],{"class":210,"line":359},8,[208,361,322],{"emptyLinePlaceholder":321},[208,363,365,368],{"class":210,"line":364},9,[208,366,367],{"class":307},"  login",[208,369,331],{"class":311},[208,371,373,375],{"class":210,"line":372},10,[208,374,345],{"class":307},[208,376,331],{"class":311},[208,378,380,382],{"class":210,"line":379},11,[208,381,353],{"class":311},[208,383,384],{"class":218},"doppler login\n",[208,386,388],{"class":210,"line":387},12,[208,389,390],{"class":311},"  \n",[208,392,394,397],{"class":210,"line":393},13,[208,395,396],{"class":307},"  get-env",[208,398,331],{"class":311},[208,400,402,405],{"class":210,"line":401},14,[208,403,404],{"class":307},"    vars",[208,406,331],{"class":311},[208,408,410,413,415],{"class":210,"line":409},15,[208,411,412],{"class":307},"      DOPPLER_PROJECT",[208,414,312],{"class":311},[208,416,417],{"class":218},"'{{default \"help\" .DOPPLER_PROJECT}}'\n",[208,419,421,424,426],{"class":210,"line":420},16,[208,422,423],{"class":307},"      DOPPLER_CONFIG",[208,425,312],{"class":311},[208,427,428],{"class":218},"'{{default \"dev\" .DOPPLER_CONFIG}}'\n",[208,430,432,435,437],{"class":210,"line":431},17,[208,433,434],{"class":307},"      FILEPATH",[208,436,312],{"class":311},[208,438,439],{"class":218},"'{{default \"\" .FILEPATH}}'\n",[208,441,443,445],{"class":210,"line":442},18,[208,444,345],{"class":307},[208,446,331],{"class":311},[208,448,450,452],{"class":210,"line":449},19,[208,451,353],{"class":311},[208,453,454],{"class":218},"doppler secrets download --project {{.DOPPLER_PROJECT}} --config {{.DOPPLER_CONFIG}} --no-file --format env > {{.FILEPATH}}\n",[208,456,458],{"class":210,"line":457},20,[208,459,390],{"class":311},[208,461,463,466],{"class":210,"line":462},21,[208,464,465],{"class":307},"  setup-infra-secrets",[208,467,331],{"class":311},[208,469,471,473],{"class":210,"line":470},22,[208,472,345],{"class":307},[208,474,331],{"class":311},[208,476,478,480,483,485],{"class":210,"line":477},23,[208,479,353],{"class":311},[208,481,482],{"class":307},"task",[208,484,312],{"class":311},[208,486,487],{"class":218},"get-env\n",[208,489,491,494,497,500,502,505,508,511,513,516,518,521,523,526],{"class":210,"line":490},24,[208,492,493],{"class":307},"        vars",[208,495,496],{"class":311},": { ",[208,498,499],{"class":307},"DOPPLER_PROJECT",[208,501,312],{"class":311},[208,503,504],{"class":218},"\"infra\"",[208,506,507],{"class":311},", ",[208,509,510],{"class":307},"DOPPLER_CONFIG",[208,512,312],{"class":311},[208,514,515],{"class":218},"\"{{.DOPPLER_CONFIG}}\"",[208,517,507],{"class":311},[208,519,520],{"class":307},"FILEPATH",[208,522,312],{"class":311},[208,524,525],{"class":218},"\".env\"",[208,527,528],{"class":311}," }\n",[208,530,532],{"class":210,"line":531},25,[208,533,322],{"emptyLinePlaceholder":321},[208,535,537,540],{"class":210,"line":536},26,[208,538,539],{"class":307},"  setup-frontend-secrets",[208,541,331],{"class":311},[208,543,545,547],{"class":210,"line":544},27,[208,546,345],{"class":307},[208,548,331],{"class":311},[208,550,552,554,556,558],{"class":210,"line":551},28,[208,553,353],{"class":311},[208,555,482],{"class":307},[208,557,312],{"class":311},[208,559,487],{"class":218},[208,561,563,565,567,569,571,574,576,578,580,582,584,586,588,591],{"class":210,"line":562},29,[208,564,493],{"class":307},[208,566,496],{"class":311},[208,568,499],{"class":307},[208,570,312],{"class":311},[208,572,573],{"class":218},"\"frontend\"",[208,575,507],{"class":311},[208,577,510],{"class":307},[208,579,312],{"class":311},[208,581,515],{"class":218},[208,583,507],{"class":311},[208,585,520],{"class":307},[208,587,312],{"class":311},[208,589,590],{"class":218},"\"apps\u002Ffrontend\u002F.env\"",[208,592,528],{"class":311},[208,594,596],{"class":210,"line":595},30,[208,597,390],{"class":311},[208,599,601,604],{"class":210,"line":600},31,[208,602,603],{"class":307},"  setup-backend-secrets",[208,605,331],{"class":311},[208,607,609,611],{"class":210,"line":608},32,[208,610,345],{"class":307},[208,612,331],{"class":311},[208,614,616,618,620,622],{"class":210,"line":615},33,[208,617,353],{"class":311},[208,619,482],{"class":307},[208,621,312],{"class":311},[208,623,487],{"class":218},[208,625,627,629,631,633,635,638,640,642,644,646,648,650,652,655],{"class":210,"line":626},34,[208,628,493],{"class":307},[208,630,496],{"class":311},[208,632,499],{"class":307},[208,634,312],{"class":311},[208,636,637],{"class":218},"\"backend\"",[208,639,507],{"class":311},[208,641,510],{"class":307},[208,643,312],{"class":311},[208,645,515],{"class":218},[208,647,507],{"class":311},[208,649,520],{"class":307},[208,651,312],{"class":311},[208,653,654],{"class":218},"\"apps\u002Fbackend\u002F.env\"",[208,656,528],{"class":311},[208,658,660],{"class":210,"line":659},35,[208,661,322],{"emptyLinePlaceholder":321},[208,663,665,668],{"class":210,"line":664},36,[208,666,667],{"class":307},"  setup-secrets",[208,669,331],{"class":311},[208,671,673,676,679,682,684,687,689,692],{"class":210,"line":672},37,[208,674,675],{"class":307},"    deps",[208,677,678],{"class":311},": [",[208,680,681],{"class":218},"setup-frontend-secrets",[208,683,507],{"class":311},[208,685,686],{"class":218},"setup-infra-secrets",[208,688,507],{"class":311},[208,690,691],{"class":218},"setup-backend-secrets",[208,693,694],{"class":311},"]\n",[208,696,698],{"class":210,"line":697},38,[208,699,322],{"emptyLinePlaceholder":321},[208,701,703,706],{"class":210,"line":702},39,[208,704,705],{"class":307},"  initial-setup",[208,707,331],{"class":311},[208,709,711,713],{"class":210,"line":710},40,[208,712,345],{"class":307},[208,714,331],{"class":311},[208,716,718,720,722,724],{"class":210,"line":717},41,[208,719,353],{"class":311},[208,721,482],{"class":307},[208,723,312],{"class":311},[208,725,726],{"class":218},"install\n",[208,728,730,732,734,736],{"class":210,"line":729},42,[208,731,353],{"class":311},[208,733,482],{"class":307},[208,735,312],{"class":311},[208,737,738],{"class":218},"login\n",[208,740,742,744,746,748],{"class":210,"line":741},43,[208,743,353],{"class":311},[208,745,482],{"class":307},[208,747,312],{"class":311},[208,749,750],{"class":218},"setup-secrets\n",[15,752,753,754,756],{},"With it, generating the ",[57,755,59],{}," file becomes part of the normal project setup.",[15,758,759],{},"No manual secret handling required.",[25,761],{},[10,763,765],{"id":764},"final-thoughts","Final Thoughts",[15,767,768],{},"Managing secrets is one of those things that seems simple until it isn't.",[15,770,771],{},"My old setup worked for a while, but it created too much friction and too many opportunities for mistakes.",[15,773,774,775,777,778,38],{},"Using Doppler — while still keeping ",[57,776,59],{}," files as a fallback — turned out to be a good balance between ",[35,779,780],{},"convenience and control",[15,782,783],{},"And honestly, anything that removes a few steps from secret management is already a win.",[785,786,787],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":76,"searchDepth":304,"depth":304,"links":789},[790,791,792,793,794,795,797,798],{"id":12,"depth":304,"text":13},{"id":29,"depth":304,"text":30},{"id":87,"depth":304,"text":88},{"id":137,"depth":304,"text":138},{"id":174,"depth":304,"text":175},{"id":248,"depth":304,"text":796},"Why I Still Keep .env Files",{"id":278,"depth":304,"text":279},{"id":764,"depth":304,"text":765},"My .env files setup","md",{"date":802},"2025-04-25","\u002Fen\u002F2025-04-25-env-files",{"title":5,"description":799},"en\u002F2025-04-25-env-files","zShizKH59ohG6cPX4WAiyMESjvAsJHGz5RTwA7BNbAw",1788970693128]