<?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>DEV Community: Pablo Lagos</title>
    <description>The latest articles on DEV Community by Pablo Lagos (@pablo74).</description>
    <link>https://dev.to/pablo74</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%2F2026738%2F83cf9ac8-5dcd-42ab-bceb-8ec19fd1a5d3.jpeg</url>
      <title>DEV Community: Pablo Lagos</title>
      <link>https://dev.to/pablo74</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pablo74"/>
    <language>en</language>
    <item>
      <title>🧊 How to Build a Fully Static Go Binary — Every Time (with go-builder and Docker)</title>
      <dc:creator>Pablo Lagos</dc:creator>
      <pubDate>Sat, 28 Jun 2025 07:07:38 +0000</pubDate>
      <link>https://dev.to/pablo74/how-to-build-a-fully-static-go-binary-every-time-with-go-builder-and-docker-45go</link>
      <guid>https://dev.to/pablo74/how-to-build-a-fully-static-go-binary-every-time-with-go-builder-and-docker-45go</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Want your Go app to “just run” on any Linux server, regardless of glibc?&lt;br&gt;
You need &lt;strong&gt;real static linking&lt;/strong&gt; — and it’s surprisingly easy to get wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide you’ll learn:&lt;/p&gt;

&lt;p&gt;✅ Why CGO can break static builds&lt;br&gt;
✅ How to cross-compile with &lt;code&gt;musl&lt;/code&gt;&lt;br&gt;
✅ How to do it &lt;em&gt;inside Docker&lt;/em&gt; for guaranteed reproducibility&lt;br&gt;
✅ How to declare all this in a clean &lt;code&gt;.gobuilder.yml&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚙️ The problem: CGO and glibc versions
&lt;/h2&gt;

&lt;p&gt;By default, Go &lt;strong&gt;wants&lt;/strong&gt; to make static binaries. But the second you use &lt;code&gt;CGO_ENABLED=1&lt;/code&gt; (for SQLite, image processing, or networking optimizations), the linker pulls in your system’s libc — and suddenly you’ve got a dynamic dependency.&lt;/p&gt;

