<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown.min.css"
    />
    <title>haunt98 posts</title>
  </head>
  <style>
    .markdown-body {
      box-sizing: border-box;
      min-width: 200px;
      max-width: 980px;
      margin: 0 auto;
      padding: 45px;
    }

    @media (max-width: 767px) {
      .markdown-body {
        padding: 15px;
      }
    }
  </style>
  <body class="markdown-body">
    <h2>
      <a href="index.html"><code>~</code></a>
    </h2>
    <h1>
      <a
        id="user-content-dockerfile-for-go"
        class="anchor"
        aria-hidden="true"
        href="#dockerfile-for-go"
        ><span aria-hidden="true" class="octicon octicon-link"></span></a
      >Dockerfile for Go
    </h1>
    <p>
      Each time I start a new Go project, I repeat many steps. Like set up
      <code>.gitignore</code>, CI configs, Dockerfile, ...
    </p>
    <p>So I decide to have a baseline Dockerfile like this:</p>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">FROM</span> golang:1.20-bullseye as builder

<span class="pl-k">RUN</span> go install golang.org/dl/go1.20@latest \
    &amp;&amp; go1.20 download

<span class="pl-k">WORKDIR</span> /build

<span class="pl-k">COPY</span> go.mod .
<span class="pl-k">COPY</span> go.sum .
<span class="pl-k">COPY</span> vendor .
<span class="pl-k">COPY</span> . .

<span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath -ldflags=<span class="pl-s">"-s -w"</span> .

<span class="pl-k">FROM</span> gcr.io/distroless/base-debian11

<span class="pl-k">COPY</span> --from=builder /build/app /app

<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre>
    </div>
    <p>
      I use
      <a
        href="https://docs.docker.com/develop/develop-images/multistage-build/"
        rel="nofollow"
        >multi-stage build</a
      >
      to keep my image size small. First stage is
      <a href="https://hub.docker.com/_/golang" rel="nofollow"
        >Go official image</a
      >, second stage is
      <a href="https://github.com/GoogleContainerTools/distroless">Distroless</a
      >.
    </p>
    <p>
      Before Distroless, I use
      <a href="https://hub.docker.com/_/alpine" rel="nofollow"
        >Alpine official image</a
      >, There is a whole discussion on the Internet to choose which is the best
      base image for Go. After reading some blogs, I discover Distroless as a
      small and secure base image. So I stick with it for a while.
    </p>
    <p>
      Also, remember to match Distroless Debian version with Go official image
      Debian version.
    </p>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">FROM</span> golang:1.20-bullseye as builder</pre>
    </div>
    <p>
      This is Go image I use as a build stage. This can be official Go image or
      custom image is required in some companies.
    </p>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">RUN</span> go install golang.org/dl/go1.20@latest \
    &amp;&amp; go1.20 download</pre>
    </div>
    <p>
      This is optional. In my case, my company is slow to update Go image so I
      use this trick to install latest Go version.
    </p>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">WORKDIR</span> /build

<span class="pl-k">COPY</span> go.mod .
<span class="pl-k">COPY</span> go.sum .
<span class="pl-k">COPY</span> vendor .
<span class="pl-k">COPY</span> . .</pre>
    </div>
    <p>
      I use <code>/build</code> to emphasize that I am building something in
      that directory.
    </p>
    <p>
      The 4 <code>COPY</code> lines are familiar if you use Go enough. First is
      <code>go.mod</code> and <code>go.sum</code> because it defines Go modules.
      The second is <code>vendor</code>, this is optional but I use it because I
      don't want each time I build Dockerfile, I need to redownload Go modules.
    </p>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath -ldflags=<span class="pl-s">"-s -w"</span> .</pre>
    </div>
    <p>This is where I build Go program.</p>
    <ul>
      <li>
        <code>CGO_ENABLED=0</code> because I don't want to mess with C
        libraries.
      </li>
      <li>
        <code>GOOS=linux GOARCH=amd64</code> is easy to explain, Linux with
        x86-64.
      </li>
      <li>
        <code>GOAMD64=v3</code> is new since
        <a href="https://go.dev/doc/go1.18#amd64" rel="nofollow">Go 1.18</a>, I
        use v3 because I read about AMD64 version in
        <a
          href="https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst"
          rel="nofollow"
          >Arch Linux rfcs</a
        >. TLDR's newer computers are already x86-64-v3.
      </li>
      <li>
        <code>-tags timetzdata</code> to embed timezone database in case base
        image does not have.
      </li>
      <li><code>-trimpath</code> to support reproduce build.</li>
      <li><code>-ldflags="-s -w"</code> to strip debugging information.</li>
    </ul>
    <p>Also there are some experiment:</p>
    <ul>
      <li><code>GOMEMLIMIT=1024MiB</code>: soft memory limit.</li>
    </ul>
    <div class="highlight highlight-source-dockerfile">
      <pre><span class="pl-k">FROM</span> gcr.io/distroless/base-debian11

<span class="pl-k">COPY</span> --from=builder /build/app /app

<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre>
    </div>
    <p>Finally, I copy <code>app</code> to Distroless base image.</p>
    <h2>
      <a
        id="user-content-thanks"
        class="anchor"
        aria-hidden="true"
        href="#thanks"
        ><span aria-hidden="true" class="octicon octicon-link"></span></a
      >Thanks
    </h2>
    <ul>
      <li>
        <a
          href="https://boyter.org/posts/how-to-start-go-project-2023/"
          rel="nofollow"
          >How to start a Go project in 2023</a
        >
      </li>
      <li>
        <a
          href="https://words.filippo.io/shrink-your-go-binaries-with-this-one-weird-trick/"
          rel="nofollow"
          >Shrink your Go binaries with this one weird trick</a
        >
      </li>
      <li>
        <a
          href="https://weaviate.io/blog/gomemlimit-a-game-changer-for-high-memory-applications"
          rel="nofollow"
          >GOMEMLIMIT is a game changer for high-memory applications</a
        >
      </li>
    </ul>

    <div>
      Feel free to ask me via
      <a href="mailto:hauvipapro+posts@gmail.com">email</a> or
      <a rel="me" href="https://hachyderm.io/@haunguyen">Mastodon</a>.
      <br />Source code is available on
      <a href="https://github.com/haunt98/posts-go">GitHub</a>
      <a href="https://codeberg.org/yoshie/posts-go">Codeberg</a>
      <a href="https://git.sr.ht/~youngyoshie/posts-go">sourcehut</a>
      <a href="https://gitea.treehouse.systems/yoshie/posts-go">Treehouse</a>
      <a href="https://gitlab.com/youngyoshie/posts-go">GitLab</a>
    </div>
  </body>
</html>