<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://yourusername.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://yourusername.github.io/" rel="alternate" type="text/html" /><updated>2026-03-17T02:23:57+00:00</updated><id>https://yourusername.github.io/feed.xml</id><title type="html">Cory Koch | Software Engineer</title><subtitle>A personal blog about software engineering, cloud architecture, and modern development practices.</subtitle><entry><title type="html">Using Docker Templating to Keep Builds Flexible</title><link href="https://yourusername.github.io/2026/03/16/docker-templating.html" rel="alternate" type="text/html" title="Using Docker Templating to Keep Builds Flexible" /><published>2026-03-16T15:00:00+00:00</published><updated>2026-03-16T15:00:00+00:00</updated><id>https://yourusername.github.io/2026/03/16/docker-templating</id><content type="html" xml:base="https://yourusername.github.io/2026/03/16/docker-templating.html"><![CDATA[<p>Docker build-time templating is a small but powerful way to make your images reusable across environments.</p>

<p>In this post, we’ll cover when to use Docker’s templating features, how to use <code class="language-plaintext highlighter-rouge">ARG</code> + <code class="language-plaintext highlighter-rouge">ENV</code>, and a real-world example with a multi-stage Dockerfile.</p>

<h2 id="why-docker-templating-matters">Why Docker templating matters</h2>

<p>Hardcoded values (<code class="language-plaintext highlighter-rouge">ENV</code>, paths, app names) make Dockerfiles brittle. Templating turns a statically-defined image into a configurable artifact for staging, production, and CI.</p>

<p>Benefits:</p>

<ul>
  <li>Fewer Dockerfile copies across projects</li>
  <li>Extra control for CI branching and deployments</li>
  <li>Reduce risk of accidentally baking secrets into images</li>
</ul>

<h2 id="1-build-args-and-environment-variables">1) Build args and environment variables</h2>

<p>Use <code class="language-plaintext highlighter-rouge">ARG</code> at build time and map it to runtime defaults with <code class="language-plaintext highlighter-rouge">ENV</code>.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Dockerfile</span>
<span class="k">ARG</span><span class="s"> APP_ENV=production</span>
<span class="k">FROM</span><span class="w"> </span><span class="s">node:20-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span> <span class="nt">--production</span>
<span class="k">COPY</span><span class="s"> . .</span>

<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">runtime</span>
<span class="k">ARG</span><span class="s"> APP_ENV</span>
<span class="k">ENV</span><span class="s"> APP_ENV=${APP_ENV}</span>
<span class="k">EXPOSE</span><span class="s"> 3000</span>
<span class="k">CMD</span><span class="s"> ["node", "server.js"]</span>
</code></pre></div></div>

<p>Build with custom args:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">--build-arg</span> <span class="nv">APP_ENV</span><span class="o">=</span>staging <span class="nt">-t</span> myapp:staging <span class="nb">.</span>
</code></pre></div></div>

<p>Then runtime in your app can read <code class="language-plaintext highlighter-rouge">process.env.APP_ENV</code>.</p>

<h2 id="2-template-with-docker-compose-variables">2) Template with <code class="language-plaintext highlighter-rouge">docker-compose</code> variables</h2>

<p>Use <code class="language-plaintext highlighter-rouge">.env</code> files or pass values via compose CLI.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.9"</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">web</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span>
      <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
      <span class="na">args</span><span class="pi">:</span>
        <span class="na">APP_ENV</span><span class="pi">:</span> <span class="s">${APP_ENV:-production}</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:${TAG:-latest}</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">3000:3000"</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">.env</code>:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>APP_ENV=development
TAG=dev
</code></pre></div></div>

<p>Then run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker compose up <span class="nt">--build</span>
</code></pre></div></div>

<h2 id="3-multi-stage-with-templated-stage-names-and-scripts">3) Multi-stage with templated stage names and scripts</h2>

<p>You can use args and conditional scripts in shell to template commands.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ARG</span><span class="s"> NODE_VERSION=20-alpine</span>
<span class="k">FROM</span><span class="w"> </span><span class="s">node:${NODE_VERSION}</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span>
<span class="k">COPY</span><span class="s"> . ./</span>
<span class="k">RUN </span>npm run build

<span class="k">FROM</span><span class="w"> </span><span class="s">nginx:stable-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">runtime</span>
<span class="k">ARG</span><span class="s"> APP_PORT=8080</span>
<span class="k">ENV</span><span class="s"> PORT=${APP_PORT}</span>
<span class="k">COPY</span><span class="s"> --from=builder /app/build /usr/share/nginx/html</span>
<span class="k">EXPOSE</span><span class="s"> ${APP_PORT}</span>
<span class="k">CMD</span><span class="s"> ["nginx", "-g", "daemon off;"]</span>
</code></pre></div></div>

