<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ryan Blunden</title>
    <description>The latest articles on Forem by Ryan Blunden (@ryanblunden).</description>
    <link>https://forem.com/ryanblunden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F19214%2F27d26c63-4ec3-460c-9179-3d7846c16d25.jpeg</url>
      <title>Forem: Ryan Blunden</title>
      <link>https://forem.com/ryanblunden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ryanblunden"/>
    <language>en</language>
    <item>
      <title>Doppler Encrypted Secrets Snapshots for High Availability</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Wed, 23 Nov 2022 05:36:42 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-encrypted-secrets-snapshots-for-high-availability-1ml0</link>
      <guid>https://forem.com/doppler/doppler-encrypted-secrets-snapshots-for-high-availability-1ml0</guid>
      <description>&lt;p&gt;Ideally, fetching secrets at runtime by your application is avoided altogether using &lt;a href="https://docs.doppler.com/docs/integrations"&gt;Doppler integrations&lt;/a&gt; (e.g. Doppler Kubernetes Secrets Operator), which continuously sync secrets to your hosting environment ahead of time, but this won't always be possible.&lt;/p&gt;

&lt;p&gt;For legacy applications, on-prem, and virtualized environments where secrets must be fetched at runtime, bundling &lt;a href="https://docs.doppler.com/docs/install-cli"&gt;Doppler CLI&lt;/a&gt; secrets snapshots into your application provides a robust failsafe in the event Doppler's API is unreachable.&lt;/p&gt;

&lt;p&gt;The result is similar to a GitOps secrets approach, but without the messy business of committing encrypted secrets to Git repositories can be avoided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build. Encrypt. Deploy. Fallback.
&lt;/h2&gt;

&lt;p&gt;Doppler secrets snapshots are bundled into the application build during CI/CD with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler secrets download secrets.json.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then to fallback to a secrets snapshot when running your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--fallback&lt;/span&gt; secrets.json.enc &lt;span class="nt"&gt;--&lt;/span&gt; ./app-start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secrets snapshots aren't just for high availability. They're perfect for network-restricted environments and offer protection if Doppler's API rate limit is exceeded.&lt;/p&gt;

&lt;p&gt;The addition of the &lt;code&gt;--fallback-only&lt;/code&gt; instructs the CLI only to use the snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--fallback&lt;/span&gt; secrets.json.enc &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--fallback-only&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; ./app-start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's look into passphrase usage for encrypting and decrypting secrets snapshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passphrase
&lt;/h2&gt;

&lt;p&gt;By default, the CLI configuration is used to construct the passphrase, which is typically the Service Token value exposed via the DOPPLER_TOKEN environment variable. While this is the simplest solution, setting the passphrase explicitly has the benefits of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only using the &lt;code&gt;DOPPLER_TOKEN&lt;/code&gt; for authentication&lt;/li&gt;
&lt;li&gt;Allowing the passphrase and DOPPLER_TOKEN values to be rotated independently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, your implementation must guarantee the passphrase remains constant from build to deployment during a release. As these are infrastructure secrets, we recommend a dedicated infra project for each application containing only the &lt;code&gt;DOPPLER_TOKEN&lt;/code&gt; and (should you use a passphrase) a &lt;code&gt;DOPPLER_PASSPHRASE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bpzJAp5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znck0v4xl1q4dqhkqzl2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bpzJAp5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znck0v4xl1q4dqhkqzl2.jpeg" alt="" width="880" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;‎&lt;a href="https://docs.doppler.com/docs/integrations"&gt;Doppler integrations&lt;/a&gt; then keep infrastructure secrets in sync between your build and deployment environments, eliminating the risks associated with manual copying and pasting.&lt;/p&gt;

&lt;p&gt;The following video demonstrates how to set up an infra project in three simple steps:&lt;/p&gt;

