<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Bitbucket - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Bitbucket - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:25:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/bitbucket/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to securely deploy to Kubernetes from Bitbucket Pipelines ]]>
                </title>
                <description>
                    <![CDATA[ By Dane Stevens Over 100,000 GitHub repos have leaked API or cryptographic keys - ZDNet Hands up if this has happened to you. You're reading a well-written article on one of countless topics, and you get to the line that goes something like this: //... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-securely-deploy-to-kubernetes-from-bitbucket-pipelines-78e668f331b9/</link>
                <guid isPermaLink="false">66d84e8ac8d279d4f28c479b</guid>
                
                    <category>
                        <![CDATA[ Bitbucket ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 18 Apr 2019 18:56:43 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9ca305740569d1a4ca58a3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dane Stevens</p>
<p><img src="https://cdn.tueri.io/274877906995/lock-it-down.jpg" alt="Locking it down" width="5760" height="3840" loading="lazy"></p>
<p><a target="_blank" href="https://www.zdnet.com/article/over-100000-github-repos-have-leaked-api-or-cryptographic-keys/">Over 100,000 GitHub repos have leaked API or cryptographic keys - ZDNet</a></p>
<p>Hands up if this has happened to you. You're reading a well-written article on one of countless topics, and you get to the line that goes something like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// DO NOT DO THIS IN A PRODUCTION APP</span>
<span class="hljs-keyword">const</span> API_KEY = <span class="hljs-string">'&lt;api-key-displayed-in-plain-text&gt;'</span>
</code></pre>
<p>Ok, so how should you be doing this? Unfortunately, there isn't a one-size-fits-all approach to securing your secrets. Different programming languages deployed in different environments all handle secrets in their own way.</p>
<p>Suffice it to say that you should never store secrets in your code or repository. Secrets should be passed into your app through environment variables at the last possible moment.</p>
<h2 id="heading-bitbucket-pipelines-continuous-delivery">Bitbucket Pipelines - Continuous Delivery</h2>
<p>I have been using <a target="_blank" href="https://medium.com/r/?url=https%3A%2F%2Fbitbucket.org%2Fproduct%2Ffeatures%2Fpipelines%3Futm_source%3DMedium%26utm_medium%3DPost%26utm_campaign%3DTueri%26utm_content%3DKubernetes">Bitbucket Pipelines</a> since it was in Alpha and I have to say, it's fantastic. It has to be the quickest and easiest way to setup continuous delivery right from your repo.</p>
<p>Pipelines are configured with YAML files and can be very simple or extremely complex depending on your needs.</p>
<h3 id="heading-pipelines-configuration">Pipelines Configuration</h3>
<p>I like to break up my build jobs into steps for a couple of reasons:</p>
<ul>
<li>If a step fails, you can re-run individual steps.</li>
<li>Each step is isolated from the others. Only your base repo and any "artifacts" you declare will be passed to the next step.</li>
</ul>
<p>Here is a 3-step bitbucket-pipelines.yml file that takes a create-react-app site, packages it as a Docker image and deploys it to a Kubernetes cluster:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">options:</span>
  <span class="hljs-comment"># Enable docker for the Pipeline</span>
  <span class="hljs-attr">docker:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">pipelines:</span>
  <span class="hljs-attr">branches:</span>
    <span class="hljs-attr">master:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">app</span> <span class="hljs-string">for</span> <span class="hljs-string">Production</span> <span class="hljs-string">(create-react-app)</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">mhart/alpine-node:10</span>
          <span class="hljs-attr">caches:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">node</span>
          <span class="hljs-attr">script:</span>
            <span class="hljs-comment"># Install Dependencies</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
            <span class="hljs-comment"># Run our Tests</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">test</span>
            <span class="hljs-comment"># Package App for Production</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>
          <span class="hljs-attr">artifacts:</span>
            <span class="hljs-comment"># Pass the "build" Directory to the Next Step</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">build/**</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span>
          <span class="hljs-attr">script:</span>
            <span class="hljs-comment"># <span class="hljs-doctag">NOTE:</span> Set $DOCKER_HUB_USERNAME and $DOCKER_HUB_PASSWORD as environment SECRETS in Bitbucket repository settings</span>
            <span class="hljs-comment"># Use $BITBUCKET_COMMIT to tag our docker image</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">export</span> <span class="hljs-string">IMAGE_NAME=&lt;docker-username&gt;/&lt;docker-image&gt;:$BITBUCKET_COMMIT</span>
            <span class="hljs-comment"># Build the Docker image (this will use the Dockerfile in the root of the repo)</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">docker</span> <span class="hljs-string">build</span> <span class="hljs-string">-t</span> <span class="hljs-string">$IMAGE_NAME</span> <span class="hljs-string">.</span>
            <span class="hljs-comment"># Authenticate with the Docker Hub registry</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">-u</span> <span class="hljs-string">$DOCKER_HUB_USERNAME</span> <span class="hljs-string">-p</span> <span class="hljs-string">$DOCKER_HUB_PASSWORD</span>
            <span class="hljs-comment"># Push the new Docker image to the Docker registry</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-string">$IMAGE_NAME</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span>
          <span class="hljs-comment"># trigger: manual</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Kubernetes</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">atlassian/pipelines-kubectl</span>
          <span class="hljs-attr">script:</span>
            <span class="hljs-comment"># <span class="hljs-doctag">NOTE:</span> $KUBECONFIG is secret stored as a base64 encoded string</span>
            <span class="hljs-comment"># Base64 decode our kubeconfig file into a temporary kubeconfig.yml file (this will be destroyed automatically after this step runs)</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">$KUBECONFIG</span> <span class="hljs-string">|</span> <span class="hljs-string">base64</span> <span class="hljs-string">-d</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">kubeconfig.yml</span>
            <span class="hljs-comment"># Tell our Kubernetes deployment to use the new Docker image tag</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">--kubeconfig=kubeconfig.yml</span> <span class="hljs-string">--namespace=&lt;namespace&gt;</span> <span class="hljs-string">set</span> <span class="hljs-string">image</span> <span class="hljs-string">deployment/&lt;deployment-name&gt;</span> <span class="hljs-string">&lt;deployment-name&gt;=&lt;docker-username&gt;/&lt;docker-image&gt;:$BITBUCKET_COMMIT</span>
</code></pre>
<p><em>bitbucket-pipelines.yml</em></p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> mhart/alpine-node:<span class="hljs-number">10</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span>

<span class="hljs-comment"># Install http server</span>
<span class="hljs-keyword">RUN</span><span class="bash"> yarn global add serve</span>

<span class="hljs-comment"># Bundle app source</span>
<span class="hljs-keyword">COPY</span><span class="bash"> build /app/build</span>

<span class="hljs-comment"># Run serve</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [ <span class="hljs-string">"serve"</span>, <span class="hljs-string">"-n"</span>, <span class="hljs-string">"-s"</span>, <span class="hljs-string">"build"</span> ]</span>
</code></pre>
<p><em>Dockerfile</em></p>
<p>Here's the important part of all that:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-string">echo</span> <span class="hljs-string">$KUBECONFIG</span> <span class="hljs-string">|</span> <span class="hljs-string">base64</span> <span class="hljs-string">-d</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">kubeconfig.yml</span>
</code></pre>
<p>Our kubeconfig file is stored as a Base64 encoded string in a Bitbucket secret named <code>$KUBECONFIG</code>.</p>
<p><em>Bitbucket secrets are stored encrypted, and decrypted when you call the variable in pipelines.</em></p>
<p>We decode the <code>$KUBECONFIG</code> variable and store it in a temporary file called kubeconfig.yml which is automatically deleted as soon as this step completes.</p>
<h2 id="heading-breaking-it-down">Breaking it Down</h2>
<h3 id="heading-step-1">Step 1</h3>
<ol>
<li>Install dependencies</li>
<li>Run tests</li>
<li>Build</li>
<li>Pass build directory to Step 2</li>
</ol>
<h3 id="heading-step-2">Step 2</h3>
<ol>
<li>Name Docker image</li>
<li>Build Docker image</li>
<li>Push image to Docker Hub</li>
</ol>
<h3 id="heading-step-3">Step 3</h3>
<ol>
<li>Decode kubeconfig</li>
<li>Set deployment image</li>
</ol>
<h2 id="heading-build-performance">Build Performance</h2>
<p>This entire build takes less than 1 minute 40 seconds and using Alpine Node the Docker image is just 29 MB.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Securing your secrets isn't hard, but it starts with knowing where to look.</p>
<p>Some tips for securing secrets in different Node.js environments:</p>
<ul>
<li>Node.js (Development): use .env files and .gitignore to keep .env files out of your repository.</li>
<li>Node.js (Production): use Kubernetes Secrets, Docker Secrets and pass as environment variables into the container.</li>
</ul>
<h3 id="heading-remember-this-one-rule">Remember this one rule:</h3>
<ul>
<li>Don't store secrets in your code, your repository or your docker image.</li>
</ul>
<p>Happy coding!</p>
<hr>
<p>_Originally published at <a target="_blank" href="https://tueri.io/blog/2019-04-04-how-to-securely-deploy-to-kubernetes-from-bitbucket-pipelines/?utm_source=FreeCodeCamp&amp;utm_medium=Post&amp;utm_campaign=Continuous%20Deployment">Tueri.io</a>_</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