<p>Build with templated values:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">--build-arg</span> <span class="nv">NODE_VERSION</span><span class="o">=</span>20-alpine <span class="nt">--build-arg</span> <span class="nv">APP_PORT</span><span class="o">=</span>8080 <span class="nt">-t</span> myapp:latest <span class="nb">.</span>
</code></pre></div></div>

<h2 id="4-when-to-avoid-templating">4) When to avoid templating</h2>

<p>Templating is not a replacement for secrets management. Don’t template API keys or credentials directly into builds.</p>

<p>Keep secrets in secure stores (<code class="language-plaintext highlighter-rouge">Vault</code>, AWS Secrets Manager, GitHub Secrets), and inject them at runtime.</p>

<h2 id="5-quick-checklist-for-clean-templating">5) Quick checklist for clean templating</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Use <code class="language-plaintext highlighter-rouge">ARG</code> for build-time variation</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Use <code class="language-plaintext highlighter-rouge">ENV</code> for runtime defaults</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Keep minimal image instructions</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Avoid secrets in Dockerfile</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Use tags for environment-specific builds</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Docker templating with <code class="language-plaintext highlighter-rouge">ARG</code>, <code class="language-plaintext highlighter-rouge">ENV</code>, and compose variables helps you produce the same image across environments while controlling behavior through flags. When done correctly, it makes builds repeatable, faster, and safer.</p>

<p>Try it today by converting one hardcoded value in your Dockerfile to an <code class="language-plaintext highlighter-rouge">ARG</code> and passing it from your CI pipeline.</p>]]></content><author><name>Cory Koch</name></author><category term="docker" /><category term="devops" /><category term="templating" /><category term="ci" /><summary type="html"><![CDATA[Docker build-time templating is a small but powerful way to make your images reusable across environments.]]></summary></entry><entry><title type="html">Docker Best Practices</title><link href="https://yourusername.github.io/2025/03/10/docker-best-practices.html" rel="alternate" type="text/html" title="Docker Best Practices" /><published>2025-03-10T00:00:00+00:00</published><updated>2025-03-10T00:00:00+00:00</updated><id>https://yourusername.github.io/2025/03/10/docker-best-practices</id><content type="html" xml:base="https://yourusername.github.io/2025/03/10/docker-best-practices.html"><![CDATA[]]></content><author><name>Cory Koch</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Mastering Git Workflows</title><link href="https://yourusername.github.io/2025/02/20/mastering-git-workflows.html" rel="alternate" type="text/html" title="Mastering Git Workflows" /><published>2025-02-20T00:00:00+00:00</published><updated>2025-02-20T00:00:00+00:00</updated><id>https://yourusername.github.io/2025/02/20/mastering-git-workflows</id><content type="html" xml:base="https://yourusername.github.io/2025/02/20/mastering-git-workflows.html"><![CDATA[]]></content><author><name>Cory Koch</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Getting Started with Kubernetes: A Practical Guide</title><link href="https://yourusername.github.io/2025/01/15/getting-started-with-kubernetes.html" rel="alternate" type="text/html" title="Getting Started with Kubernetes: A Practical Guide" /><published>2025-01-15T15:00:00+00:00</published><updated>2025-01-15T15:00:00+00:00</updated><id>https://yourusername.github.io/2025/01/15/getting-started-with-kubernetes</id><content type="html" xml:base="https://yourusername.github.io/2025/01/15/getting-started-with-kubernetes.html"><![CDATA[<p>Kubernetes has become the de facto standard for container orchestration. In this post, I’ll walk you through the fundamentals and show you how to deploy your first application.</p>

<h2 id="what-is-kubernetes">What is Kubernetes?</h2>

<p>Kubernetes is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications.</p>

<h2 id="key-concepts">Key Concepts</h2>

<h3 id="pods">Pods</h3>

<p>The smallest deployable unit in Kubernetes. A Pod can contain one or more containers.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">my-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">nginx:latest</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">80</span>
</code></pre></div></div>

<h3 id="deployments">Deployments</h3>

<p>Deployments provide declarative updates for Pods and ReplicaSets.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">nginx-deployment</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">nginx</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">nginx</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">nginx</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">nginx:1.14.2</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">80</span>
</code></pre></div></div>

<h2 id="getting-started">Getting Started</h2>

<ol>
  <li>Install kubectl</li>
  <li>Set up a local cluster with Minikube or Kind</li>
  <li>Deploy your first application</li>
  <li>Expose it via a Service</li>
</ol>

<p>Stay tuned for more in-depth Kubernetes tutorials!</p>]]></content><author><name>Cory Koch</name></author><category term="kubernetes" /><category term="devops" /><category term="containers" /><summary type="html"><![CDATA[Kubernetes has become the de facto standard for container orchestration. In this post, I’ll walk you through the fundamentals and show you how to deploy your first application.]]></summary></entry></feed>