&lt;p&gt;%[&lt;a href="https://vimeo.com/769739281"&gt;https://vimeo.com/769739281&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;With the required pieces in place, let's move on to creating and using secrets snapshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;DOPPLER_TOKEN&lt;/code&gt; and &lt;code&gt;DOPPLER_PASSPHRASE&lt;/code&gt; injected into your build environment (e.g. GitHub Action Secrets), we can now create the secrets snapshot file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler secrets download &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--passphrase&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_PASSPHRASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  secrets.json.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secrets snapshots also support name transformers and download formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASP.NET Core secrets snapshot&lt;/span&gt;
doppler secrets download &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--passphrase&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_PASSPHRASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--format&lt;/span&gt; dotnet-json &lt;span class="se"&gt;\&lt;/span&gt;
 appsettings.json.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the secrets snapshot file is included in your build, then you're done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;DOPPLER_TOKEN&lt;/code&gt; and &lt;code&gt;DOPPLER_PASSPHRASE&lt;/code&gt; injected into the deployment environment via automation (e.g Ansible), the Doppler CLI will attempt to fetch the latest version of secrets using the secrets snapshot as a fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--passphrase&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_PASSPHRASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--fallback&lt;/span&gt; secrets.json.enc &lt;span class="nt"&gt;--&lt;/span&gt; ./start-app.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Doppler secret snapshots are your high-availability solution to ensure applications can always access their secrets.&lt;/p&gt;

&lt;p&gt;Using Docker? Check out our dedicated &lt;a href="https://docs.doppler.com/docs/docker-high-availability"&gt;Docker High Availability documentation&lt;/a&gt; to learn more.&lt;/p&gt;

</description>
      <category>secretops</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>How To Prevent Secrets From Ending Up On Developers' Machines</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Tue, 21 Jun 2022 00:28:05 +0000</pubDate>
      <link>https://forem.com/doppler/how-to-prevent-secrets-from-ending-up-on-developers-machines-40cb</link>
      <guid>https://forem.com/doppler/how-to-prevent-secrets-from-ending-up-on-developers-machines-40cb</guid>
      <description>&lt;p&gt;Even with environment variable storage offered by modern hosting platforms and secrets managers provided by every cloud, developers' machines are still littered with secrets in unencrypted text files because local development was left out of the picture.&lt;/p&gt;

&lt;p&gt;But we can remedy this situation using dynamic secrets injection and ephemeral secrets files, and in this post, we'll be using the &lt;a href="https://docs.doppler.com/docs/accessing-secrets"&gt;Doppler CLI&lt;/a&gt; to demonstrate how this is possible.&lt;/p&gt;

&lt;p&gt;But before diving in, we need to solve the problem of secure and encrypted storage for secrets used in local development.&lt;/p&gt;

&lt;h2&gt;
  
  
  SecretOps and Local Development
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AoNHev2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekavxmtuelypomp0wp7u.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AoNHev2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekavxmtuelypomp0wp7u.jpeg" alt="Doppler SecretOps Platform" width="880" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Development scoped secrets simply don't exist in traditional solutions because &lt;a href="https://blog.doppler.com/siloed-secrets-and-security-theatre-why-we-need-secretops"&gt;secrets are siloed&lt;/a&gt; within the confines of their respective cloud or platform.&lt;/p&gt;

&lt;p&gt;While multi-cloud capable secret managers such as &lt;a href="https://blog.doppler.com/doppler-vs-hashicorp-vault"&gt;HashiCorp Vault&lt;/a&gt; showed great promise, the prohibitively steep learning curve and unavoidable complexity involved with fetching secrets create a significant barrier to adoption as teams are left with no incentive to switch.&lt;/p&gt;

&lt;p&gt;A SecretOps Platform builds upon the idea of centralized secrets storage but differs from existing solutions by providing a CLI and integrations for syncing secrets to any environment, machine, platform, or cloud secrets manager. It's the best of both worlds, providing a single source of truth for management while development teams choose the best secrets access method on a per-application basis.&lt;/p&gt;

&lt;p&gt;How does this help local development? In a SecretOps world, each application has a &lt;strong&gt;Development&lt;/strong&gt; environment specifically for use on developers' machines, solving the storage problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CDbAr_Z5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1654745217584/uRYxiX7It.jpg%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CDbAr_Z5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1654745217584/uRYxiX7It.jpg%2520align%3D%2522left%2522" alt="doppler-project.jpg" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://docs.doppler.com/docs/install-cli"&gt;Doppler CLI&lt;/a&gt; to demonstrate, let's dive in to explore the mechanics of dynamic secrets injection and ephemeral secrets files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic Secrets Injection
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.doppler.com/docs/install-cli"&gt;Doppler CLI&lt;/a&gt; uses the same secrets injection model as platforms such as Heroku and Cloudflare Workers by injecting the secrets as environment variables into the application process.&lt;/p&gt;

&lt;p&gt;You can use a command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; ./launch-app.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a long-running background process in a virtual machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; npm start &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the Doppler CLI inside a Docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;…
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="c"&gt;# Install Doppler CLI&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s2"&gt;"=https"&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 https://cli.doppler.com/install.sh | sh

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["doppler", "run", "--", "npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And inject environment variables consumed by Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possible to pipe secrets in .env file format to the Docker CLI, where it reads the output as a file using &lt;a href="https://tldp.org/LDP/abs/html/process-sub.html"&gt;process substitution&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; docker&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  my-awesome-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same technique applies to creating a Kubernetes secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic my-app-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-env-file&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; docker&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if a secrets file is what your application needs, we've got you covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ephemeral Secrets Files
&lt;/h2&gt;

&lt;p&gt;The Doppler CLI enables the mounting of ephemeral secrets files in .env, JSON, or a custom file format using a secrets template that is automatically cleaned up when the application process exits. Imagine how happy your Security team will be when they learn that secrets will never live on any developer's machines again!&lt;/p&gt;

&lt;p&gt;To mount an .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Node.js&lt;/span&gt;
doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; .env &lt;span class="nt"&gt;--&lt;/span&gt; npm start

&lt;span class="c"&gt;# PHP&lt;/span&gt;
doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; .env &lt;span class="nt"&gt;--&lt;/span&gt; php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To mount a JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; env.json &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can specify the format using &lt;code&gt;--mount-format&lt;/code&gt; if the file extension doesn't map to a known format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; app.config &lt;span class="nt"&gt;--mount-format&lt;/span&gt; json &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can use a custom template, e.g. configure Firebase functions emulator using a &lt;code&gt;.runtimeconfig.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create the template&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{ "doppler": {{tojson .}} }'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .runtimeconfig.json.tmpl

&lt;span class="c"&gt;# 2. Mount the .runtimeconfig.json and run the emulator&lt;/span&gt;
doppler run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; .runtimeconfig.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount-template&lt;/span&gt; .runtimeconfig.json.tmpl &lt;span class="nt"&gt;--&lt;/span&gt; firebase emulators:start &lt;span class="nt"&gt;--only&lt;/span&gt; functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even make things more secure by restricting the number of read requests using the --mount-max-reads option, e.g. caching PHP configuration which only requires the .env file to be read once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; .env &lt;span class="nt"&gt;--mount-max-reads&lt;/span&gt; 1 &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"php artisan config:cache &amp;amp;&amp;amp; php artisan serve"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're wondering what happens to the mounted file if the Doppler process is force killed, its file contents will appear to vanish instantly. The mounted file isn't a regular file at all, but a &lt;a href="https://en.wikipedia.org/wiki/Named_pipe#In_Unix"&gt;Unix named-pipe&lt;/a&gt;. If you've ever heard the phrase "&lt;a href="https://en.wikipedia.org/wiki/Everything_is_a_file"&gt;&lt;em&gt;everything is a file in Unix&lt;/em&gt;&lt;/a&gt;", you now have a better understanding of what that means.&lt;/p&gt;

&lt;p&gt;Named pipes are designed for inter-process communication while still using the file system as the access point. In this case, it's a client-server model, where your application is effectively sending a read request to the Doppler CLI. If the Doppler CLI is force killed, the .env file (named pipe) will still exist, but because no process is attached to it, requests to read the file will simply hang. Just delete the file from your terminal, and you're good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Thanks to SecretOps, we now have the workflows required to prevent secrets from ever living on a developer's machine again. All you need is dynamic secrets injection, ephemeral secrets files, and a single source of truth to pull it all together.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=how-to-prevent-secrets-from-ending-up-on-developers-machines-40cb"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>programming</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Manually Updating .env Files Isn't DevOps</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Tue, 07 Jun 2022 05:28:43 +0000</pubDate>
      <link>https://forem.com/doppler/manually-updating-env-files-isnt-devops-33i7</link>
      <guid>https://forem.com/doppler/manually-updating-env-files-isnt-devops-33i7</guid>
      <description>&lt;p&gt;Managing .env files and keeping them in sync across environments is painful when done manually.&lt;/p&gt;

&lt;p&gt;Doesn't it seem odd that Slack is still a common way for .env files to be synced between team members? Shouldn't we be concerned that syntax errors from .env file edits are so prevalent that dotenv linting tools are needed?&lt;/p&gt;

&lt;p&gt;It's time to bring the DevOps principles of automation and ephemeral resources to managing environment variables and .env files. This post is a compilation of Doppler's best tips and tricks to do just that as is broken into three sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Centralized Environment Variable Management&lt;/li&gt;
&lt;li&gt;Dynamically Injected Environment Variables&lt;/li&gt;
&lt;li&gt;Dynamically Created Ephemeral .env Files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although the examples are Doppler centric, the goal is to give you fresh ideas for improving app config and secrets automation in your workplace.  We've got lots of stuff to cover, so let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Centralized Environment Variable Management
&lt;/h2&gt;

&lt;p&gt;It's simply not possible to automate the syncing of environment variables across teams, hosting platforms, and development environments without a centralized source of truth at the organization level.&lt;/p&gt;

&lt;p&gt;Modern platforms such as Heroku and Vercel provide built-in environment variable storage, but unless all of your applications are hosted on a single platform, they can only function as a source of truth for individual applications. And if you're using cloud-based virtual machines such as those from DigitalOcean or AWS EC2, you're on your own to figure out environment variable storage and access.&lt;/p&gt;

&lt;p&gt;With the exception of Vercel's development scoped environment variables, first-class local development support is missing from every modern platform and cloud provider, explaining why so many teams still rely on .env files, even if not used in production. We know how crucial it is for local environments to closely mirror production, but it seems we're willing to make tradeoffs when it comes to environment variables.&lt;/p&gt;

&lt;p&gt;In the past, secrets managers such as &lt;a href="https://blog.doppler.com/doppler-vs-hashicorp-vault"&gt;HashiCorp Vault&lt;/a&gt; were seen as the solution. But replacing the simplicity of environment variables with complex SDKs often resulted in &lt;a href="https://blog.doppler.com/siloed-secrets-and-security-theatre-why-we-need-secretops"&gt;siloed secrets and teams going rogue&lt;/a&gt; by managing environment variables their own way. Cloud secrets managers also didn't improve the local development story.&lt;/p&gt;

&lt;p&gt;Essentially, we need a new way of managing environment variables that reflects the needs of modern application development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we need SecretOps
&lt;/h2&gt;

&lt;p&gt;SecretOps is designed for multi-cloud deployments and combines the strengths of traditional solutions while addressing their weaknesses.As a starting point, a SecretOps platform must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralize the storage and management of secrets&lt;/li&gt;
&lt;li&gt;Provide flexible and secure environment variable injection for every platform and cloud&lt;/li&gt;
&lt;li&gt;Provide a first-class local development experience&lt;/li&gt;
&lt;li&gt;Increase Developer productivity through secrets automation workflows&lt;/li&gt;
&lt;li&gt;Decrease the complexity of secrets management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;a href="https://www.doppler.com"&gt;Doppler&lt;/a&gt; as an example, we're tackling these requirements by providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fully-hosted solution with collaborative secrets management workflows and fine-grained access controls, all from a single centralized dashboard.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.doppler.com/docs/install-cli"&gt;CLI&lt;/a&gt; for injecting environment variables and ephemeral .env files in any environment, from local development to CI/CD, Docker, Kubernetes, Virtual Machines etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.doppler.com/docs/integrations"&gt;Integrations&lt;/a&gt; that sync secrets to platforms such as Vercel, Netlify, Heroku, and GitHub Secrets, so applications don't have to integrate with Doppler directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doppler's operating model is that managing secrets should be centralized, but fetching and syncing secrets should be tailored to every customer's needs. For example, many of our customers enjoy the Doppler dashboard's superior features and developer experience but sync secrets to Azure Key Vault so production secrets access remains as is. &lt;/p&gt;

&lt;p&gt;Our vision for SecretOps is constantly evolving. Our goal is to share, inspire, and help move our industry forward with new ideas that take secrets automation to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Dynamically Injected Environment Variables
&lt;/h2&gt;

&lt;p&gt;Using Doppler to illustrate, let's look at several methods for environment variable injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform Injected Environment Variables
&lt;/h3&gt;

&lt;p&gt;Having a platform or infrastructure tool to inject environment variables into your application is the best solution, as you can then say goodbye to .env files altogether.&lt;/p&gt;

&lt;p&gt;So while that removes the need for .env files in select platforms, additional tooling is needed for virtual machines, local development, and Kubernetes, just to name a few.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI Application Runner
&lt;/h3&gt;

&lt;p&gt;This method uses a CLI to run your application, injecting environment variables directly into the application process.&lt;/p&gt;

&lt;p&gt;Here is how the &lt;a href="https://docs.doppler.com/docs/install-cli"&gt;Doppler CLI&lt;/a&gt; can be used to inject environment variables into a Node.js application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; ./launch-app.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a long-running background process in a virtual machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; npm start &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the Doppler CLI inside a Docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;…

# Install Doppler CLI
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s2"&gt;"=https"&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 https://cli.doppler.com/install.sh | sh

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["doppler", "run", "--", "npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A CLI application runner with environment variable injection should have the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be open source&lt;/li&gt;
&lt;li&gt;Support every major operating system&lt;/li&gt;
&lt;li&gt;Installable via package managers&lt;/li&gt;
&lt;li&gt;Correctly passes signals to the application (e.g SIGINT) so your application can terminate gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;doppler run&lt;/code&gt; command is just one way of accessing secrets, and you can find more examples in our &lt;a href="https://docs.doppler.com/docs/accessing-secrets"&gt;CLI Secrets Access Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Docker Container Environment Variables
&lt;/h3&gt;

&lt;p&gt;This method injects environment variables into a Docker container at runtime, removing the temptation of embedding an .env file in the Docker image (yes, it happens) and avoiding the host creating and mounting the .env file inside the container.&lt;/p&gt;

&lt;p&gt;The Doppler CLI pipes secrets in .env file format to the Docker CLI where it reads the output as a file using &lt;a href="https://tldp.org/LDP/abs/html/process-sub.html"&gt;process substitution&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; docker&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  my-awesome-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we get the benefits of .env file configuration but without an .env file ever touching the disk.&lt;/p&gt;

&lt;p&gt;You can see other use cases in our &lt;a href="https://github.com/DopplerUniversity/docker-examples"&gt;docker-examples GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Compose Environment Variables
&lt;/h3&gt;

&lt;p&gt;Docker Compose differs from Docker as it accesses environment variables from the host when &lt;code&gt;docker compose up&lt;/code&gt; is run.&lt;/p&gt;

&lt;p&gt;Docker Compose sensibly requires you to define which environment variables to pass through to each service as variables such as &lt;code&gt;$PATH&lt;/code&gt; are host-specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.9'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
        &lt;span class="c1"&gt;# Host environment variables passed through to container&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AUTH_TOKEN&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_CONNECTION_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because of how Docker Compose access environment variables, we can use the Doppler CLI as an application runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other Docker Compose use cases can be found in our &lt;a href="https://github.com/DopplerUniversity/docker-examples/tree/main/docker-compose-pem-certificates"&gt;docker-examples GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes Environment Variables
&lt;/h3&gt;

&lt;p&gt;Kubernetes provides excellent support for injecting environment variables into containers using Key-Value pairs stored in a &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Kubernetes secret&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doppler provides two options for syncing secrets to Kubernetes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doppler CLI Created Secrets&lt;/li&gt;
&lt;li&gt;Doppler Kubernetes Operator Managed Secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Doppler CLI Created Secrets
&lt;/h3&gt;

&lt;p&gt;The first step is to create a generic Kubernetes secret (the first argument being the secret's name) where just like Docker, secrets in .env file format are piped to &lt;code&gt;kubectl&lt;/code&gt;  where it reads the output as a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic my-app-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-env-file&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; docker&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Doppler Kubernetes Operator Managed Secrets
&lt;/h3&gt;

&lt;p&gt;Our &lt;a href="https://docs.doppler.com/docs/kubernetes-operator"&gt;Kubernetes Operator&lt;/a&gt; is designed to scale and fully automate secrets syncing from Doppler to Kubernetes. Once installed and configured, it instantly creates and updates Kubernetes secrets as they change in Doppler with support for automatic deployment reloads if the secrets they're consuming have changed.&lt;/p&gt;

&lt;p&gt;As it's a more advanced solution that requires Kubernetes cluster administration experience, we won't be covering it here but check out our &lt;a href="https://docs.doppler.com/docs/kubernetes-operator"&gt;Kubernetes Operator documentation&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes Deployments and Environment Variables
&lt;/h3&gt;

&lt;p&gt;Injecting environment variables into a Deployment from the Key-Value pairs in a Kubernetes secret is done using the &lt;code&gt;envFrom&lt;/code&gt; property of a container spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awesome-app&lt;/span&gt;
          &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app-secret&lt;/span&gt; &lt;span class="c1"&gt;# Kubernetes secret name&lt;/span&gt;
&lt;span class="s"&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, you've learned some new tricks for environment variable injection! Now let's move on to.env files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamically Created Ephemeral .env Files
&lt;/h2&gt;

&lt;p&gt;Environment variable injection is always preferred, but sometimes, an .env file is the only workable solution.&lt;/p&gt;

&lt;p&gt;Protective measures such as locking down file ownership, file permissions, and heavily restricting shell access should go without saying. But the risk of .env files existing on the file system indefinitely has always been a concern and why we've been hesitant to recommend .env file usage in the past.&lt;/p&gt;

&lt;p&gt;But thanks to the Doppler CLI, we can now mount ephemeral .env files that are automatically cleaned up when the application exits. Imagine not having to worry about anyone in your company accidentally committing an .env file again! &lt;/p&gt;

&lt;p&gt;One of the most popular use cases is for PHP developers building Laravel applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; .env &lt;span class="nt"&gt;--&lt;/span&gt; php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file extension is used to automatically set the format (JSON format is also supported):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; secrets.json &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can set the format if the file extension doesn't map to a known type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; app.config &lt;span class="nt"&gt;--mount-format&lt;/span&gt; json &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To increase security, you can also restrict the number of reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--mount&lt;/span&gt; .env &lt;span class="nt"&gt;--mount-max-reads&lt;/span&gt; 1 &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"php artisan config:cache &amp;amp;&amp;amp; php artisan serve"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're wondering what happens to the mounted file if the Doppler process is force killed, its file contents will appear to vanish instantly. This is because the mounted file isn't a regular file at all, but a &lt;a href="https://en.wikipedia.org/wiki/Named_pipe#In_Unix"&gt;Unix named-pipe&lt;/a&gt;. If you've ever heard the phrase “&lt;a href="https://en.wikipedia.org/wiki/Everything_is_a_file"&gt;&lt;em&gt;everything is a file in Unix&lt;/em&gt;&lt;/a&gt;”, you now have a better understanding of what that means.&lt;/p&gt;

&lt;p&gt;Named pipes are designed for inter-process communication while still using the file system as the access point. In this case, it's a client-server model, where your application is effectively sending a read request to the Doppler CLI. In the event the Doppler CLI is force killed, the .env file (named pipe) will still exist, but because no process is attached to it, requests to read the file will simply hang.&lt;/p&gt;

&lt;p&gt;It's also named pipes that enable us to restrict the number of reads using the &lt;code&gt;--mount-max-reads&lt;/code&gt; option as once the limit is exceeded, the CLI simply removes the named pipe from the file system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I hope you take away some new automation ideas to bring back to your team so you can spend less time updating .env files and more time shipping software.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=manually-updating-env-files-isnt-devops-33i7"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>programming</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Why syncing .env files doesn’t scale for secrets management</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Wed, 19 Jan 2022 07:59:09 +0000</pubDate>
      <link>https://forem.com/doppler/why-syncing-env-files-doesnt-scale-for-secrets-management-5325</link>
      <guid>https://forem.com/doppler/why-syncing-env-files-doesnt-scale-for-secrets-management-5325</guid>
      <description>&lt;h2&gt;
  
  
  Learn why using a Universal Secrets Platform is the key to managing environment variables at scale and eliminates the need for syncing .env files.
&lt;/h2&gt;

&lt;p&gt;The benefits of using &lt;a href="https://12factor.net/config" rel="noopener noreferrer"&gt;environment variables to keep secrets out of source code&lt;/a&gt; are well established. But are .env files the best method for managing them?&lt;/p&gt;

&lt;p&gt;Secrets management has &lt;a href="https://blog.doppler.com/why-secrets-management-is-not-a-key-value-store" rel="noopener noreferrer"&gt;evolved beyond the limited Key-Value storage&lt;/a&gt; that .env files provide. However, most developers are either unaware of .env file’s shortcomings or have simply become numb to the pain from the many years of usage and lack of innovation.&lt;/p&gt;

&lt;p&gt;This post aims to highlight the risks of continuing to use .env files, why the major cloud vendors and hosting platforms offer a built-in secrets or environment variables store which can be used instead, and how secrets managers such as &lt;a href="https://thenewstack.io/secrets-management-doppler-or-hashicorp-vault/" rel="noopener noreferrer"&gt;Doppler and HashiCorp Vault&lt;/a&gt; are providing the much-needed management and &lt;a href="https://blog.doppler.com/the-missing-third" rel="noopener noreferrer"&gt;secrets automation layer&lt;/a&gt; on top of encrypted and access controlled secret storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief history of .env files
&lt;/h2&gt;

&lt;p&gt;The usage of environment variables and .env files for application config and secrets largely began around 2013. It was a long overdue and important step towards more secure secrets management practices.&lt;/p&gt;

&lt;p&gt;Libraries such as &lt;a href="https://pypi.org/project/python-dotenv/" rel="noopener noreferrer"&gt;Python’s dotenv&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;Node’s dotenv&lt;/a&gt; made it easy for developers to use .env files and environment variables for application configuration, giving developers a simple path to removing secrets from their source code for good.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642574498495%2FFQ9CKLwrYa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642574498495%2FFQ9CKLwrYa.png" alt="Node.js dotenv first commit - July 2013"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642574768773%2FZ35-1cZtx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642574768773%2FZ35-1cZtx.png" alt="Python dotenv first commit - June 2013"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To think that .env file usage has remained practically unchanged for over eight years is quite remarkable. It should come as no surprise then, that it’s time to say &lt;a href="https://blog.doppler.com/the-triumph-and-tragedy-of-env-files-zgkyjEpw1pGUVw" rel="noopener noreferrer"&gt;goodbye to .env files&lt;/a&gt; in exchange for alternatives that better meet the needs of modern application development.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problems with .env files
&lt;/h2&gt;

&lt;p&gt;Using .env files allowed us to move secrets out of source code. Unfortunately, they introduced a new set of challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scaling issues related to syncing .env file changes across environments and different cloud providers, increasing the risk of infrastructure misconfiguration and potential downtime.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy for .env files to contain syntax errors, requiring additional tools such as &lt;a href="https://github.com/dotenv-linter/dotenv-linter-KWTN2DR" rel="noopener noreferrer"&gt;dotenv-linter&lt;/a&gt; to be added to pro-commit hooks or GitHub checks.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sharing of unencrypted secrets in .env files over Slack when secrets change or new developers join a team risks breaking the principle of least privilege by exposing secrets to potentially unauthorized users.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.doppler.com/blog/goodbye-env-files" rel="noopener noreferrer"&gt;Inconsistent format of environment variables&lt;/a&gt; can cause issues, e.g. Docker and GitHub require unquoted values while other packages do not.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Patchy and inconsistent support for multi-line secrets such as TLS certificates, SSH keys, JSON, and YAML.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secrets used in multiple applications must be duplicated in every .env file (instead of &lt;a href="https://docs.doppler.com/docs/enclave-secrets#referencing-secrets" rel="noopener noreferrer"&gt;dynamic secrets referencing&lt;/a&gt;), making updating and rolling credentials tedious and repetitive.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If persisted to disk in plain text, they may be readable by unauthorized users with access to the system and threat actors if file restrictive file permissions aren't used.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to accidentally expose to &lt;a href="https://www.zdnet.com/article/botnets-have-been-silently-mass-scanning-the-internet-for-unsecured-env-files/" rel="noopener noreferrer"&gt;malicious bots&lt;/a&gt; if placed in the &lt;a href="https://dev.to/ewnx01/env-file-in-public-folder-is-security-risk-59ej-sS_SKFDo7"&gt;webroot of a web server&lt;/a&gt; or &lt;a href="https://trufflesecurity.com/blog/an-s3-bucket-worm-in-the-making-thousands-of-secrets-found-in-open-s3-buckets" rel="noopener noreferrer"&gt;S3 buckets&lt;/a&gt;.&lt;br&gt;&lt;br&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Local development environments break whenever team members forget to share updates that need to be applied to their .env files, e.g. when a feature branch is merged that requires a new secret.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's clear .env files have serious implications for application security Next, we’ll take a closer look at why the productivity impacts of using .env files may be worse than you think.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden productivity costs from using .env files
&lt;/h2&gt;

&lt;p&gt;Small repetitive problems, such as manually updating .env files across several servers as part of the deployment process, while perhaps initially frustrating and annoying, can easily become just an expected part of the application deployment lifecycle.&lt;/p&gt;

&lt;p&gt;While some developers would argue that the papercuts associated with using .env files are minor, one thing we can all agree on is that interruptions can have serious productivity implications for writing code.&lt;/p&gt;

&lt;p&gt;According to a recent study, &lt;a href="https://www.fastcompany.com/944128/worker-interrupted-cost-task-switching" rel="noopener noreferrer"&gt;the average lost time per serious interruption is 23 minutes&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You need to get into the mindset for development and then slowly trace back to where you left off. This can easily take more than 30 minutes." - Gloria Mark, Professor of Informatics California, Irvine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cost of misconfiguration errors is not just the time spent fixing an .env file related issue. It's the impact of unexpected context switching and the challenge of getting back into a state of deep work, also known as “flow”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why developers have ignored traditional secrets managers
&lt;/h2&gt;

&lt;p&gt;Traditional secrets managers such as &lt;a href="https://azure.microsoft.com/en-au/services/key-vault" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt; or &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt; provide encrypted storage and fine-grained access controls, specially designed for storing secrets such as API keys, database credentials, SSH keys, and TLS certificates.&lt;/p&gt;

&lt;p&gt;They are incredibly secure, robust, and enterprise-ready. But unfortunately, secret managers such as &lt;a href="https://blog.doppler.com/doppler-vs-hashicorp-vault" rel="noopener noreferrer"&gt;HashiCorp Vault are built for security teams, not developers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a result, they can be complex to implement correctly and often require secret-fetching implementation details to leak into application code—the exact opposite of the benefits afforded by using language-agnostic environment variables.&lt;/p&gt;

&lt;p&gt;Even security-minded developers motivated to use a traditional secrets managers have typically given up for one primary reason: Using .env files was much easier.&lt;/p&gt;

&lt;p&gt;Instead of environment variables, a vendor-specific SDK, platform integration, or custom application code for fetching secrets from a vendor’s API is often required.&lt;/p&gt;

&lt;p&gt;For example, take this &lt;a href="https://docs.aws.amazon.com/code-samples/latest/catalog/javascript-secrets-secrets_getsecretvalue.js.html" rel="noopener noreferrer"&gt;AWS Secrets Manager SDK for Node.js&lt;/a&gt; sample code for fetching secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load the AWS SDK&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&amp;lt;{{MyRegionName}}&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;secretName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&amp;lt;{{MySecretName}}&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;decodedBinarySecret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a Secrets Manager client&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecretsManager&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// In this sample we only handle the specific exceptions for the 'GetSecretValue' API.&lt;/span&gt;
&lt;span class="c1"&gt;// See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html&lt;/span&gt;
&lt;span class="c1"&gt;// We rethrow the exception by default.&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSecretValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;SecretId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;secretName&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DecryptionFailureException&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// Secrets Manager can't decrypt the protected secret text using the provided KMS key.&lt;/span&gt;
            &lt;span class="c1"&gt;// Deal with the exception here, and/or rethrow at your discretion.&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;InternalServiceErrorException&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// An error occurred on the server side.&lt;/span&gt;
            &lt;span class="c1"&gt;// Deal with the exception here, and/or rethrow at your discretion.&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;InvalidParameterException&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// You provided an invalid value for a parameter.&lt;/span&gt;
            &lt;span class="c1"&gt;// Deal with the exception here, and/or rethrow at your discretion.&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;InvalidRequestException&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// You provided a parameter value that is not valid for the current state of the resource.&lt;/span&gt;
            &lt;span class="c1"&gt;// Deal with the exception here, and/or rethrow at your discretion.&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ResourceNotFoundException&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// We can't find the resource that you asked for.&lt;/span&gt;
            &lt;span class="c1"&gt;// Deal with the exception here, and/or rethrow at your discretion.&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Decrypts secret using the associated KMS CMK.&lt;/span&gt;
        &lt;span class="c1"&gt;// Depending on whether the secret is a string or binary, one of these fields will be populated.&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SecretString&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecretString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SecretBinary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;decodedBinarySecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ascii&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Your code goes here. &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's this level of complexity compared with using environment variables that turns most developers off from the start.&lt;/p&gt;

&lt;p&gt;As product teams are incentivized to ship software and new features as fast as possible, migrating to a traditional secrets manager usually only occurs due to regulatory requirements or security mandates.&lt;/p&gt;

&lt;p&gt;But is it possible to still use environment variables for modern applications without .env files?&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern platforms with native environment variable storage
&lt;/h2&gt;

&lt;p&gt;Modern hosting platforms such as &lt;a href="https://netlify.com" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, &lt;a href="https://vercel.com/docs/concepts/projects/environment-variables" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, &lt;a href="https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;, &lt;a href="https://developers.cloudflare.com/workers/platform/environment-variables" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt;, &lt;a href="https://fly.io/docs/reference/secrets/" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt;, and &lt;a href="https://docs.railway.app/develop/variables" rel="noopener noreferrer"&gt;Railway&lt;/a&gt; all come with secure environment variable storage built-in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575262453%2FjLCF9p-T4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575262453%2FjLCF9p-T4.png" alt="Cloudflare Workers’ environment variable management UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This not only shows how easy it is to migrate away from .env files but confirms that environment variables are still the best language and platform-agnostic method for injecting secrets into an application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you need .env files for local development?
&lt;/h2&gt;

&lt;p&gt;It may seem we’re still reliant on .env files for local development if hosting platforms only manage environment variables for applications running on their infrastructure. But this is beginning to change.&lt;/p&gt;

&lt;p&gt;Every developer understands that inconsistencies between local and production environments are a recipe for unexpected issues. That’s why a USP provides first-class support for managing secrets in every environment. A trend modern hosting providers are also beginning to follow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575297042%2FLYFYptizC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575297042%2FLYFYptizC.png" alt="Vercel development environment variable UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vercel.com/docs/concepts/projects/environment-variables#development-environment-variables-CetGQl" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, for example, offers environment variable storage specifically for local development that is fetched and injected into the Node.js application via the Vercel CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vercel dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what about developers using hosting providers without such functionality? This is where a USP such as &lt;a href="https://doppler.com/" rel="noopener noreferrer"&gt;Doppler&lt;/a&gt; fills in the gaps, eliminating the need for manually managed .env files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642577688122%2FUDNB7cg5S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642577688122%2FUDNB7cg5S.png" alt="Doppler project environments"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once developers have &lt;a href="https://docs.doppler.com/docs/create-project" rel="noopener noreferrer"&gt;created a project&lt;/a&gt; and &lt;a href="https://docs.doppler.com/docs/install-cli" rel="noopener noreferrer"&gt;installed the Doppler CLI&lt;/a&gt;, secrets can be injected as environment variables into any application process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doppler run &lt;span class="nt"&gt;--&lt;/span&gt; npm run firebase-local-functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developer tooling is rapidly improving to provide a better integrated local development experience that will eliminate the differences between local and production environments and the need for manually managed .env files on developer machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Universal approach to taming secret sprawl
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575414749%2FAmayL8-jj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575414749%2FAmayL8-jj.jpeg" alt="Doppler Universal Secrets Platform diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.doppler.com/secrets-sprawl-is-hurting-your-productivity-heres-how-to-fix-it" rel="noopener noreferrer"&gt;Taming&lt;/a&gt; &lt;a href="https://blog.doppler.com/secrets-sprawl-is-hurting-your-productivity-heres-how-to-fix-it" rel="noopener noreferrer"&gt;secret sprawl&lt;/a&gt; is a growing challenge for every development team and one that is only made worse as the number of .env files increase. We need an entirely new approach to secrets management that goes beyond incremental improvements—A Universal Secrets Platform.&lt;/p&gt;

&lt;p&gt;Taking a “Universal” approach means being able to manage and sync secrets to every application on any platform by avoiding the problems associated with &lt;a href="https://blog.doppler.com/siloed-secrets-and-security-theatre-why-we-need-universal-secrets-management" rel="noopener noreferrer"&gt;siloed secrets&lt;/a&gt; and antiquated solutions that don’t scale such as trying to sync dotenv files across platforms.&lt;/p&gt;

&lt;p&gt;This can be achieved through a hub-and-spoke model where the USP acts as a single source of truth for secret storage and management with &lt;a href="https://www.doppler.com/integrations" rel="noopener noreferrer"&gt;integrations&lt;/a&gt; automatically syncing secrets when they change to any external platform, including other secrets managers.&lt;/p&gt;

&lt;p&gt;We hope our vision of a &lt;a href="https://blog.doppler.com/siloed-secrets-and-security-theatre-why-we-need-universal-secrets-management#an-automation-driven-approach-universal-secrets-management" rel="noopener noreferrer"&gt;Universal Secrets Platform&lt;/a&gt; serves as inspiration for other secrets managers to create a more developer-friendly experience in order to make migrating away from .env files a more attractive option to developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We don’t need to sync .env files. We need the developer-specific workflows that a Universal Secrets Platform such as &lt;a href="https://www.doppler.com" rel="noopener noreferrer"&gt;Doppler&lt;/a&gt; can provide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575672271%2FLMHpHGZS3E.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1642575672271%2FLMHpHGZS3E.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simplicity of .env files, while attractive at first, is also its greatest weakness. The demands of modern application development and the explosion of microservices across multiple clouds and platforms provide scalability challenges .env files simply can’t address.&lt;/p&gt;

&lt;p&gt;The use of .env files was certainly an improvement over hard-coded secrets. But better options now exist for secrets management, and not only will your infrastructure be more secure without .env files, you’ll be more productive without them too.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=why-syncing-env-files-doesnt-scale-for-secrets-management-5325" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>programming</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>Doppler: How to Set Environment Variables for a Python Django Application using Apache and mod_wsgi in Docker</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Tue, 17 Aug 2021 07:00:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-how-to-set-environment-variables-for-a-python-django-application-using-apache-and-modwsgi-in-docker-3oh1</link>
      <guid>https://forem.com/doppler/doppler-how-to-set-environment-variables-for-a-python-django-application-using-apache-and-modwsgi-in-docker-3oh1</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CjRMKidT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqn44aw7amct38itfxaz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CjRMKidT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqn44aw7amct38itfxaz.jpg" alt="How to Set Environment Variables for a Python Django Application using Apache and mod_wsgi in Docker" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using environment variables to configure Django and other Python applications is awesome, but using them with Apache and mod_wsgi in Docker is a tricky thing to get right.&lt;/p&gt;

&lt;p&gt;That's why I created this step-by-step tutorial and &lt;a href="https://file+.vscode-resource.vscode-webview.net/Users/rb/Projects/django-apache-mod-wsgi/%5Bhttps://github.com/DopplerUniversity/django-apache-mod-wsgi%5D"&gt;sample application&lt;/a&gt; to put all the info you need in one place.&lt;/p&gt;

&lt;p&gt;Although this tutorial is for Docker and Django, the same steps apply, whether you're using a Virtual Machine or a different Python framework.&lt;/p&gt;

&lt;p&gt;Prefer just to read the code? Head to the accompanying repository at &lt;a href="https://github.com/DopplerUniversity/django-apache-mod-wsgi"&gt;https://github.com/DopplerUniversity/django-apache-mod-wsgi&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Variables, Apache and mod_wsgi
&lt;/h2&gt;

&lt;p&gt;When hosting a Python WSGI compatible framework like Django in Apache with mod_wsgi, the only environment variables populated in the &lt;strong&gt;os.environ&lt;/strong&gt; dictionary are those that exist in the environment of the script that starts Apache. But instead of having to mess with Apache's service manager settings (e.g. &lt;strong&gt;systemd&lt;/strong&gt; or &lt;strong&gt;systemctl&lt;/strong&gt;), there's a better way.&lt;/p&gt;

&lt;p&gt;Most Apache distributions provide a shell script specifically for the purpose of setting environment variables that will be made available to modules such as mod_wsgi.&lt;/p&gt;

&lt;p&gt;It's then a matter of knowing the location of this shell script as it can be different depending on the Linux distribution. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Debian/Ubuntu: &lt;strong&gt;/etc/apache2/envvars&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  CentOS: &lt;strong&gt;/etc/sysconfig/httpd&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll be using the &lt;a href="https://github.com/docker-library/python/blob/master/3.9/buster/slim/Dockerfile"&gt;python:3.9-slim-buster Docker image&lt;/a&gt; is Debian based.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appending App Config and Secrets to the Environment Variables File
&lt;/h2&gt;

&lt;p&gt;Essentially, it boils down to fetching the secrets as key/value pairs and writing them to the &lt;strong&gt;envvars&lt;/strong&gt; file in the typical shell environiment variables format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FIRST_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"The"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LAST_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Mandalorion"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But where from and how do we fetch the app config and secrets to populate that file?&lt;/p&gt;

&lt;p&gt;As I'm the Developer Advocate for Doppler, I'll start with a &lt;a href="https://docs.doppler.com/docs/enclave-installation"&gt;Doppler CLI&lt;/a&gt; example, but the mechanics of "fetch secrets, then append to file" can easily be adapted.&lt;/p&gt;

&lt;p&gt;First, you would need to set up your project in Doppler and you can use the following button to get you started if you want to follow along.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashboard.doppler.com/workplace/template/import?template=https%3A%2F%2Fgithub.com%2FDopplerUniversity%2Fdjango-apache-mod-wsgi%2Fblob%2Fmain%2Fdoppler-template.yaml&amp;amp;_ga=2.263293196.1681842210.1630885780-1034093108.1623672779"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-IDcarx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/DopplerUniversity/app-config-templates/main/doppler-button.svg" alt="Import to Doppler" width="176" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then use the Doppler CLI inside the Docker container to fetch the secrets (requires a &lt;strong&gt;DOPPLER_TOKEN&lt;/strong&gt; environment variable with a &lt;a href="https://docs.doppler.com/docs/enclave-service-tokens"&gt;Service Token&lt;/a&gt; value):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Transform JSON key:value pairs into export statements using jq&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[info]: Appending environment variables to /etc/apache/envvars using Doppler CLI'&lt;/span&gt;
    doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;$'. | to_entries[] | "export &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s1"&gt;.key)=&lt;/span&gt;&lt;span class="se"&gt;\'\(&lt;/span&gt;&lt;span class="s1"&gt;.value)&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/envvars
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice I've used single quotes, not double quotes around the values?&lt;/p&gt;

&lt;p&gt;That's because it gives you the flexibility of storing secrets with double quotes such as JSON in Doppler which you could use for example, to &lt;a href="https://github.com/DopplerUniversity/django-apache-mod-wsgi/blob/main/src/doppler/settings.py#L29"&gt;dynamically set Django's ALLOWED_HOSTS&lt;/a&gt; setting dynamically for any environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ALLOWED_HOSTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'ALLOWED_HOSTS'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also use a &lt;strong&gt;.env&lt;/strong&gt; file but &lt;a href="https://www.doppler.com/blog/the-triumph-and-tragedy-of-env-files"&gt;I wouldn't recommend it&lt;/a&gt; and instead, I'd look into using a &lt;a href="https://www.doppler.com/blog/what-is-a-secrets-manager"&gt;secrets manager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But that aside, here is how you could do it using an &lt;strong&gt;.env&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.env"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[info]: Appending environment variables to /etc/apache/envvars from .env file'&lt;/span&gt;
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/envvars
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we know how to pass environment variables from Apache to mod_wsgi, let's move onto getting this working in Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Configuration for Apache and mod_wsgi
&lt;/h2&gt;

&lt;p&gt;Let's breakdown the task of configuring a Python Django Application using Apache and mod_wsgi in Docker into three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Custom Start Script&lt;/li&gt;
&lt;li&gt; Apache Site Config&lt;/li&gt;
&lt;li&gt; Dockerfile&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you only want to see the working code examples, head to the accompanying repository at &lt;a href="https://github.com/DopplerUniversity/django-apache-mod-wsgi"&gt;https://github.com/DopplerUniversity/django-apache-mod-wsgi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As this isn't a Docker or Apache tutorial, I won't be diving too deeply into the Dockerfile or Apache site config file, but if you've got questions, head over to the &lt;a href="https://community.doppler.com/"&gt;Doppler community forum&lt;/a&gt; and I'll be able to help you there.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Custom Start Script
&lt;/h3&gt;

&lt;p&gt;Running your application in Docker is usually a case of setting the &lt;strong&gt;CMD,&lt;/strong&gt; for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "src/app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it's trickier here as we first need to append the environment variables to &lt;code&gt;/etc/apache2/envvars&lt;/code&gt; before running Apache.&lt;/p&gt;

&lt;p&gt;As this requires multiple commands, we'll create a custom script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# apache-doppler-start&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'ServerName localhost'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/apache2.conf &lt;span class="c"&gt;# Silence FQDN warning&lt;/span&gt;

&lt;span class="c"&gt;# Doppler CLI&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[info]: Appending environment variables to /etc/apache/envvars from Doppler CLI'&lt;/span&gt;
    doppler secrets download &lt;span class="nt"&gt;--no-file&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;$'. | to_entries[] | "export &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s1"&gt;.key)=&lt;/span&gt;&lt;span class="se"&gt;\'\(&lt;/span&gt;&lt;span class="s1"&gt;.value)&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/envvars
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Mounted .env file&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.env"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[info]: Appending environment variables to /etc/apache/envvars from .env file'&lt;/span&gt;
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/envvars
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Run Apache&lt;/span&gt;
apache2ctl &lt;span class="nt"&gt;-D&lt;/span&gt; FOREGROUND
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Apache Site Config
&lt;/h3&gt;

&lt;p&gt;Here is an example Apache site config file for a Django application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# wsgi.conf
&lt;/span&gt;&amp;lt;&lt;span class="n"&gt;VirtualHost&lt;/span&gt; *:&lt;span class="m"&gt;80&lt;/span&gt;&amp;gt;
    &lt;span class="n"&gt;ServerName&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;-&lt;span class="n"&gt;apache&lt;/span&gt;-&lt;span class="n"&gt;mod&lt;/span&gt;-&lt;span class="n"&gt;wsgi&lt;/span&gt;
    &lt;span class="n"&gt;ServerAlias&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;-&lt;span class="n"&gt;apache&lt;/span&gt;-&lt;span class="n"&gt;mod&lt;/span&gt;-&lt;span class="n"&gt;wsgi&lt;/span&gt;
    &lt;span class="n"&gt;ServerAdmin&lt;/span&gt; &lt;span class="n"&gt;webmaster&lt;/span&gt;@&lt;span class="n"&gt;doppler&lt;/span&gt;

    &lt;span class="c"&gt;# Defining `WSGIDaemonProcess` and `WSGIProcessGroup` triggers daemon mode
&lt;/span&gt;    &lt;span class="n"&gt;WSGIDaemonProcess&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;-&lt;span class="n"&gt;apache&lt;/span&gt;-&lt;span class="n"&gt;mod&lt;/span&gt;-&lt;span class="n"&gt;wsgi&lt;/span&gt; &lt;span class="n"&gt;processes&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;=&lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;-&lt;span class="n"&gt;name&lt;/span&gt;=%{&lt;span class="n"&gt;GROUP&lt;/span&gt;} &lt;span class="n"&gt;python&lt;/span&gt;-&lt;span class="n"&gt;path&lt;/span&gt;=/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;local&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;python3&lt;/span&gt;.&lt;span class="m"&gt;9&lt;/span&gt;/&lt;span class="n"&gt;site&lt;/span&gt;-&lt;span class="n"&gt;packages&lt;/span&gt;:/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;app&lt;/span&gt;    
    &lt;span class="n"&gt;WSGIProcessGroup&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;-&lt;span class="n"&gt;apache&lt;/span&gt;-&lt;span class="n"&gt;mod&lt;/span&gt;-&lt;span class="n"&gt;wsgi&lt;/span&gt;
    &lt;span class="n"&gt;WSGIScriptAlias&lt;/span&gt; / /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;app&lt;/span&gt;/&lt;span class="n"&gt;doppler&lt;/span&gt;/&lt;span class="n"&gt;wsgi&lt;/span&gt;.&lt;span class="n"&gt;py&lt;/span&gt;

    &amp;lt;&lt;span class="n"&gt;Directory&lt;/span&gt; /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;app&lt;/span&gt;/&lt;span class="n"&gt;doppler&lt;/span&gt;/&amp;gt;
        &amp;lt;&lt;span class="n"&gt;Files&lt;/span&gt; &lt;span class="n"&gt;wsgi&lt;/span&gt;.&lt;span class="n"&gt;py&lt;/span&gt;&amp;gt;
            &lt;span class="n"&gt;Require&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;granted&lt;/span&gt;
        &amp;lt;/&lt;span class="n"&gt;Files&lt;/span&gt;&amp;gt;
    &amp;lt;/&lt;span class="n"&gt;Directory&lt;/span&gt;&amp;gt;

    &lt;span class="c"&gt;# Redirect all logging to stdout for Docker
&lt;/span&gt;    &lt;span class="n"&gt;LogLevel&lt;/span&gt; &lt;span class="n"&gt;INFO&lt;/span&gt;
    &lt;span class="n"&gt;ErrorLog&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt;
    &lt;span class="n"&gt;TransferLog&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt;
&amp;lt;/&lt;span class="n"&gt;VirtualHost&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Dockerfile
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Dockerfile&lt;/strong&gt; is reasonably straightforward, installing the Doppler CLI and Apache dependencies before copying the Django source code, custom script, and Apache site config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.9-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED 1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE 1&lt;/span&gt;

&lt;span class="c"&gt;# Install Doppler CLI and related dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nt"&gt;-qq&lt;/span&gt; update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https ca-certificates curl gnupg jq &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;curl &lt;span class="nt"&gt;-sLf&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s2"&gt;"=https"&lt;/span&gt; &lt;span class="s1"&gt;'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key'&lt;/span&gt; |  apt-key add - &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb https://packages.doppler.com/public/cli/deb/debian any-version main"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/doppler-cli.list &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;apt-get &lt;span class="nt"&gt;-qq&lt;/span&gt; update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install &lt;/span&gt;doppler

&lt;span class="c"&gt;# Install Apache and related dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt; apache2 apache2-dev libapache2-mod-wsgi-py3 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get clean &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get remove &lt;span class="nt"&gt;--purge&lt;/span&gt; &lt;span class="nt"&gt;--auto-remove&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements*.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Application source&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ ./&lt;/span&gt;

&lt;span class="c"&gt;# Custom CMD script&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; apache-doppler-start /usr/local/bin/&lt;/span&gt;

&lt;span class="c"&gt;# Apache site config&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; wsgi.conf /etc/apache2/sites-enabled/000-default.conf&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80 443&lt;/span&gt;

&lt;span class="c"&gt;# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop&lt;/span&gt;
&lt;span class="k"&gt;STOPSIGNAL&lt;/span&gt;&lt;span class="s"&gt; SIGWINCH&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["apache-doppler-start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With all the pieces in place, we can nowbuild the Docker image (clone the &lt;a href="https://github.com/DopplerUniversity/django-apache-mod-wsgi"&gt;sample repository&lt;/a&gt; to follow along):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image build &lt;span class="nt"&gt;-t&lt;/span&gt; django-apache-mod-wsgi:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're ready to run the container!&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Django Application with Apache and mod_wsgi in Docker
&lt;/h2&gt;

&lt;p&gt;We'll start with a Doppler example, then with an &lt;strong&gt;.env&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;With Doppler, you'll first need to set a &lt;strong&gt;DOPPLER_TOKEN&lt;/strong&gt; environment variable to the value of a &lt;a href="https://docs.doppler.com/docs/enclave-service-tokens"&gt;Service Token&lt;/a&gt;. This is what provides read-only access to a specific Doppler config in production environments.&lt;/p&gt;

&lt;p&gt;Usually, this would be securely set by your deployment environment (e.g. GitHub Action Secret) but for completeness and simplicity, we'll set it manually below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOPPLER_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dp.st.xxxx"&lt;/span&gt; &lt;span class="c"&gt;# Service token value created from Doppler dashboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; doppler-apache-mod-wsgi &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DOPPLER_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOPPLER_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    django-apache-mod-wsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .env File
&lt;/h3&gt;

&lt;p&gt;To run the &lt;strong&gt;.env&lt;/strong&gt; file version, we'll use the &lt;strong&gt;sample.env file&lt;/strong&gt; from the &lt;a href="https://github.com/DopplerUniversity/django-apache-mod-wsgi/blob/main/sample.env"&gt;sample repository&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# sample.env&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'doppler.settings'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'yes'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ALLOWED_HOSTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'["*"]'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'bf5e1b31-6ba7-48e2-9175-f2293671e6df'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then to run the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container run &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; dotenv-apache-mod-wsgi &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/sample.env:/usr/src/app/.env &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="se"&gt;\&lt;/span&gt;
    django-apache-mod-wsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Nice work in making it to the end!&lt;/p&gt;

&lt;p&gt;Now you know how to configure Python applications hosted with Apache and mod_wsgi running in Docker using environment variables for app configuration and secrets.&lt;/p&gt;

&lt;p&gt;Feedback is welcome and you can reach us on &lt;a href="https://twitter.com/dopplerhq"&gt;Twitter&lt;/a&gt;, our &lt;a href="https://community.doppler.com/"&gt;Community forum&lt;/a&gt;, or send me an email at &lt;a href="//mailto:ryan.blunden@doppler.com"&gt;ryan.blunden@doppler.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-how-to-set-environment-variables-for-a-python-django-application-using-apache-and-modwsgi-in-docker-3oh1"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>apache</category>
      <category>docker</category>
    </item>
    <item>
      <title>Why Secrets Management Is NOT Just a Key-Value Store</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Tue, 10 Aug 2021 00:28:34 +0000</pubDate>
      <link>https://forem.com/doppler/why-secrets-management-is-not-just-a-key-value-store-27i4</link>
      <guid>https://forem.com/doppler/why-secrets-management-is-not-just-a-key-value-store-27i4</guid>
      <description>&lt;p&gt;For those who’ve managed secrets via .env files or have briefly looked at secrets managers such as  &lt;a href="https://www.doppler.com/blog/doppler-vs-hashicorp-vault"&gt;HashiCorp Vault&lt;/a&gt; , it’s easy to get the impression that “secrets management” is simply the storage and retrieval of secrets from a Key-Value data store.&lt;/p&gt;

&lt;p&gt;But recurring tasks such as instantly comparing secret values between environments when troubleshooting, or being alerted when a new secret is introduced are crucial features every secrets manager should provide. Without them, development teams are forced to cobble together their own solutions, often in silos on a per-application basis.&lt;/p&gt;

&lt;p&gt;The reality is that .env files and traditional secrets managers were never designed to meet the needs of modern application development teams, where microservices and multi-cloud deployments are the new normal.&lt;/p&gt;

&lt;p&gt;Essentially, we want the flexibility of a Key-Value secret storage solution but with an operating model and feature set that decreases complexity, increases developer productivity, and standardizes how secrets are managed for applications in every part of the business.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore the most important aspects of secrets management and why Key-Value storage is only the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access Control and Permissions
&lt;/h2&gt;

&lt;p&gt;Who needs access to secrets? What should their level of permissions be and which applications and environments should they have access to? Is access provided according to org structure or application ownership?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qdgOUrjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057618522/BmJjxHL1l.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qdgOUrjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057618522/BmJjxHL1l.jpeg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If access controls aren’t fine-grained enough, you risk violating the principle of least privilege but if too rigid, can impede the ability of teams to embrace a culture of DevOps and “shifting left” to bring security earlier into the application development process.&lt;/p&gt;

&lt;p&gt;The following is a starting point for secret and access permission requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ability to segment parts of the business into separate workplaces so for example, an acquired start-up can onboard to using the secrets manager while remaining entirely separate from the organization’s existing secrets.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consider how access will be managed across cloud and platform boundaries, e.g. AWS, GCP, and Vercel?&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ability to grant access to secrets for specific applications and environments within each application.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In most cases, developers should only have access to secrets belonging to their applications and only for the appropriate environment (e.g., dev and test). DevSecOps will have greater permission levels so that they can work cross-functionally in any environment.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Temporary secrets access to external entities (e.g. contractors) should be scoped to a subset of application secrets via an auth/service token.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One-off sharing of secrets to external entities (e.g. TLS Certificates to an external firm) should be captured in an activity log and sent via encrypted means using a service such as &lt;a href="https://share.doppler.com/"&gt;Doppler Share&lt;/a&gt;.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mandated MFA for secrets manager dashboard access.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Privilege elevation (e.g. developer viewing production secrets) be time-limited and captured in an activity log.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Seamless integration with existing Single Sign-on solutions such as SAML and SCIM to allow default access permission levels to be assigned based on role and/or group membership.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A further consideration is restricting machine access to a CIDR IP address range, preventing leaked service tokens from use outside of trusted corporate and cloud networks.&lt;/p&gt;

&lt;p&gt;There’s a lot to consider for access control and permissions and your secrets manager should make it straightforward to meet these requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning and Rollback
&lt;/h2&gt;

&lt;p&gt;Versioning and the ability to roll back changes is a must and shouldn’t come at the cost of reduced performance or additional storage fees.&lt;/p&gt;

&lt;p&gt;While most secret managers support versioning and rollback, it’s critical to evaluate the actual process.&lt;/p&gt;

&lt;p&gt;Being able to roll back a misconfigured change in a single click with support for triggering an automatic application reload or deploy is something teams should come to expect from a secrets manager, enabling a misconfiguration fix to be applied in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret Documentation
&lt;/h2&gt;

&lt;p&gt;There are wildly different perspectives on documenting code, but the one thing all developers can agree upon is that useful code comments provide context that explains the “why”, not just what the code does.&lt;/p&gt;

&lt;p&gt;Such context and comments can be especially valuable for secrets, but where should this be documented?&lt;/p&gt;

&lt;p&gt;A reasonable option is a README or inline code comments where the secret is in use, however, the context may be lost if a secret’s value is changed in your secrets manager by someone who hasn’t viewed the code.&lt;/p&gt;

&lt;p&gt;Being able to notate secrets directly in the secrets manager reduces the chance of misconfiguration errors that could’ve been easily avoided, had the appropriate comments (and context) be provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development
&lt;/h2&gt;

&lt;p&gt;The closer your development environment is to production, the less likely unexpected problems are to arise during deployment, which is why secrets should be accessed the same way in development, as they are in production.&lt;/p&gt;

&lt;p&gt;Your secrets manager should treat Development as a proper environment in its own right and be responsible for supplying secrets to developers locally. This saves each developer from manually updating and patching their own environment variables list in their IDE or .env file.&lt;/p&gt;

&lt;p&gt;Development environments also present a unique challenge where secret values will often be specific to each developer, e.g. when working on a feature branch that uses a new secret.&lt;/p&gt;

&lt;p&gt;Developers need the flexibility to override secrets during development without affecting other developers on the team. This is an essential feature a secrets manager should provide to ensure secrets access remains consistent for every environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Single Unified View for Application Configuration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b5ja_SSc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057620203/Rs6Mz_GQf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b5ja_SSc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057620203/Rs6Mz_GQf.png" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the reasons developers love .env files is that it gives them a single unified view of how an application is configured as they tend to hold both secrets and config data such as API keys, port bindings, and feature flags.&lt;/p&gt;

&lt;p&gt;When assessing the transition from .env files to a secrets manager, teams are often confused as to whether secrets and config should be separated. For example, should secrets be migrated to the secrets manager while config continues to exist in .env files?&lt;/p&gt;

&lt;p&gt;While secret managers are designed to store sensitive data, it makes sense to store both config and secrets together, as there is no logistical or financial advantage gained by separating them. &lt;/p&gt;

&lt;p&gt;If secrets and config are simply displayed as Key-Value pairs in the secrets manager’s dashboard, it’s up to the viewer to define a way to visually organize secrets by application, then further segment into separate environments.&lt;/p&gt;

&lt;p&gt;Your secrets manager should provide a simple built-in mechanism to view secrets for a specific application and environment, and storing config as well as secrets in your secret manager ensures you’ll still get a single unified view of how your application is configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce Friction to Increase Adoption
&lt;/h2&gt;

&lt;p&gt;While implementing a secrets manager is often a security endeavor, it's better viewed as a productivity one, as every project team can appreciate time and effort saved. And if security posture is improved, well even better!&lt;/p&gt;

&lt;p&gt;But if the focus is on security at the cost of productivity (even if short term), gaining widespread adoption will be significantly more difficult, as product teams are incentivized to ship new features, not improve security (unless mandated to do so).&lt;/p&gt;

&lt;p&gt;Therefore, the secrets manager that is likely to have the biggest impact in improving security, is the one that provides a nearly frictionless experience for teams to implement and use on a daily basis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing Misconfiguration Issues
&lt;/h2&gt;

&lt;p&gt;Misconfiguration issues are much easier to deal with if caught before changes are applied in production. To achieve this, developers and DevOps Engineers need access to a secrets activity log that pushes secret events to where they naturally communicate such as a Slack or Microsoft Teams channel.&lt;/p&gt;

&lt;p&gt;The secrets management dashboard should also provide indicators of potential problems such as if a secret was added to the staging environment but not production, as well as troubleshooting features such as the ability to instantly compare a secret value across every environment.&lt;/p&gt;

&lt;p&gt;A secrets manager must therefore help to prevent known issues associated with application configuration and deployment scenarios to increase production stability and reduce time spent firefighting issues caused by misconfiguration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate Application Reload and Redeployment
&lt;/h2&gt;

&lt;p&gt;The Infrastructure as Code movement put automation at the heart of deployment workflows and an essential feature of any secrets manager should be the option to automatically trigger an application to reload or redeploy if its config or secrets change.&lt;/p&gt;

&lt;p&gt;This again highlights the need for secret managers to go beyond simply Key-Value storage, as, as they must know which application to trigger a redeployment for, based on the application and environment the changed secret belongs to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets Management Goes Beyond Secret Storage
&lt;/h2&gt;

&lt;p&gt;By now, it should be clear that secrets management extends beyond the secure storage of secrets as Key-Value pairs. We’ve touched on processes, best practices, as well as various risks, challenges, and issues that can arise and what a secrets manager should reasonably do to mitigate them.&lt;/p&gt;

&lt;p&gt;These relate to key elements of storage, access, visibility, integrations, workflows, and troubleshooting processes for not just secrets management, but application configuration generally.&lt;/p&gt;

&lt;p&gt;Doppler is simply the result of building the universal secrets manager we wish had but simply didn’t exist.&lt;/p&gt;

&lt;p&gt;Doppler enables you to stop using old ways to store, share, and access secrets via .env files.&lt;/p&gt;

&lt;p&gt;It's your centralized source of truth for managing secrets across multiple clouds, platforms, and packaging formats, from cloud-native build packs to containers, serverless, and more.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=why-secrets-management-is-not-just-a-key-value-store-27i4"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>What is a Secrets Manager?</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Tue, 10 Aug 2021 00:18:02 +0000</pubDate>
      <link>https://forem.com/doppler/what-is-a-secrets-manager-574k</link>
      <guid>https://forem.com/doppler/what-is-a-secrets-manager-574k</guid>
      <description>&lt;p&gt;Secrets and credentials management is widely considered to be the most overlooked aspect of software development. Many teams struggle daily to organize and sync secrets between environments, with manually maintained .env files being one of the most common sources of frustration for developers and DevSecOps.&lt;/p&gt;

&lt;p&gt;Not only does this impact developer productivity, but without a centralized data store with fine-grained access control and audit logs, secrets like APIs and login credentials are at risk of exploitation.&lt;/p&gt;

&lt;p&gt;Security teams want developers to “shift left” and incorporate security best practices into the application development lifecycle and tooling. Still, too often, .env files fall into the “if it ain’t broke, don’t fix it” category” simply because it appears to be the best solution available.&lt;/p&gt;

&lt;p&gt;The good news is that a relatively new breed of security infrastructure tooling known as “Secrets Managers” are here to make .env files and other insecure secrets storage practices a thing of the past.&lt;/p&gt;

&lt;p&gt;In this article, we’ll unpack Secrets Managers, what they do, why they matter, and what to look for when choosing a Secrets Manager for your team and organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Secrets Are At Risk – And Here’s Why
&lt;/h2&gt;

&lt;p&gt;For example, a developer is working on an infrastructure-as-code solution that requires access to database credentials and API keys as part of the application deployment process.&lt;/p&gt;

&lt;p&gt;A straightforward solution could be to store the required secrets in a plain text file that the deployment code can consume. The developer knows it’s not ideal, but it works, and they’re under pressure to finish the job so they can get back to shipping new features. At that moment, it’s the simplest solution. And sometimes, simplicity wins out, but for the wrong reasons.&lt;/p&gt;

&lt;p&gt;Some organizations consider the simplicity of .env files to be its strength, but the underlying maintenance issues such as the manual syncing of changes between environments, as well as the security risk from unencrypted secrets storage on application servers, often go unnoticed or ignored.&lt;/p&gt;

&lt;p&gt;In the same way, we learned that storing secrets unencrypted in application code was insecure; we need to apply that same thinking to the storage of unencrypted secrets on the file system.&lt;/p&gt;

&lt;p&gt;This is why forward-thinking organizations are embracing Secret Managers, so let’s now focus on what they are and how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Secrets Manager?
&lt;/h2&gt;

&lt;p&gt;Today, organizations depend on commercial, open-source, and internally developed applications, automated IT infrastructure, and DevOps methodologies to support innovation and business growth. Regardless of your development environment, every automation tool, script, and application you use consumes data of a sensitive nature in order to perform its job.&lt;/p&gt;

&lt;p&gt;A Secrets Manager is a storage and management solution for storing any type of sensitive data your application requires, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Database credentials&lt;/li&gt;
&lt;li&gt;  API keys&lt;/li&gt;
&lt;li&gt;  SSH Keys&lt;/li&gt;
&lt;li&gt;  TLS Certifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Secrets Manager provides a centralized source of truth that empowers organizations to securely and efficiently store and control access based on role, machine identity, environment, and other factors to apply the principle of least privilege.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Secrets Managers Matter
&lt;/h2&gt;

&lt;p&gt;Cybercriminals often seek to &lt;a href="https://www.zdnet.com/article/botnets-have-been-silently-mass-scanning-the-internet-for-unsecured-env-files/"&gt;exploit vulnerabilities in plain text methods such as .env files&lt;/a&gt; as they are easy to scan for. The initial breach of stealing credentials is often only the beginning of a broader, more devastating attack.&lt;/p&gt;

&lt;p&gt;Failing to use the protective powers of a Secrets Manager puts you further at risk from the following security challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigating the Risk of Cybercrime
&lt;/h3&gt;

&lt;p&gt;Cybercrime is big business, with successful breaches &lt;a href="https://www.cnbc.com/2019/10/13/cyberattacks-cost-small-companies-200k-putting-many-out-of-business.html"&gt;costing companies upwards of $200,000&lt;/a&gt;, on average. Without a Secrets Manager, your organization’s external threat mitigation strategy is severely lacking.&lt;/p&gt;

&lt;p&gt;For example, if you don’t have a centralized secrets repository, how do you know who can access specific credentials? Can you guarantee that all your secrets are encrypted at rest? Can you say with confidence that you have done everything in your power to prevent your secrets from falling into the wrong hands?&lt;/p&gt;

&lt;p&gt;It’s much better to acknowledge immediate improvements are possible and act on them now instead of later in the aftermath of a breach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Secrets Sprawl Across your Applications and Environments
&lt;/h3&gt;

&lt;p&gt;Secrets are widespread across your application deployment environments, platforms, and cloud infrastructure. Without a single source of truth for secrets storage, tracking secret usage becomes ad hoc, making operations such as mandatory rolling of credentials at set time intervals difficult and time-consuming.&lt;/p&gt;

&lt;p&gt;A secrets manager can provide answers to questions every engineering leader should be asking from a risk management perspective, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  How do we roll back a secret change in the event of a production outage caused by misconfiguration?&lt;/li&gt;
&lt;li&gt;  Which applications are using a shared secret such as an API key?&lt;/li&gt;
&lt;li&gt;  How fast can we roll a compromised credential and reload applications to pick up the change?&lt;/li&gt;
&lt;li&gt;  Where is the audit log for tracking secret access and value changes?&lt;/li&gt;
&lt;li&gt;  How are we sharing secrets with external contractors?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Maintaining Security Without Access Controls
&lt;/h3&gt;

&lt;p&gt;Secrets contain highly sensitive information, and without a Secrets Manager, anyone that can access .env files, Slack messages containing secrets, or other internal documentation where secrets may be stored, risk unintended access by unauthorized users or, worse, threat actors with malicious intent.&lt;/p&gt;

&lt;p&gt;To mitigate this risk, you need tight access controls that enable you to select who can manage secrets for each environment (e.g. only DevSecOps for live environments) and assign appropriate permissions and policies to groups, individual users, and machines.&lt;/p&gt;

&lt;p&gt;A Secrets Manager also provides a detailed audit log, critical when reviewing secret change history and administrative operations that document what users and machines accessed secrets and when.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Secure Environments for Machine Identities
&lt;/h3&gt;

&lt;p&gt;In Gartner’s round-up of &lt;a href="https://www.gartner.com/smarterwithgartner/gartner-top-security-and-risk-trends-for-2021/"&gt;security risks and trends for 2021&lt;/a&gt;, the management of machine identities as an integral security capability was listed in the top eight.&lt;/p&gt;

&lt;p&gt;Through cloud automation and infrastructure as code, organizations are grappling with greater numbers of non-human entities – containers, servers, applications, and devices – meaning the management of machine identities is becoming increasingly important to overall security.&lt;/p&gt;

&lt;p&gt;Gartner notes that “establishing an enterprise-wide strategy for managing machine identities, certificates and secrets will enable the organization to better secure digital transformation.”&lt;/p&gt;

&lt;p&gt;A Secrets Manager should be a foundational component of that management strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing and Accessing Secrets
&lt;/h2&gt;

&lt;p&gt;You shouldn’t be sharing secrets via IM, zip files, git, or email, embedding credentials in code or databases, or using simple, albeit insecure, .env files. That much is clear. Secrets Managers are fundamental to streamlined workflows and security best practices, but how can organizations be sure that teams will use the chosen solution?&lt;/p&gt;

&lt;p&gt;In short, the selected Secrets Manager must be easy and intuitive to use, as any anticipated friction can hurt widespread adoption.&lt;/p&gt;

&lt;p&gt;Teams can manage secrets via an access-controlled dashboard, CLI, or language-specific SDKs. Accessing secrets is then performed at runtime, ensuring an application always has the latest version of the secrets when deployed.&lt;/p&gt;

&lt;p&gt;And assuming application developers are following the industry best practice of using environment variables to access secrets in memory (not the file system), the security benefits are instant and significant.&lt;/p&gt;

&lt;p&gt;Organizations seeking to replace .env files with a secrets manager should ensure it has built-in support for injecting secrets as environment variables, eliminating the need for application code changes for a fast and painless migration.&lt;/p&gt;

&lt;p&gt;Environment variables have the benefit of being language and framework agnostic, which is why platforms such as AWS Lambda, Heroku, Vercel, Netlify, and others have standardized on environment variables for compatibility and because they require no third-party packages to consume.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Select the Ideal Secrets Manager for Your Organization
&lt;/h2&gt;

&lt;p&gt;Your security is only as good as your Secrets Manager. When weighing your options, consider the following factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed of implementation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Leading Secret Managers allow you to hit the ground running, minimizing migration time to secure your secrets sooner rather than later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Versatility across environments&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Your team works in all environments – from development to production and your secrets manager should as well.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User experience&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
What do your team’s favorite developer tools have in common? They solve a specific problem exceptionally well while providing a delightful developer experience. Choose a secrets manager that meets your security standards that your developers will love to use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Onboarding process&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A quick and streamlined onboarding process makes it easy to add teams and applications. A secrets manager should make life easier for development teams, not harder.        &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Application access&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Choose a secrets manager that meets your access policy requirements without additional overhead and complexity. The most secure secrets management implementation is the one that’s most widely adopted.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secrets Injected via environment variables&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If possible, choose a Secrets Manager that supports injection via environment variables to ensure secrets access is language and framework agnostic without the need for third-party libraries or SDKs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Level Up Your Organization’s Security with a Secrets Manager
&lt;/h2&gt;

&lt;p&gt;The security risks posed by ad hoc and insecure secret storage and access are too pressing to ignore. Secrets stored in encrypted formats such as .env files with no centralized source of truth to control access are putting themselves at risk unnecessarily.&lt;/p&gt;

&lt;p&gt;A Secrets Manager empowers your team to apply the principle of least privilege with fine-grained access controls for restricting what secrets and environments are accessible based on user groups and machine identities. All of which can be automated.&lt;/p&gt;

&lt;p&gt;Using environment variables to inject secrets into applications at runtime maximizes language and framework compatibility, and the easier your chosen secrets manager solution is to implement, the better chance it will be adopted organization-wide.&lt;/p&gt;

&lt;p&gt;Don’t leave your organization’s security to chance.&lt;/p&gt;

&lt;p&gt;Start your Secrets Manager journey today by signing up for &lt;a href="https://doppler.com"&gt;Doppler&lt;/a&gt;—The multi-cloud Secrets Manager your team will love that takes minutes, not hours to use.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=what-is-a-secrets-manager-574k"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Doppler: Truffle Security Recommends Doppler For Remediating Leaked Secrets</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Mon, 09 Aug 2021 07:00:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-truffle-security-recommends-doppler-for-remediating-leaked-secrets-2fgn</link>
      <guid>https://forem.com/doppler/doppler-truffle-security-recommends-doppler-for-remediating-leaked-secrets-2fgn</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m4baFo0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5de9972f49103c9dc496402b/6110c87e6056a5db32bd5ac7_TruffleHog.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m4baFo0N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets.website-files.com/5de9972f49103c9dc496402b/6110c87e6056a5db32bd5ac7_TruffleHog.jpg" alt="" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If an engineering leader asked you "How we are protecting ourselves against the accidental leakage of secrets?", what would you say?&lt;/p&gt;

&lt;p&gt;It's not an easy question to answer, especially for those more on the Developer side of DevOps who often don't have a background or focus on security.&lt;/p&gt;

&lt;p&gt;The answer we see emerging is a two-pronged offensive strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Secure Secrets Management&lt;br&gt;
‍&lt;/strong&gt;Sensitive data such as API keys and database credentials must be stored in an encrypted, audited, and accessed controlled secrets manager.
‍&lt;/li&gt;
&lt;li&gt; **Secrets Leakage Detection
**Continuous scanning of environments for leakage of credentials in places such as Slack channels, Git repositories, and other internal systems (e.g. task trackers and Wiki's)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While &lt;a href="https://www.doppler.com/"&gt;Doppler&lt;/a&gt; addresses secrets management, &lt;a href="https://trufflesecurity.com/"&gt;Truffle Security&lt;/a&gt;, the company behind the &lt;a href="https://github.com/trufflesecurity"&gt;popular free and open source secrets scanner TruffleHog,&lt;/a&gt; is on a mission to detect and prevent the accidental leakage of secrets and credentials.&lt;/p&gt;

&lt;p&gt;Dylan Ayrey, Co-Founder of Truffle Security recently published a blog post explaining why &lt;a href="https://trufflesecurity.com/blog/remediating-trufflehog-findings-with-doppler"&gt;Doppler is their recommended for solution&lt;/a&gt; for enterprise companies looking for an instant remediation strategy once secret leaks are detected and no secrets manager is present:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There’s a lot of different secrets management solutions out there, but many of them aren’t very user friendly, and can be difficult to set up. One we’ve had our eye on recently is Doppler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's not just about mitigating the risks of secrets being leaked, as that will inadvertently happen, but what is critical, is how fast you can respond, revoke, and roll any leaked credentials. Again, from Dylan:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The more approachable and usable your secrets management solution is, the quicker leaked keys can be rotated out, and the less exposure time they have to bad actors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Truffle Security and Doppler are a formidable team in keeping your secrets secure, giving attackers the least possible chance of getting their hands on valid credentials before their leakage has been detected and credentials revoked.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-truffle-security-recommends-doppler-for-remediating-leaked-secrets-2fgn"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>Kubernetes Security Hardening Guidance</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Wed, 04 Aug 2021 19:00:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-kubernetes-security-hardening-guidance-2f7b</link>
      <guid>https://forem.com/doppler/doppler-kubernetes-security-hardening-guidance-2f7b</guid>
      <description>&lt;p&gt;How to increase the security posture of a Kubernetes cluster is always top of mind for cluster administrators.&lt;/p&gt;

&lt;p&gt;While we recommend starting with the &lt;a href="https://kubernetes.io/docs/concepts/security/"&gt;documented Kubernetes security best practices&lt;/a&gt;, a new resource to add to your must-read list is the &lt;a href="https://media.defense.gov/2021/Aug/03/2002820425/-1/-1/1/CTR_KUBERNETES%20HARDENING%20GUIDANCE.PDF"&gt;Kubernetes Hardening Guidance Report&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Produced by the National Security Agency (NSA) and the Cybersecurity and Infrastructure Security Agency (CISA), it details the most critical threats to the security of Kubernetes environments, providing guidance for cluster configuration in order to minimize risk.&lt;/p&gt;

&lt;p&gt;More specifically, the report focusses on hardening techniques in three main areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Container and Pod scanning for vulnerabilities, weaknesses, and misconfiguration&lt;/li&gt;
&lt;li&gt;  Running containers and Pods with the most restrictive set of privileges possible&lt;/li&gt;
&lt;li&gt;  Network security recommendations for firewall configuration, network separation, authentication, and log auditing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can download the report from &lt;a href="https://media.defense.gov/2021/Aug/03/2002820425/-1/-1/1/CTR_KUBERNETES%20HARDENING%20GUIDANCE.PDF"&gt;https://media.defense.gov/2021/Aug/03/2002820425/-1/-1/1/CTR_KUBERNETES%20HARDENING%20GUIDANCE.PDF&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-kubernetes-security-hardening-guidance-2f7b"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>security</category>
    </item>
    <item>
      <title>Secrets Sprawl is Hurting Your Productivity. Here’s How to Fix It.</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Sun, 01 Aug 2021 02:29:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-secrets-sprawl-is-hurting-your-productivity-here-s-how-to-fix-it-1h8g</link>
      <guid>https://forem.com/doppler/doppler-secrets-sprawl-is-hurting-your-productivity-here-s-how-to-fix-it-1h8g</guid>
      <description>&lt;p&gt;Passwords, tokens, encryption keys, and API keys start to pile up as we spin up new cloud resources and release new apps. Because different systems and people need to access this authentication information, these secrets suddenly become duplicated across various services — even in some places they shouldn’t be!&lt;/p&gt;

&lt;p&gt;A typical IT environment may use container-related secrets managers, such as Kubernetes Secrets or Docker secrets. Or, they might use platform-specific secrets managers such as AWS Secrets Manager, Azure Key Vault, and Google Cloud Secret Manager. They may even use key-value stores, such as Hashicorp Vault and CyberArk Conjur.&lt;/p&gt;

&lt;p&gt;Organizations frequently use environment variables in individual virtual machines (VMs) and serverless functions to configure applications and provide secrets such as API keys. Unfortunately, some developers are still hard-code sensitive data into their application’s source code, or using plain text unencrypted files such as a .env file. Of course, this is never a good idea, and most teams realize this, but a viable alternative that won’t impact productivity is often not apparent.&lt;/p&gt;

&lt;p&gt;It becomes increasingly challenging for companies to distribute, organize, and secure these secrets as IT infrastructure grows. This haphazard storage of secrets is called secrets sprawl.&lt;/p&gt;

&lt;p&gt;Secret sprawl is such a common problem because secrets hold together these different application components. Most modern applications consist of smaller building blocks and use a growing number of technologies.&lt;/p&gt;

&lt;p&gt;As developers ensure each application has everything it needs, it’s easy for our secrets to get out of control. Everything seems fine until we need to make a change or — even worse — there’s a security breach. Suddenly, tracking down those credentials becomes a time-sucking, trying task that we never expected.&lt;/p&gt;

&lt;p&gt;Let’s look at some of the pain points secrets sprawl causes and how we can get our secrets back under control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets Sprawl’s Pain Points
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i4x0azmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057595590/1dhbBgNGq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i4x0azmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057595590/1dhbBgNGq.jpeg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When our secrets are sprawled all over our infrastructure, the development and operations teams (and, really, the whole organization) face some pain points. First, it’s challenging to keep track of where all those secrets are. When we need to find specific secrets, we might end up digging through various secrets managers, code, or even someone’s desk for that elusive USB stick.&lt;/p&gt;

&lt;p&gt;Did our credentials just change? Now we have to track down every duplicate copy of that secret across our entire infrastructure.&lt;/p&gt;

&lt;p&gt;Second, security is a notable secrets sprawl pain point. When our secrets spread across a wide area, it’s challenging to track who has access — let alone manage that access and enforce access rules. We should protect and encrypt all these secrets locations, but there are just so many. And they’re growing every day!&lt;/p&gt;

&lt;p&gt;We know we should frequently change our secrets to reduce the chance of compromise (especially when an employee leaves), but secrets sprawl makes this a ton of work. Maybe we really wanted to work on that new feature instead, so we decided to just change our credentials next month.&lt;/p&gt;

&lt;p&gt;Third, secrets sprawl makes it more challenging to share secrets. It’s great that our credentials are nice and secure in Kubernetes secrets. But that doesn’t help when we need to use that secret for an Elastic Compute Cloud (EC2) virtual machine or in our continuous integration and continuous deployment (CI/CD) pipelines.&lt;/p&gt;

&lt;p&gt;Also, if secrets are in multiple locations, maybe only a few employees know where they all are. As a result, the organization becomes dependent on those specific employees, making it even harder to get new team members up to speed on the secrets architecture. There’s also the potential that the keepers of the secret might feel a sense of ownership and be reluctant to share. When they do share the secrets, doing it in plain text isn’t ideal.&lt;/p&gt;

&lt;p&gt;Most organizations plan to scale their development team and architecture. Without transparency in secrets management, secret sprawling becomes a major scaling bottleneck. Growth also means more secrets, and more secrets means more locations in which secrets are stored.&lt;/p&gt;

&lt;p&gt;Now that we know some of the pain points secrets sprawl causes, let’s look at some solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Secrets Sprawl with a Secrets Manager
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GUpYJRg6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057597735/3DQ5v5Hft.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GUpYJRg6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631057597735/3DQ5v5Hft.jpeg" alt="" width="808" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Centralization helps keep secrets sprawl in check. When you keep all your secrets in one central location, you know where to find them and can easily make changes and monitor who and what machines and services have access. Secrets managers help centralize your secrets by storing them in one place where all your services connect.&lt;/p&gt;

&lt;p&gt;If your company is small enough, you might decide to deploy all applications as Kubernetes containers and store all secrets in Kubernetes Secrets. This centralization tightens access control and makes it easier to audit secrets. Maintaining a centralized and encrypted secrets store ensures the organization applies consistent threat protection. It reduces unauthorized access and boosts security posture.&lt;/p&gt;

&lt;p&gt;In a large company, one administrator might manage multiple Kubernetes clusters as you scale. In this situation, individual users can’t enforce application-wide policies. This means secrets are better managed.&lt;/p&gt;

&lt;p&gt;Sticking to a single cloud provider and using its built-in secrets manager helps. But, this really limits what you can do with your infrastructure. A single provider might not have all the services you need for dynamically growing infrastructures. Many organizations are pivoting to the multiple cloud computing services approach for better disaster recovery and better servicing users distributed over a larger area (or even internationally) with edge computing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Multi-Cloud Secrets Manager
&lt;/h2&gt;

&lt;p&gt;Multi-cloud capable secret managers such as &lt;a href="https://www.doppler.com/blog/doppler-vs-hashicorp-vault#:~:text=Doppler%20is%20loved%20by%20Developers,and%20multi%2Dcloud%20deployment%20platforms."&gt;Doppler and HashiCorp Vault&lt;/a&gt; reduce secrets sprawl by centralizing secrets storage with integrations for a wide range of platforms.&lt;/p&gt;

&lt;p&gt;Vault’s Key-Value store, while flexible, wasn't designed for organizing secrets by application or microservice. Developers must determine how secrets will be stored and extracted from Vault and injected into their application. Some organizations will need and appreciate Vault’s flexibility but it could be prohibitively complex for others, making them avoid using it and defeating its purpose. Its storage and maintenance costs can also add up. Above all, secrets management is not just about &lt;a href="https://www.doppler.com/blog/why-secrets-management-is-not-a-key-value-store"&gt;Key-Value storage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But &lt;a href="https://www.doppler.com/"&gt;Doppler&lt;/a&gt; offers a better solution. It was built for today's microservice world, organizing secrets by application with a customizable list of environments. It enables you to control all your secrets centrally, and you can set it up across every environment, from development to production.&lt;/p&gt;

&lt;p&gt;Doppler delivers a &lt;a href="https://www.doppler.com/blog/doppler-vs-hashicorp-vault#dopplers-multicloud-integration-model"&gt;seamless integration&lt;/a&gt; experience for every cloud provider and platform. While Doppler provides centralized secrets storage management, you can still use the built-in secrets managers your favorite cloud services provide, such as AWS Lambda, Azure Key Vault, Heroku, Vercel, and a &lt;a href="https://www.doppler.com/integrations"&gt;growing list&lt;/a&gt; of others. Secrets automatically sync to external secrets stores, saving developers time so they can focus on building products and features.&lt;/p&gt;

&lt;p&gt;With Doppler, it’s a completely managed service, saving teams time and money otherwise spent on deploying, updating, and supporting a self-hosted secrets manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Because of the growing trend to deploy microservices to multiple clouds and platforms, every organization faces secrets sprawl, even if they don’t realize it yet. It’s a natural consequence of growth. As developers, we can be so immersed in our secrets sprawl, or only focussed on our particular application that we aren’t even aware of the bigger picture and impact secret sprawl is having across teams. It becomes the status quo despite hindering productivity.&lt;/p&gt;

&lt;p&gt;Secrets managers reduce secrets sprawl by securing all these credentials in a central location. Change your credentials everywhere with just one click and see exactly who and what can access any given secret.&lt;br&gt;
‍&lt;br&gt;&lt;br&gt;
Whether you’re part of a fresh startup or a growing organization, it’s never too early or too late to wrangle your secrets into one manageable dashboard.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-secrets-sprawl-is-hurting-your-productivity-here-s-how-to-fix-it-1h8g"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Doppler: Visual Studio Dev Containers For Node.js Apps on AWS</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Thu, 08 Jul 2021 07:00:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-visual-studio-dev-containers-for-node-js-apps-on-aws-1p84</link>
      <guid>https://forem.com/doppler/doppler-visual-studio-dev-containers-for-node-js-apps-on-aws-1p84</guid>
      <description>&lt;p&gt;If your Node.js applications are deployed as a Docker container in production, doesn't it make sense to develop inside a container as well?&lt;/p&gt;

&lt;p&gt;Follow along with this live-coding session to learn how to build a Node.js remote container-based development environment on AWS using Visual Studio Code Dev Containers, including support for step-debugging and development-specific package installation.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_h9dz6drUNw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Also check out our &lt;a href="https://www.doppler.com/blog/visual-studio-code-remote-dev-containers-on-aws"&gt;Visual Studio Code Remote Dev Containers on AWS Set Up Guide&lt;/a&gt; to learn how to configure your local and remote machine and you can find the code used throughout the video on &lt;a href="https://github.com/DopplerHQ/mandalorion-gifs-node"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions, you can find me on &lt;a href="https://twitter.com/ryan_blunden"&gt;Twitter&lt;/a&gt; or send me an email at &lt;a href="//mailto:ryan.blunden@doppler.com"&gt;&lt;strong&gt;ryan.blunden@doppler.com&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-visual-studio-dev-containers-for-node-js-apps-on-aws-1p84"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Transcript
&lt;/h2&gt;

&lt;p&gt;I'm Ryan Blunden, developer advocate at Doppler and I'm really excited to be presenting to you today, a pretty ambitious topic, really. Not just dev containers on the VS Code, which is a very new technology, but how to actually run dev containers remotely on AWS. So, I'm going to share my screen and we're going to get straight into it. &lt;/p&gt;

&lt;p&gt;Now, because this isn't your typical webinar, we've got a bit of a to-do list to get through. Now, you don't need to necessarily read all of these things, but this just gives you an idea of what we're going to try and tackle in the 45 minutes or maybe 42 minutes that are left for the session today. &lt;/p&gt;

&lt;p&gt;So, just to start off with a really quick intro in terms of the application. The application is called a Mandalorian Gifs Generator. And it's a really simple app that, as you might guess, randomly generates Mandalorian Gifs. I'm a big believer that if you want to learn something, you should build a real application and test it out that way. So, if we go to localhost:8080, and take a look. This is the sort of thing that we're going to be deploying in our remote container. Cool. So, let's stop that and then heading back to our list. The other thing I want to show you quickly is what does it actually look like to launch a dev container? Now, I'm going to be using this with Docker locally because that's a bit of way to save time. &lt;/p&gt;

&lt;p&gt;And so, if we bring up the command palette in Visual Studio Code, we can see here command to rebuild and reopen in the container. So, this is the exact flow that you'll do if you've got your code cloned locally, and then you want to launch that code in a container, that is the workflow that you will do. And let's see. Ah, we have some sort of error. Now, what happens is when you've got an error, you will then bounce back to Visual Studio Code, so you can make that change and then go back into the container flow. And I have a suspicion that the reason why this is happening is to do with volume mounting. Now, if there is one area of Visual Studio Code in getting this all to work that is the most challenging, it is by far mounting your code in the container. &lt;/p&gt;

&lt;p&gt;I'm going to get into this code a lot more later, but as you can see here, I'm using the workspace mount that will be in the virtual machine. Here, I'm going to be using the local version of that. And if you are going to start off with dev containers, I definitely recommend doing it locally first with a local installation of Docker, just because there are a lot of moving pieces. What we can see here is once we have opened the container on Visual Studio Code, this is the really cool part. This is why Visual Studio Code and Microsoft and GitHub are going to succeed in that dream of having a completely remote development environment that still has all of the features that we're used to. So, we can see here that I've got all of my project files. I've got an integrated terminal except this terminal is actually running inside the Docker container. &lt;/p&gt;

&lt;p&gt;And what's really cool is because it's Visual Studio Code, I still have all of the features in terms of step debugging and all of those sorts of things that I would normally, if I was, using Visual Studio Code and everything was running locally. So, I'll reopen this code locally. Now, I'm from Doppler and Doppler is a secrets manager. The purpose of this all isn't really going to be to feature Doppler but just we'll be touching on some things related to dotenv files, which can make things a little tricky with Visual Studio Code. So, if you are going to use Doppler with Visual Studio Code, which I'd highly recommend you check out, what you'll need to do is launch Visual Studio Code with some environment variables that Doppler needs in order to configure itself so it knows which secrets to pull in for which project.&lt;/p&gt;

&lt;p&gt;To give you just a really quick peek at what Doppler looks like, essentially you have a list of projects, this one's Mandalorian Gifs, and just like an env file, here are all of the keys and the secret values over here as well. So, we'll see if we can get to doing a brief demonstration of Doppler later. Okay. So, I'm going to [inaudible 00:06:18] this out and now let's get back to our presentation. And I keep checking on time. All right. So, what I want to do is just really run you through the steps that you'll need to go through in terms of setup. Ideally, if you want to follow along, or if you want to do this afterwards, you'll clone the repository. And if you want to check that out, it's at Doppler HQ and then just search for the Mandalorian Gifs node repository. So, once you've done that and you've cloned it locally, then what you would do is just do what I did then.&lt;/p&gt;

&lt;p&gt;You will use Docker and the Visual Studio Remote dev containers to try this out locally. And the reason you want to try it out locally initially, is because as you saw, every time it goes from standard Visual Studio Code to the dev container it's launching containers in the background, it's doing a bunch of setup. And as soon as you move this to a remote host, it becomes a lot slower. So, it's just like when you're developing applications, you want a really fast feedback loop. You want that for the dev container development process as well.&lt;/p&gt;

&lt;p&gt;The next thing that you'll need is you'll need SSH auth set up. Now, if you don't know a great deal about open SSH, that's a topic in and of itself. Microsoft does have some good resources on learning how to use open SSH and the reason why we need this is because the easiest way for our local Docker CLI to control the Docker host that's going to be on AWS is through an SSH tunnel. We can do it, otherwise, using TLS and TCP and sockets, but that's kind of really complicated. So, we're going to be using SSH. So, ideally, you understand, you've used SSH before and you already have an SSH identity set up because that's what we're going to use to be able to connect to the machine. &lt;/p&gt;

&lt;p&gt;Next, you're going to need some AWS credentials. Now, the good news for running this in the cloud is you don't necessarily need to do things like EC2 instance with security groups and assumed IAM roles and stuff like that. That's perfectly good for production workloads. All you need and what I would recommend is a Lightsail instance. So, Lightsail is kind of like DigitalOcean on AWS, where it makes it easy just to create an instance. You can directly edit the network ports right in this UI here, and it just makes it really, really simple. So, that's what I would recommend, and it's not too expensive as well. &lt;/p&gt;

&lt;p&gt;And then the optional step is installing the Doppler CLI locally. If you want to do that, it's really, really easy and what's good is it's free to get started. So, you don't have to enter a credit card or anything like that. And you still get all the features you need if you wanted to use Doppler in the production. And if you had to install page, you'll see how to install the Doppler CLI for, basically, every operating system imaginable and we've got first-class support for windows as well. &lt;/p&gt;

&lt;p&gt;Okay. Now, let's get into some concepts. Now, I'm going to presume that because you are attending this webinar or you're watching it after the fact that you know what Docker is, you know what remote development is and you're on board. You want the future sooner. Now, if you're not sure, the reason why remote development is so compelling is for a couple of reasons. One is that if your application is deployed in production in a container, then it makes sense that your development environment is as close to that as possible. Because if you're using a different version of node locally, because that's what comes with Homebrew and that's different from what you're using in your container, that's just like an example of an opportunity where unexpected things can happen when you deploy your application in a production environment. Now, in terms of why development containers as opposed to virtual machines, GitHub and GitHub Codespaces is going to work in a container. &lt;/p&gt;

&lt;p&gt;And one of the cool things that they're going to be able to do as a built-in service I imagine is you will be able to check out a teammates branch as part of reviewing a pull request. That's going to open in a remote dev container. And as long as there's a way to get the secrets that that application requires, so it can start off and it configure itself, you can actually preview the application for that pull request is for, without having to bring that down locally, do a whole dance and all that sort of stuff. &lt;/p&gt;

&lt;p&gt;So, that's what Visual Studio Code and GitHub is moving towards, and it is incredible for that. You don't necessarily need to have a project locally and rebuild the container. You can optionally just say I don't want you to build a container. I just want you to use an existing image and then just launch me into that. So, it's pretty amazing technology and that's where the future is as well. Being able to spin up a container development environment in minutes without having to do much work at all, because the hard work is just getting Visual Studio Code to be able to control Docker. All right. So, we've talked about why remote containers.&lt;/p&gt;

&lt;p&gt;Now, if you go to Visual Studio Code's site, there is an entire section dedicated to just remote development in general because there's a couple of options to get started. One is developing just remotely on the VM itself versus in a remote container. Now, I would say that remote containers are where you want to end up or perhaps just local containers, but doing remote development over SSH is a good way to start because at least it'll expose you to tools such as the Remote Explorer, you'll get to configure open SSH. So, that is a great way to get going. In terms of, you know, which one is better, containers are great because they're more lightweight. You can run different sorts of applications and you can run applications that have multiple containers as well. &lt;/p&gt;

&lt;p&gt;But remote VM, that's still remote development. So, that's super cool. Now, really briefly, to sort of demystify the magic that you saw before, I want to really briefly touch on how Visual Studio Code itself is architected because it's not necessarily working the way that you think. Now, focusing on this diagram here, the way that it works, Visual Studio Code is running on... Obviously, it's running on my machine. But only the themes that it needs to run locally when we're actually running a container. So, things like any theme I have, that is going to be running locally, but the things that are going to be running remotely is the VS Code server. And here, when we see open exposed port, it is open SSH that is providing a tunnel into the VM and then into the container itself. &lt;/p&gt;

&lt;p&gt;This is really powerful. This is why for instance VS Codes, JavaScript and TypeScript language server is able to run remotely and give us all of that code intelligence. So, all of the heavy lifting is being done here. This is essentially almost just a thin client text editor at this point. Visual Studio Code is also taking care of doing things like port-forwarding from our local machine to inside the container. So, we can just hit our application localhost:8080 but it's tunneling that through to the actual container. So, the amount of complexity that Visual Studio Code is taking care for us is huge and the fact that we can run extensions from Visual Studio Code just about anything inside our containers as well is just phenomenal. So, this isn't a stripped paired back version of remote development in VS Code, they are trying to give you the full, entire experience. &lt;/p&gt;

&lt;p&gt;Okay. The next step and I won't have a chance to go into this in too much detail, but it's a similar thing with Docker. So, if you've used Docker locally, it's easy to think that Docker is just like an encapsulated application, but it also has a client and a host. And so, what happens is, at the moment on my machine, let's say, when I do Docker run, everything is running in a virtual machine on my own machine.&lt;/p&gt;

&lt;p&gt;But what I can do is I can configure the client and say, "Client, I actually want you to control this different Docker host over SSH." And there's a way to configure Doppler... Sorry, Docker, with things called context. That's what enables you to say, "Hey, Docker, I want to use this remote host," or, "I want to switch back to doing something local." So, that's just a really cool feature to learn about Docker that is going to come in handy because obviously when VS Code is running at dev containers, we want to run the dev containers on our remote VM.&lt;/p&gt;

&lt;p&gt;We're all good. All right. Let's keep cracking on. How am I going for time? Yeah, not too bad. Okay. Now, some of these steps for you, they might be very pedestrian, very boring, but I want to try and set everything up from scratch, A, because if something goes wrong, that's an opportunity to learn. And, B, it's important to see how all these pieces fit together because that's not something I've seen in any other webinar or tutorial so far. And that's why I was so excited to share this with you. &lt;/p&gt;

&lt;p&gt;Okay. So, the first thing that you need to do is when you go in and create the instance, go into your account and add your SSH key. Now, the reason that you want to go and add your own SSH key, it's so that you don't have to use the one that they provide, you have to download it, set the right file permissions, use the right flag on open SSH. You don't want to have to do any of those things, or you can learn how to add it to your identity, but then that's tricky because it doesn't do it over machine restarts. So, chances are, if you're a developer, you've already got an SSH key. So, just upload that, it's going to make things so much easier. &lt;/p&gt;

&lt;p&gt;Now, if we head back, we're going to create our instance. Now, in terms of how big your instance is going to be, well, I guess it depends on what you're going to be doing. If you're running all the services in a monorepo, then you're going to need something really beefy. And that is probably one of the biggest benefits of developing remotely as well. Even if you've got a massive specter MacBook Pro that still may not be able to launch every container that you need. So, in terms of which machine, it really depends on the demands, it depends on how much your boss will let you spend, it depends on a lot of things. So, I'm going to choose just this one. It's pretty cool for CPU's. That should definitely do it. Check that the SSH key that you have uploaded is the one that's selected here. It's going to make it a lot easier. Let's do DopplerDevContainer and then the other thing that we'll want to do is add a launch script because what we need to do is we need to install Docker and get that all going. &lt;/p&gt;

&lt;p&gt;And so, the easiest way to do this... This script, by the way, all of this code is available here, if you go into the AWS... Oh, that's interesting, and then you can just grab it here. All right. So, what we're going to do is I'm going to grab all of this code, go back to Lightsail, paste it in here, and then it's going to execute that as part of setting the machine up. So, we'll create the instance and this will take about three to five minutes, something like that. Okay. So, still booting up, still installing Docker and things like that, what I'm going to do is I'm going to copy this IP address and I'm going to create a host alias. Now, the reason why I do this is I'm not good at remembering things in general, so, I don't have much hope of remembering an IP address but what I can do is go into a regular terminal and it's not code. &lt;/p&gt;

&lt;p&gt;Sudo nano, and I'm just going to make an entry in the host's file. Now, maybe you haven't used a host file before but it's a really cool way of just taking an IP address and adding an alias. So, instead of having to refer to this when we set up our SSH connection, we just use this instead and let's save that. Cool. All right. So, now this isn't going to work because I don't think the port for ping has been set, but you can say that it did resolve to that IP address. All right. So, let me keep going back to my list. We've done this. We're creating the virtual machine, that's a work in progress. We've created our host alias, and then the next step it's configuring the VM. So, what we can do is, by now, we should probably be able to connect to it over SSH. User is Ubuntu because that's the Ubuntu distribution and our machine alias is AWS dev. &lt;/p&gt;

&lt;p&gt;Alright. And we are in. Let's see if Docker is installed. Hey, it's installed. This is a good sign. Let's now verify, if I go back to my instructions that it is working. Let's see. Let's go back here. That's not what I wanted to do. Let's try that again. And do we get, "Hello world?" Hey, we get, "Hello world." Okay. Now, for those of you that are systems admin inclined, you'll notice that I was able to run Docker, which is a privileged thing to be able to control on a system, but it's using the Ubuntu user. Now, the reason why we need to do that is Visual Studio Code will be using the open SSH connection, which uses the Ubuntu user. So, therefore, we had to give Ubuntu user permissions to be able to control Docker, and we certainly didn't want to make it possible for the root user to connect over open SSH.&lt;/p&gt;

&lt;p&gt;So, that's just a bit of a necessary thing that we had to do. All right. I think we've gone pretty well on time. Let's see. So, we've tested that. We've configured our VM. And the last thing I should check is that we have the code for this on the virtual machine. Okay, which we did not. So, let's go and clone that down. Oops. Okay. Now, the reason why we're cloning the code for this repository, even though we're going to be working in a dev container is because one of the most challenging aspects of dev containers is when you've got your repo locally, and then you want to be able to develop on it remotely, that code has to get to the remote Docker server somehow. &lt;/p&gt;

&lt;p&gt;Now, when Visual Studio Code builds your dev container, it's not doing anything magic, it's basically looking for the Docker file, and from scratch, it's going to go through all of these steps. Now, because we can see here that it's copying the code that's inside source, it is going to do that initially locally, but then once you're in the cloud, once you're running your container remotely, well, it doesn't have access to your local files anymore. And there's no way to mount your local files to the remote source. That's only something you can do if you're using Docker locally. This is probably the most challenging aspect of this, so if you've got any questions, send me an email at ryan.blunden@doppler, and I can point you in the right direction. &lt;/p&gt;

&lt;p&gt;Okay. So, the reason why I've done this is a couple of reasons. One is I can authenticate this machine with GitHub or my code host, so I can go through all of my Git workflows here. And what we're also going to do is we're going to just Mount this Mandalorian Gifs node path into the container, because that's so much easier than using like a volume where it's difficult to then get those code changes in. It's already got a Git repository so Visual Studio Code is going to be able to pick up those changes. It's just a much, much easier way to go. I'm going to send you some links after so you can delve into the different ways of doing this, but to get started with remote development, which is definitely in the advanced part of things, [inaudible 00:23:18] in the VM and then mount that code from the VM into the dev container. &lt;/p&gt;

&lt;p&gt;All right. There's a lot to remember, which, hopefully, this to do list concept is working for you. All right. So, now that we've got our VM in the cloud, now we need to configure Docker so it is able to control that remote host because that's what Visual Studio Code is going to do for us when it creates the dev container. All right. So, remote SSH, we've already tested that, that works. Now, let's create the Docker context. So, because I have my AWS dev alias, that's what I can use here. If you're using an IP address, then you would put your IP address in here. So, I'm going to copy this code and then run in the shell. Now, the reason why I'm not running it, I can just as easily run it in the shell, so, let me start doing that.&lt;/p&gt;

&lt;p&gt;Okay. So, what this is going to do is it's going to create a new context, which is a new configuration for what the CLI is pointing out to control in terms of a Docker host. And here we are saying that we're going to connect to it via SSH. Now, I will say that sometimes there can be performance challenges using open SSH and chances are, if you're like DevSecOps team or your DevOps platforms team is setting this up for you, they may also set it up using TLS. So, you're communicating directly with the Docker daemon, but for our purposes, and this is also great for security, this is how we're going to go. Now, in terms of being able to inspect the different contexts that we have, we can see that we have AWS dev and then the default is from Docker desktop, which is where... And we can say that that's the one I'm using because it has the little asterisks next to it. &lt;/p&gt;

&lt;p&gt;In terms of Docker configuration, you can either do it this way or you can do something called using the Docker host setting in VS Code. Now, this is where things get a little bit tricky too. The reason why you might want to use docker.host in VS Code instead of creating the Docker context is because if you do it this way, it means that Visual Studio Code will respect the setting that you put in here, but it's only specific for Visual Studio Code. So, for instance, if you want it to use dev containers remotely in here, but then when you run Docker containers in your local shell, that can still point to your local Docker instance. Personally, I try to stay away from Visual Studio Code settings if they're not for the workspace as much as possible. And it's really not difficult to be able to set the Docker context. You can just say docker context use AWS dev. And now we're using AWS dev context.&lt;/p&gt;

&lt;p&gt;Then if you wanted to switch back to use your local Docker instance, then you just do that. And the great thing is that this setting is consistent between Visual Studio Code and your system as well. So, I think it makes much more sense just to use it directly here. &lt;/p&gt;

&lt;p&gt;Okay. So, then the next check, let's actually try running a container, running it locally, as in triggering it locally, but we should be able to observe that it is actually running remotely on our VM. So, we'll see if this works. You'll notice now that there's a bit of a delay and that's because it has to go through open SSH and we can see, yeah, it seems to be working. Actually, I just want to do this again, just to prove this is a real demo into Docker PS, and once that is up and running, see, hey, we're in the machine and we can see that container running. &lt;/p&gt;

&lt;p&gt;Cool. All right. So, that is basically everything covered in terms of Docker configuration. As I said, the remote SSH part and configuring that authentication between your machine and the VM, on Windows, this is a lot tricky than it is on Linux or Mac, depending upon if you're using WSL, depending upon the [inaudible 00:28:05] SSH client, if you're using a terminal such as Git Bash or the integrated terminal in Visual Studio Code, there's a lot of different options, and so be prepared to go on a bit of a wild ride to get that set up, but then once it's set up, it is super easy. Okay. So, now we're kind of getting to the good stuff, the dev container related stuff. Let's go through and show you all of the extensions that you need to install. To save you having to manually search the extension store, I can just share with you this. This is what I want to do. It's not Q&amp;amp;A messages. If that was message, where would I be?&lt;/p&gt;

&lt;p&gt;[inaudible 00:29:01] There we go. All right. [inaudible 00:29:10] Okay. All right. So, I'm not going to install a whole bunch of extensions. It's really just these three. And let me close that. And the extensions that you absolutely have to have, this is going to be done for you, so you don't really need to worry about that, but the Docker extension and the remote containers extension, obviously, so we can have remote containers. Now, the Docker extension is really cool because if you want to do things like clean up the dev containers, clean up any images that are created, then you get this random little view here and you can do things like remove this image or remove a container. We're not really going to be using that too much. So, we've done that. We did the dev container locally before, so we don't have to worry about that. &lt;/p&gt;

&lt;p&gt;Let's see here. Let's now take a look at the remote UI as well. This remote UI is a relatively new feature and it is also brilliant. Now, it's different because the Docker UI is really just showing us things about Docker, surprise, surprise. The Remote Explorer, what's really cool about this is that it understands dev containers opens as stage VMs as well. Things like WSL instances. So, this is your sort of like map for anything that you might be doing in Visual Studio Code that's happening remotely. Okay. Let's do that. Now, let's quickly talk about the dev container workflow. What I found really challenging when I was learning about dev containers is that there is so much documentation and it's really overwhelming. But the thing is, there's a lot of things that Microsoft are doing to make things as easy as possible for you. &lt;/p&gt;

&lt;p&gt;So, while it is a bit of a steep learning curve, it's not too bad once you get your head around the overall concepts, which is really the point of this webinar. So, essentially what you will do, I'd recommend that you're using Docker locally. So, you'll be modifying your dev container file. And just to quickly show you what that looks like. So, you'll be doing things like... You'll be, perhaps, modifying your Docker file. You'll be creating a command that runs when the container first starts like installing dev packages, configuring ports, all that sort of stuff. &lt;/p&gt;

&lt;p&gt;So, there is a lot of like tweak a dev container, rebuild it, it didn't work, go back to the code, tweak it, rebuild. And essentially that's what is going on in this workflow here. So, you open it, did everything work? Did you get an error? Okay. Do some success, then reopen it again. That's why doing it locally you'll get a much faster feedback loop and be prepared for a lot of these when you first get going.&lt;/p&gt;

&lt;p&gt;Okay. Dev container syntax. Let's go cover that. So, the spec for dev containers is huge, and that's because there are so many different things that you can do. You've got build args, build targets, a lot of the same things that you've got when you actually build your container. You've got the container env, you've got remote env and you've got things where it's like, what is the VS Code server should start as? Should that be different to the container that it's actually going to be running in? One thing that they advocate for, which is good, is running as a non root user, reason being is that if you run as a root user in your container, because we are mapping the code from the host into our container, we have... We'll just go in here, you can see that all of this code is owned by Ubuntu in the Ubuntu group. &lt;/p&gt;

&lt;p&gt;If we run our container as root, any files that we create in the container are going to be owned by root here as well. Now, in the resources I'm going to email you, there is an entire section on container user scenarios, such as making the user IDs match. Yeah, it's a whole thing but I've got a link in there to help you out. All right. So, getting back to the dev container syntax. It's reasonably simply to get going once you understand how everything works. So, let me just go through the most essential things for you to understand when it comes to the syntax here. First of all, you need to choose whether you're going to be building your image from scratch each time, or whether you just want to use an off the shelf image. Most of the time you'll want to do this. &lt;/p&gt;

&lt;p&gt;This might be a good option if you're just wanting to quickly review a pull request and you don't need to necessarily rebuild the image just to do that. You can change the container user to be root, but what I would do is I would stay away from this until you know you need to. The reason why you might need to, in my example, all my command is doing, it's just installing the dev packages, such as Es Lint and Prettier and things like that. So, I don't need necessarily root access, but if you are installing things like a compiler like GCC, then you'll need root access to be able to install those system level packages. This is where we specify the command that we are going to run. Forward Ports, pretty straightforward, no pun intended. These are the ports that your application is going to be listing on. &lt;/p&gt;

&lt;p&gt;Now, you can define the workspace folder. Now, by default, I believe this is just root workspace, which is this. Now, this is fine. Don't feel like you need to change this. The reason why I'm doing this is because I set up a path that is looking for the nodemon binary in node modules. That's why I need these to be the same. That's just me, you don't have to do this. Then, this is probably the most important part. You have essentially three ways. I touched on this really briefly earlier, you have three ways in which you can mount code in here. One is use a volume. This is great to get started. It's also awesome because you don't even need access to the file system here because Docker is going to be creating an inner volume. &lt;/p&gt;

&lt;p&gt;And then what happens is Visual Studio Code takes that volume and then mounts it inside the container. Now, this is cool, but it's quite advanced because then you need Git credentials inside the container in order to be able to create [inaudible 00:35:44] Git operations as well. So, that's definitely on the more advanced side of things. This is the version that we're going to be using because it's going to be mounting the code that's in here into this here. And if you've ever used a volume mount into a running container for like local development purposes, this is exactly how you would do that. If you're doing things locally, then you just want this because you just say Visual Studio Code, wherever my workspace is on my machine, then just map that into here. All right. &lt;/p&gt;

&lt;p&gt;So, yeah, as I said, because we're going to be running this remotely, we'll go with option number two. Now, you can also set environment variables for your container. Now, in this instance I've got the Doppler CLI installed locally. And the reason why I use Doppler and I'm not just being the developer advocate, but it makes it so much easier to manage your secrets. If I can just give you a really brief overview of why to demonstrate what I think is like the killer features to use in development, A, you get a really nice UI where you can go and change things, you can do things like add notes to secrets. And what's great is because Doppler is like a central source of truth for secrets. This is what your teammates are also going to be pulling from as well. &lt;/p&gt;

&lt;p&gt;So, for instance, if they have just merged code and their code relies on a new config value called new secret. Well, if you were using the dotenv files, you have to have a way of getting this new secret variable out to everyone on your team. This might be over Slack. You might even be uploading just your whole env file for one to look at. That's not great for security, and it's just a manual process. Everyone has to then go and grab that and put it in their dot.env file. If you use Doppler and you make this change and save it, then the next time that you use Doppler to run your application, it's always going to fetch to the latest version of your secrets. So, that just makes it so much nicer. The other thing that you can do as well, which is particularly handy in development, because what happens is I'm working on a project stuff and Brian, they're working on different, different features, and they might need to override a particular setting. &lt;/p&gt;

&lt;p&gt;Now, if we go in here, yes, they can change it here, but this is going to change it for all of the developers. We have a concept of branch config. And what that enables me to do is I can then come into this branch config and let's say, I can change it to G, I hit save. And then if I'm pulling my secrets from my particular config, it means that I will see this rating, but my teammates will still see what is in the dev config. And you can see that this little icon here saying that I've overridden that value. So, just a couple of reasons why you might want to consider using a secrets manager instead of dotenv files. And we're going to be talking about dotenv files in future articles on Doppler site, so, definitely go and check those out. &lt;/p&gt;

&lt;p&gt;If things start to get weird and they just don't work in terms of step debugging, this is something you can try. As I said, this really is bleeding edge technology, so sometimes things don't always go as planned. And then here's just an example of an extension that's actually going to be run in the remote container. So, I'm going to be able to use Prettier to look out my Prettier [inaudible 00:39:05] I config and reformat my code, but this is all happening remotely inside the VS Code server. Okay. So, that is the basic syntax. And look at that, we're almost out of time, but that's okay. I only got a few more things I really want to talk about. So, we've talked about the container syntax. I've given you a crash course on the code mounting, but now let's actually do something cool, and look at step debugging. &lt;/p&gt;

&lt;p&gt;So, to do step debugging, let's actually rebuild and reload this container, this time in the remote environment. So, what's going to do, it's going to show you all of the logs. For the most part, this is not going to be very interesting. Here we can see that it is rebuilding the container and it does this from scratch in order to make sure that if you've changed anything in the Docker file, that's going to be reflected in the container that it ends up running for you. While that is doing that, I want to talk about a couple of other things as well. So, the Visual Studio Code is cool in that it... Let me move this. It's cool because it sets up the local tunneling. So, I just lost my train of thought. It sets up the local tunneling, which is cool, but that can be a little bit slower because it's going from your machine tunneling over open SHH to the remote host and then tunneling into the container. &lt;/p&gt;

&lt;p&gt;So, there's a lot of sort of like jumps that happen in there. If you haven't used it before, there's a great little product called ngrok. It's free to use initially, but they do have paid plans as well. And what this does is it's tunneling as a service. So, what's particularly cool is if you wanted to do something like test out Doppler webhooks for order reloading when secrets change, you'll need an HTTPS address for that. And so, you can run just your app on port 8080 just over HDP, and then ngrok will actually take care of creating a tunnel directly into that. And because it's a tunnel from ngrok servers directly into the container, it's much faster to preview your app and I'll be able to give you a demo on how that works. &lt;/p&gt;

&lt;p&gt;Okay. So, this is definitely going to be a bit slower, the first time you start out, because there's a lot of machinery the Visual Studio Code needs to install. So, for us, not just the dev container, but all of the VS server infrastructure as well. Okay. So, far so good, now it's running our dev container set up command and, hey, now we can start to see our code in Visual Studio Code. This is, I think, the holy grail that developers have been looking for. We have a very consistent editor experience, including being able to have our launch configurations, but this is all running on the server.&lt;/p&gt;

&lt;p&gt;Now, when I'm doing like training on these sort of things, I always like to prove that there's no magic. So, if we head back over here and we go into the Mandalorian Gifs code, if I do that, and then if I touch new file and we head back in here, what we should see is new file. And if I delete that, we should see it deleted from here as well. It's super cool, right? Almost like magic, but because you guys know what's going on, it's not magic. Okay. So, let's go back. What was my next step? Okay. Now... We'll, wait. So, the way to think about this is it doesn't necessarily look at your Docker file, its goal is not to automatically start off the container. What it's done is it's essentially created a development environment for you. And so, now we create a terminal. This terminal is also in the context of the container. &lt;/p&gt;

&lt;p&gt;And so, now what we want to do is we want to run our application. So, let me just see if... Oh yeah. So, there's weird things like this that happen, bleeding edge technology. Let's see. Okay, great. So, Doppler is working. So, I'm going to run this application using Doppler because it takes care of an issue with dotenv files related to the host name, environment variable and I'll show you that problem in a second. Okay. So, we are launching our application and fantastic. Everything is working brilliantly. Now, we can open this in the browser, and this is tunneling from our local machine through the open SSH connection, then through the container on the host. So, the performance isn't terrible, but it's not the best. And so, this is why using something like ngrok can speed things up immensely. &lt;/p&gt;

&lt;p&gt;So, let's go and set that up. And also, one thing I should say too is... Yeah, we're definitely over time. I'm going to keep going for five minutes. If you need to drop off, no worries. This is all going to be covered in the recording, and we'll send you a link to that. So, one thing that I'm a big fan of is making sure that everything is self-contained in your package of [inaudible 00:44:38] and so here we've got all of the instructions for setting up ngrok and the way I've built this repository, obviously this isn't a real app that we're going to deploy and create a business around, but all of the code that you'll need to learn, I make sure that everything is here, including things like how to set up your Lightsail instance with Ubuntu. So, if you are someone that just wants to go and just check out the code and learn that way, go for it. &lt;/p&gt;

&lt;p&gt;So, this is working. This is all well and good. Now, I want to show you how do you use ngrok in order to make this even better. So, I'm going to say npm run ngrok setup. That's going to install everything. It's going to ask for my authtoken. And the authtoken is how I get features such as a stable remote URL, so I don't have to remember the random one that ngrok is going to create for me. Obviously, I'm using a password manager. Please use a password manager. Please don't ever use just normal passwords. &lt;/p&gt;

&lt;p&gt;There's just too many breaches and things like that. You need a way to create super robust passwords that you'd never be able to remember. Okay. So, ngrok has created a forwarding interface from this to local host in the container. So, if I click this, we'll see it's much faster. So, this is really the speed and workflow that you would expect. This isn't bad and you don't necessarily need ngrok. But for me, I don't think this performance is ideal. And it's simply a limitation of just using remote SSH and all of that sort of tunneling as well. So, this is for sure what I would go with. &lt;/p&gt;

&lt;p&gt;Okay. If things get weird, there's a couple of things you can do. You can tell Visual Studio Code that you simply want to rebuild the container. You can also ask Visual Studio Code to reload the window. And then if you get into some sort of like weird state where Visual Studio Code can't connect to like the container, even though you can verify the container is running, you can just disconnect, go back to your local view, and then you can go into using... Where's it going? Oh, once you reload Visual Studio Code locally, and you get access to Docker, then you can just manually delete the container that Visual Studio Code has created for you. You can delete the image that it's created for you. So, you can just get back to a clean slate. So, there's a few different things that can go wrong, but, hopefully after this guide it'll be reasonably smooth sailing for you. &lt;/p&gt;

&lt;p&gt;Okay. So, that's everything that I really wanted to cover in this webinar. We went a little bit over time, but not too bad. So, hopefully, by the end of this obviously, there's a lot to get your head around and a lot to set up, but I wanted to demystify the magic of dev containers. There's a lot of technology the Visual Studio Code is taken care of for you, but essentially the VS Code is a client. The Docker CLI is a local client as well. We're using open SSH to communicate with a remote host, in this case, Ubuntu VM on AWS. And that is what is allowing Visual Studio Code to control containers on that remote host. We've gone through some of the challenges and benefits of remote development in containers. &lt;/p&gt;

&lt;p&gt;The benefits are huge, but they're not without a bit of a learning curve and some configuration issues. So, whether this is the right thing for your team, it's up for debate. But I think it's worth experimenting with this, for sure. Taking you through how to set up a VM in AWS and I'd recommend Lightsail just because it's built for this kind of thing. You just want a simple way to create a VM. We've learnt how to configure Docker locally using the open SSH configuration. And we've learnt with Docker contexts, how we can switch between the AWS version and our local Docker version. You've got a basic understanding now of the dev container syntax at least in terms of the most important things that you're going to definitely need in terms of getting things working. And then we've also covered knowledge of source code mounting options. &lt;/p&gt;

&lt;p&gt;Now, as a quick side note, I just wanted to touch on why using dotenv files can just be really problematic in this sort of scenario. So, I'm going to stop our debug server here. What I'm going to do is I've got a sample dotenv file. I'm simply going to copy this and paste it. Okay. So, we've got a dotenv file. And now when I run our application, instead of using the Doppler CLI, I'm just going to run it as normal. So, we have all of our app configuration in dotenv file. Things seem pretty good, pretty straightforward. Let's bring up the debug console. Okay. Now, for those of you that have used Docker and have had issues with being able to reach into the application inside of Docker from the outside world and have had troubles, you'll know exactly what the problem is. &lt;/p&gt;

&lt;p&gt;If I now go back to here, it's not working. But we can see that the code is up, what's going on? Well, it turns out that the container itself has a host name, environment variable. And so, if we're using the dotenv [inaudible 00:50:28] which we are, let's see if there's a way that we can... All right, environment variables, so it makes the dotenv file the source of truth. Okay. So, it seems like this is something that's already been asked. &lt;/p&gt;

&lt;p&gt;Okay. So, essentially this is not something they're planning on fixing at all. So, that's really challenging. I think on the homepage, they actually do have a section for how to override. There we go. This is really odd and we don't want to have to go through all of this issue. So, you can work around this. You can go into your launch configuration and you could always specify that for here. We also want to find env. We set this to HOSTNAME. We set this to 0.0.0.0, and now if we run it with node... Oops, What I probably wanted to do is just reload that. Cool. Let's run that again.&lt;/p&gt;

&lt;p&gt;And, hopefully, we should see... Okay, now we're seeing HOSTNAME. And if we get back here, I know I've got things to work. Now, this is just... You might think, "Well, that's not that big a deal," but what we and our customers have noticed, it's that it's little paper cuts like this, that just add up to a lot of pain over time. You really shouldn't have to hack your launch configuration just to work around an issue with dotenv. So, that's why using a secret manager that has the source of truth to your secrets remotely is just going to be the way to go, regardless of whether it's Doppler or a different secrets manager, you really should look at moving away from dotenv files, not just for the productivity benefits, but for the security benefits as well. &lt;/p&gt;

&lt;p&gt;All right. Well, sorry, I did go 10 minutes over time. I am going to send out a link after this session with some recommended reading, some troubleshooting tips, things to consider such as Git credentials that we didn't have time to go into today. And you should now have all of the knowledge to at least confidently start in bringing dev containers to your own remote environments and your applications. So, I hope you enjoyed that, and all of that made a lot of sense. It is a lot to get your head around, but I think Remote dev containers are the future. That's what we've always wanted. And once GitHub Codespaces come out of beta, this will be exciting because then everyone to a certain extent will get the benefits of Remote dev containers for things such as easily previewing a pull request in a container, in a live environment.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-visual-studio-dev-containers-for-node-js-apps-on-aws-1p84"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>docker</category>
      <category>visualstudiocode</category>
    </item>
    <item>
      <title>Doppler: Visual Studio Code Remote Dev Containers on AWS Set Up Guide</title>
      <dc:creator>Ryan Blunden</dc:creator>
      <pubDate>Wed, 07 Jul 2021 11:28:00 +0000</pubDate>
      <link>https://forem.com/doppler/doppler-visual-studio-code-remote-dev-containers-on-aws-set-up-guide-46c5</link>
      <guid>https://forem.com/doppler/doppler-visual-studio-code-remote-dev-containers-on-aws-set-up-guide-46c5</guid>
      <description>&lt;p&gt;This guide will provide a list of system requirements and links to resources to get you set up and ready for remote development on AWS using &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;Visual Studio Code Dev Containers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the remote Ubuntu configuration steps are AWS-specific, the process will be almost identical for other cloud providers.&lt;/p&gt;

&lt;p&gt;This guide assumes you’re familiar with the Terminal, SSH, Docker, Linux (Ubuntu more specifically), and AWS, so if you’re not experienced using these technologies, be prepared to spend more time getting things set up.&lt;/p&gt;

&lt;p&gt;If you’re entirely new to Dev Containers, I’d recommend watching my &lt;a href="https://www.youtube.com/watch?v=_h9dz6drUNw"&gt;webinar on Visual Studio Dev Containers for Node.js apps on AWS&lt;/a&gt; to give you a solid overview of what’s involved. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;Visual Studio Code Remote Development in Containers guide&lt;/a&gt; and &lt;a href="https://code.visualstudio.com/docs/remote/containers-tutorial"&gt;tutorial&lt;/a&gt; are also great resources to help you get started.&lt;/p&gt;

&lt;p&gt;Let’s get to it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Repository
&lt;/h2&gt;

&lt;p&gt;If you’re looking for an actual Node.js application using Dev Containers, check out the &lt;a href="https://github.com/DopplerHQ/mandalorion-gifs-node"&gt;Mandalorion Gifs Node.js repository&lt;/a&gt;, including an example &lt;a href="https://github.com/DopplerHQ/mandalorion-gifs-node/blob/main/.devcontainer/devcontainer.json"&gt;devcontainer.json file&lt;/a&gt; and &lt;a href="https://github.com/DopplerHQ/mandalorion-gifs-node/blob/main/.vscode/launch.json"&gt;launch configuration&lt;/a&gt; for step-debugging.  &lt;/p&gt;

&lt;p&gt;Alternatively, you can use one of &lt;a href="https://github.com/microsoft?q=vscode-remote-try&amp;amp;type=&amp;amp;language=&amp;amp;sort="&gt;Microsoft’s sample repositories&lt;/a&gt; with examples in Go, Python, Rust, NET core, and other popular languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. AWS Credentials
&lt;/h2&gt;

&lt;p&gt;Your AWS credentials must have permissions required for managing a Lightsail instance and SSH keys, or if using EC2, you’ll need permissions for SSH Key Pairs, Security Groups, and EC2 instances.&lt;/p&gt;

&lt;p&gt;If using EC2, I’d recommend checking if your organization has a base AMI you should be using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2. Install Visual Studio Code Remote Container Extensions
&lt;/h2&gt;

&lt;p&gt;The following extensions are required for remote container-based development with Visual Studio Code:&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Install Docker Locally
&lt;/h2&gt;

&lt;p&gt;A local installation of Docker is required for Visual Studio Code to manage containers on the AWS instance from your local machine.&lt;/p&gt;

&lt;p&gt;Mac and Windows users should install &lt;a href="https://www.docker.com/products/docker-desktop"&gt;Docker Desktop 2.0+&lt;/a&gt;, and Linux users will need a distribution of &lt;a href="https://docs.docker.com/install/#supported-platforms"&gt;Docker CE/EE&lt;/a&gt; 18.06 or newer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4. Install OpenSSH Compatible SSH Client
&lt;/h2&gt;

&lt;p&gt;You will need a &lt;a href="https://code.visualstudio.com/docs/remote/troubleshooting#_installing-a-supported-ssh-client"&gt;Visual Studio Code supported OpenSSH compatible SSH client&lt;/a&gt; installed locally to create an SSH tunnel that Visual Studio Code will use to control Docker on the remote host. &lt;/p&gt;

&lt;p&gt;For Windows 10 users, if you’re currently using Putty, I strongly encourage you to use &lt;a href="https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse"&gt;Open SSH&lt;/a&gt; instead, as it will save you from frustrating troubleshooting experiences now and in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5. Create SSH Key
&lt;/h2&gt;

&lt;p&gt;If you haven’t already, you’ll need to generate an &lt;a href="https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent"&gt;SSH key and add it to the list of known identities&lt;/a&gt; for your SSH client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6. Create Ubuntu 20.04 AWS instance
&lt;/h2&gt;

&lt;p&gt;Configuring your AWS Ubuntu instance consists of two steps: Uploading or importing your SSH key, then creating the instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.1: Upload Local SSH Public Key to AWS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/getting-started/hands-on/launch-a-virtual-machine/"&gt;AWS Lightsail&lt;/a&gt; is recommended as it’s the easiest option for creating an &lt;strong&gt;Ubuntu 20.04&lt;/strong&gt; instance running Docker, but an EC2 instance works just as well if that’s your preference.&lt;/p&gt;

&lt;p&gt;Before creating your virtual machine, upload or import your local SSH public key (usually at ~/.ssh/id_rsa.pub) to either &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/lightsail-how-to-set-up-ssh"&gt;Lightsail&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html"&gt;EC2&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can optionally use an SSH key created by AWS, but I recommend using your own if possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.2: Create Ubuntu Instance
&lt;/h3&gt;

&lt;p&gt;The most crucial step is to &lt;strong&gt;select your uploaded public key&lt;/strong&gt; when choosing the SSH key for your instance. If you have an issue connecting via SSH later, it’s almost certainly because you didn’t upload or select your SSH key when creating the instance.&lt;/p&gt;

&lt;p&gt;Next, you can use the following code as the &lt;strong&gt;launch script&lt;/strong&gt; that will be run as part of initializing the instance, but you can always run these commands later once logged in via SSH.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noninteractive
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root

&lt;span class="c"&gt;# System dependencies&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; make nano

&lt;span class="c"&gt;# Install Docker&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/docker-archive-keyring.gpg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker-ce docker-ce-cli containerd.io

&lt;span class="c"&gt;# Install Docker Compose&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/docker-compose

&lt;span class="c"&gt;# Enable standard user (ubuntu) to manage containers (required for Remote Containers)&lt;/span&gt;
groupadd docker
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ubuntu
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7. SSH to the Ubuntu Instance
&lt;/h2&gt;

&lt;p&gt;Once the instance has been initialized, get the IP address from the AWS console, then log in using the following SSH command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh ubuntu@&lt;span class="nv"&gt;$INSTANCE_IP_ADDRESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re having trouble connecting, check that the public key you imported into AWS is attached to your instance, and if not, the easiest path forward is to delete, then recreate the instance with your SSH key selected.&lt;/p&gt;

&lt;p&gt;If you’re still having connection issues, re-run the ssh command with the &lt;code&gt;-v\&lt;/code&gt; flag to &lt;a href="http://tecmint.com/enable-debugging-mode-in-ssh/"&gt;enable the SSH client’s debug mode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the debug output still isn’t helping, check out &lt;a href="https://docs.digitalocean.com/products/droplets/resources/troubleshooting-ssh/connectivity/"&gt;DigitalOcean’s SSH troubleshooting guide&lt;/a&gt;, which will hopefully offer additional solutions to try.&lt;/p&gt;

&lt;p&gt;Once logged in, you’ll need to install and configure Docker if you haven’t already.&lt;/p&gt;

&lt;p&gt;Start by switching to the root user (&lt;code&gt;sudo su\&lt;/code&gt;), then copy and paste the &lt;a href="https://github.com/DopplerHQ/mandalorion-gifs-node/blob/main/aws/ubuntu-setup.sh"&gt;commands from our Ubuntu installation script&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8. Check Docker Works on AWS
&lt;/h2&gt;

&lt;p&gt;Once the installation commands have been run, log out, then SSH back in and try running a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run —rm hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this doesn’t work, try switching to the root user (&lt;code&gt;sudo su\&lt;/code&gt;), and try running the container.&lt;/p&gt;

&lt;p&gt;If you can run the container as the root user, it’s likely a permissions issue with the &lt;strong&gt;ubuntu&lt;/strong&gt; user. In this case, I recommend reading Docker’s documentation on &lt;a href="https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user"&gt;managing Docker as a non-root user&lt;/a&gt;, which should fix it.&lt;/p&gt;

&lt;p&gt;If all else fails, try rebooting the instance, SSH in again and try running the container as &lt;strong&gt;ubuntu&lt;/strong&gt; again, and if still having trouble, check out &lt;a href="https://code.visualstudio.com/docs/remote/troubleshooting"&gt;Visual Studio Code’s Troubleshooting Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9. Create a Docker Context Locally
&lt;/h2&gt;

&lt;p&gt;It’s time to start putting the pieces together!&lt;/p&gt;

&lt;p&gt;Now create a new &lt;a href="https://docs.docker.com/engine/context/working-with-contexts/"&gt;Docker context&lt;/a&gt; locally that will communicate with Docker running remotely via an SSH tunnel.&lt;/p&gt;

&lt;p&gt;Open a terminal locally, then run the following command to create a &lt;strong&gt;devcontainers&lt;/strong&gt; context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context create devcontainers &lt;span class="nt"&gt;--docker&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssh://ubuntu@&lt;span class="nv"&gt;$INSTANCE_IP_ADDRESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you’ll switch from the &lt;strong&gt;default&lt;/strong&gt; context (Docker locally) to &lt;strong&gt;devcontainers&lt;/strong&gt; by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context use devcontainers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the same terminal window, check that the Docker CLI can communicate with Docker on the remote machine by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run —rm hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you could not run the container, check out &lt;a href="https://code.visualstudio.com/docs/containers/ssh#_set-up-ssh-tunneling"&gt;Visual Studio Code’s SSH Tunneling for Docker guide&lt;/a&gt; to help you troubleshoot connection issues.&lt;/p&gt;

&lt;p&gt;If you want to switch back to using the default context, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context use default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 10. Try Running a Dev Container
&lt;/h2&gt;

&lt;p&gt;Phew! If you’ve made it this far, you’re now ready to try running a Dev Container remotely!&lt;/p&gt;

&lt;p&gt;Open Visual Studio Code, then run the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remote-Containers: Try a Development Container Sample...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then select one of the available options.&lt;/p&gt;

&lt;p&gt;This time you run a Dev Container, it can take anywhere from 5-10 minutes. Visual Studio Code needs first to set up the extension host and Visual Studio Code server before building the Dev Container environment.&lt;/p&gt;

&lt;p&gt;Presuming all goes well, you’ll eventually see the files in the container in the Explorer panel, and clicking on &lt;strong&gt;Terminal&lt;/strong&gt; will open a command-line prompt inside the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Awesome work!
&lt;/h2&gt;

&lt;p&gt;You’re running Dev Container remotely in the cloud with the experience of coding locally in Visual Studio Code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Now Bring the Dev Container Workflow to Your Application
&lt;/h2&gt;

&lt;p&gt;This guide has covered the required steps to set up and run Dev Containers remotely on AWS, although the same process applies regardless of which cloud provider you’re using.&lt;/p&gt;

&lt;p&gt;Below is a list of additional resources you'll need if implementing at a team level, such as &lt;a href="https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container"&gt;configuring Git credentials&lt;/a&gt; so you can push commits from your Dev Container environment.&lt;/p&gt;

&lt;p&gt;If you’re new to Dev Containers, I recommend starting with the following videos from Microsoft, then progress to the documentation, as understanding the big picture helps before diving into the documentation.&lt;/p&gt;

&lt;p&gt;Feedback is welcome, and you can reach us on &lt;a href="https://twitter.com/dopplerhq"&gt;Twitter&lt;/a&gt;, our &lt;a href="https://community.doppler.com/"&gt;Community forum&lt;/a&gt;, or send me an email at &lt;a href="//mailto:ryan.blunden@doppler.com"&gt;ryan.blunden@doppler.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dashboard.doppler.com/register?utm_source=dev&amp;amp;utm_medium=blog&amp;amp;utm_content=doppler-visual-studio-code-remote-dev-containers-on-aws-set-up-guide-46c5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VldNMgYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcu1dmvld1wzmz3b0wl0.png" alt="Create your free Doppler account" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>aws</category>
      <category>visualstudiocode</category>
      <category>node</category>
    </item>
  </channel>
</rss>