&lt;p&gt;Run your binary on a server with an older glibc, and boom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libc.so.6: version `GLIBC_2.32' not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🫧 The solution: build with &lt;code&gt;musl&lt;/code&gt; and force static linking
&lt;/h2&gt;

&lt;p&gt;Musl is a lightweight C standard library designed for static builds.&lt;br&gt;
Here’s the secret sauce:&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="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;CC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;musl-gcc &lt;span class="se"&gt;\&lt;/span&gt;
  go build &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-linkmode external -extldflags '-static'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file myapp   &lt;span class="c"&gt;# should say: statically linked&lt;/span&gt;
ldd myapp    &lt;span class="c"&gt;# should say: not a dynamic executable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ ✅ ✅&lt;/p&gt;




&lt;h2&gt;
  
  
  🗂️ Making this repeatable with &lt;code&gt;go-builder&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Manually remembering &lt;code&gt;CGO_ENABLED&lt;/code&gt;, &lt;code&gt;CC&lt;/code&gt;, and those scary &lt;code&gt;-ldflags&lt;/code&gt;? Painful.&lt;br&gt;
Let’s lock it down:&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="c1"&gt;# .gobuilder.yml&lt;/span&gt;
&lt;span class="na"&gt;build_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;builds&lt;/span&gt;
&lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./cmd/myapp&lt;/span&gt;
&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
  &lt;span class="na"&gt;CC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;musl-gcc&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ldflags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-linkmode&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;external&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-extldflags&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'-static'"&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;main.version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${VERSION:-dev}"&lt;/span&gt;
  &lt;span class="na"&gt;trimpath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;verify_static&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# check with 'file' after build!&lt;/span&gt;

&lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
    &lt;span class="na"&gt;arch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amd64&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
    &lt;span class="na"&gt;arch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arm64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&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="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.4.0 go-builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ You get binaries under &lt;code&gt;builds/linux/amd64/&lt;/code&gt; and &lt;code&gt;builds/linux/arm64/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;✅ If the linker produces a dynamic binary by mistake, the &lt;code&gt;verify_static: true&lt;/code&gt; will fail the build.&lt;/li&gt;
&lt;li&gt;✅ No bash scripts, no fragile Makefiles.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🐳 What if you don’t have &lt;code&gt;musl-gcc&lt;/code&gt; installed?
&lt;/h2&gt;

&lt;p&gt;Use a container with &lt;code&gt;musl&lt;/code&gt; ready to go. Here’s a &lt;code&gt;docker&lt;/code&gt; block in the same YAML:&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;docker&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;golang:1.23-alpine&lt;/span&gt;
  &lt;span class="na"&gt;workdir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/src&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh&lt;/span&gt;
  &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add --no-cache musl-dev build-base git&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
    &lt;span class="na"&gt;CC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcc&lt;/span&gt; &lt;span class="c1"&gt;# gcc is the name of musl-gcc in alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your build runs &lt;strong&gt;inside Alpine Linux&lt;/strong&gt;, with musl preinstalled. The &lt;code&gt;builds/&lt;/code&gt; folder is shared with your host, so your static binaries are there immediately — no extra copy step.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go-builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your machine stays clean: no local musl toolchain needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Dry-run: double-check before wasting time
&lt;/h2&gt;

&lt;p&gt;Want to see exactly what &lt;code&gt;go-builder&lt;/code&gt; will run?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go-builder &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker &lt;code&gt;run&lt;/code&gt; command (if &lt;code&gt;docker:&lt;/code&gt; is defined)&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;go build&lt;/code&gt; line, with every tag, flag, and linker trick.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Key takeaways
&lt;/h2&gt;

&lt;p&gt;✔ &lt;strong&gt;musl&lt;/strong&gt; makes true static linking predictable&lt;br&gt;
✔ &lt;strong&gt;CGO_ENABLED=1 CC=musl-gcc&lt;/strong&gt; + &lt;code&gt;-extldflags '-static'&lt;/code&gt; are the magic trio&lt;br&gt;
✔ &lt;code&gt;verify_static: true&lt;/code&gt; guarantees your binary is really static&lt;br&gt;
✔ Using Docker means zero “it works on my machine” moments&lt;br&gt;
✔ &lt;code&gt;go-builder&lt;/code&gt; locks your build config in YAML: repeatable, documented, and easy to share&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Next steps
&lt;/h2&gt;

&lt;p&gt;📄 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/pablolagos/go-builder" rel="noopener noreferrer"&gt;github.com/pablolagos/go-builder&lt;/a&gt;&lt;br&gt;
🧩 Install: &lt;code&gt;go install github.com/pablolagos/go-builder@latest&lt;/code&gt;&lt;br&gt;
⚡ &lt;code&gt;--init&lt;/code&gt; writes a starter YAML, ready to tweak.&lt;/p&gt;

&lt;p&gt;Stop chasing glibc errors. Ship one binary, run it anywhere.&lt;br&gt;
&lt;strong&gt;Static linking can actually be fun.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Do you ship static Go binaries?&lt;/strong&gt; Drop your tips below — I'd love to hear how you do it!&lt;/p&gt;

</description>
      <category>compiling</category>
      <category>developers</category>
    </item>
    <item>
      <title>Fixing “GLIBC_x.x not found” in Go Binaries: Build Once, Run Anywhere</title>
      <dc:creator>Pablo Lagos</dc:creator>
      <pubDate>Sat, 28 Jun 2025 06:55:43 +0000</pubDate>
      <link>https://dev.to/pablo74/fixing-glibcxx-not-found-in-go-binaries-build-once-run-anywhere-129j</link>
      <guid>https://dev.to/pablo74/fixing-glibcxx-not-found-in-go-binaries-build-once-run-anywhere-129j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“I built my shiny Go app. It works on my machine. But when I run it on a server — boom:&lt;/em&gt;&lt;br&gt;
&lt;code&gt;Error while loading shared libraries: libc.so.6: version 'GLIBC_2.32' not found&lt;/code&gt;.”_&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sound familiar? Let’s break down why this happens, what it means, and &lt;strong&gt;how you can prevent it every time&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🤔 Why does &lt;code&gt;GLIBC&lt;/code&gt; bite you?
&lt;/h3&gt;

&lt;p&gt;Go is famous for producing &lt;strong&gt;static&lt;/strong&gt; binaries by default. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your code (or a dependency) uses &lt;strong&gt;CGO&lt;/strong&gt;, Go will link to your system’s C libraries.&lt;/li&gt;
&lt;li&gt;On your dev machine, your libc might be GLIBC 2.35 (Ubuntu 22.04).&lt;/li&gt;
&lt;li&gt;But your production machine could be older — say, GLIBC 2.28 (Debian 10).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result? Your binary demands symbols that the older server’s libc doesn’t have.&lt;br&gt;
No fancy containerization will fix that if the binary expects to find &lt;code&gt;libc.so.6&lt;/code&gt; on the host!&lt;/p&gt;


&lt;h2&gt;
  
  
  ✔️ 3 ways to make this error disappear
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ✅ 1. &lt;strong&gt;Use a musl-based toolchain&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Musl is a minimal C library that’s explicitly designed for static linking.&lt;/p&gt;

&lt;p&gt;For example:&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="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;CC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64-linux-musl-gcc &lt;span class="se"&gt;\&lt;/span&gt;
  go build &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-linkmode external -extldflags '-static'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You ship &lt;em&gt;zero&lt;/em&gt; dynamic libc dependency.&lt;/li&gt;
&lt;li&gt;Your binary works on any Linux kernel, no matter the host’s GLIBC version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also build inside Alpine (which uses musl by default):&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; golang:1.23-alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; musl-dev build-base

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-tags&lt;/span&gt; netgo &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-linkmode external -extldflags '-static'"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; myapp ./cmd/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ✅ 2. &lt;strong&gt;Check that your binary is truly static&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Don’t guess — verify!&lt;br&gt;
Run &lt;code&gt;file&lt;/code&gt; on the binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file myapp
&lt;span class="c"&gt;# Should say: ELF 64-bit LSB executable, statically linked&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or check dynamic dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ldd myapp
&lt;span class="c"&gt;# Should say: "not a dynamic executable"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see &lt;code&gt;libc.so.6&lt;/code&gt; in the list: your binary is &lt;em&gt;not&lt;/em&gt; fully static!&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ 3. &lt;strong&gt;Use a minimal base image or scratch&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you package your binary in a container, you don’t want to drag along a full glibc runtime just for your app.&lt;/p&gt;

&lt;p&gt;Examples:&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="c"&gt;# Option A: static binary → scratch&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; myapp /myapp&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/myapp"]&lt;/span&gt;

&lt;span class="c"&gt;# Option B: Alpine → musl → always works&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.19&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; myapp /usr/local/bin/myapp&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["myapp"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No glibc, no runtime version mismatch.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Gotchas: When you &lt;em&gt;can’t&lt;/em&gt; be fully static
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Some CGO dependencies can’t be statically linked easily (e.g., dynamic plugins, drivers, or certain SQLite features).&lt;/li&gt;
&lt;li&gt;In these cases, match your build environment to your production environment: same glibc, same version, same distro.&lt;/li&gt;
&lt;li&gt;Tools like Docker help, but musl or static linking is the long-term fix.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧘 Stay sane: make it declarative
&lt;/h2&gt;

&lt;p&gt;To avoid forgetting &lt;code&gt;CGO_ENABLED&lt;/code&gt;, &lt;code&gt;CC&lt;/code&gt;, or &lt;code&gt;-extldflags&lt;/code&gt;, keep your build config versioned:&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="c1"&gt;# .gobuilder.yml&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
  &lt;span class="na"&gt;CC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcc&lt;/span&gt;
&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ldflags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-linkmode&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;external&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-extldflags&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'-static'"&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;main.version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${VERSION}"&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;netgo"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file, repeatable builds, no surprises.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary: one checklist for portable Go binaries
&lt;/h2&gt;

&lt;p&gt;✔ Always verify: &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;ldd&lt;/code&gt;&lt;br&gt;
✔ Use musl + static linking when possible&lt;br&gt;
✔ Test your binary on your oldest target environment&lt;br&gt;
✔ Don’t depend on your dev machine’s glibc!&lt;/p&gt;

&lt;p&gt;Next time you see &lt;em&gt;“version &lt;code&gt;GLIBC_2.xx&lt;/code&gt; not found”&lt;/em&gt;, you’ll know exactly what to do.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Happy compiling!&lt;/strong&gt;&lt;br&gt;
If you want a reproducible cross-build workflow with YAML and Docker baked in, check out &lt;a href="https://github.com/pablolagos/go-builder" rel="noopener noreferrer"&gt;go-builder&lt;/a&gt; — it keeps static linking predictable.&lt;/p&gt;

</description>
      <category>compiling</category>
      <category>developers</category>
    </item>
    <item>
      <title>Creating users with SSH access only in Linux</title>
      <dc:creator>Pablo Lagos</dc:creator>
      <pubDate>Wed, 04 Sep 2024 14:24:16 +0000</pubDate>
      <link>https://dev.to/pablo74/creating-users-with-ssh-access-only-in-linux-3hm1</link>
      <guid>https://dev.to/pablo74/creating-users-with-ssh-access-only-in-linux-3hm1</guid>
      <description>&lt;p&gt;If we’re managing a Linux server and looking to enhance its security, a great step we can take is to create user accounts that can only log in using SSH keys, rather than relying on passwords. &lt;/p&gt;

&lt;p&gt;This approach helps us protect against brute-force attacks and unauthorized access attempts that target weak or compromised passwords. &lt;/p&gt;

&lt;p&gt;In this guide, we'll walk through the steps to create a new user with a home directory, and configure our server to allow login for this user exclusively through SSH key-based authentication. &lt;/p&gt;

&lt;p&gt;By doing so, we’ll establish a more secure and reliable access method for our server.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the User with a Home Directory
&lt;/h3&gt;

&lt;p&gt;Run the following command to create the user general with a home directory:&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;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;-m: Creates the home directory (/home/general).&lt;br&gt;
 -s /bin/bash: Sets /bin/bash as the default login shell for the user.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Configure SSH Key-Only Login
&lt;/h3&gt;

&lt;p&gt;To disable password login and allow only SSH key-based access, follow these steps:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Lock the user's password to prevent password login:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-l&lt;/span&gt; &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This command locks the account for password-based login.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Set up SSH keys for the user:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Switch to the new user:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;su - &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create the .ssh directory in the user's home directory and set the correct permissions:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create or copy the authorized_keys file with the allowed public SSH key and set the correct permissions:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ~/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Paste the public SSH key (e.g., id_rsa.pub) into the ~/.ssh/authorized_keys file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exit the general user:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Verify SSH Configuration
&lt;/h3&gt;

&lt;p&gt;Edit the SSH configuration file to ensure that SSH key authentication is allowed:&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;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you have the following settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PubkeyAuthentication yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the setting is commented &lt;code&gt;#PubkeyAuthentication yes&lt;/code&gt;, it will work correctly, as the default value for PubkeyAuthentication is yes&lt;/p&gt;

&lt;p&gt;If the PubkeyAuthentication was changed, save the changes and restart the SSH service:&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;sudo &lt;/span&gt;systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Test SSH Access
&lt;/h3&gt;

&lt;p&gt;Now, try logging in with the new created user 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;ssh &amp;lt;username&amp;gt;@server-ip &lt;span class="nt"&gt;-i&lt;/span&gt; path/to/private/key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should only be able to log in if you have the private key corresponding to the public key set up in ~/.ssh/authorized_keys.&lt;/p&gt;

&lt;p&gt;This completes the setup for the user to authenticate exclusively via SSH key!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>terminal</category>
    </item>
  </channel>
</rss>
