In general, CGO_ENABLED=1 leads to faster, smaller builds & runtimes - as it can dynamically load the host OS's native libraries (e.g. glibc, DNS resolver etc.) - when running on the build OS. This is ideal for local rapid development. For deployment, CGO_ENABLED=1 may not be practical or even possible - when considering the deployment hosting OS.
If you're purely using the standard library, they you may not necessarily need CGO enabled. In certain standard libraries behavior will different if using a pure-Go version (CGO_ENABLED=0) or a CGO-enabled version:
- net: see DNS Name Resolution
- os/users:
CGO_ENABLED=1uses the native OS (e.g. on Linuxnsswitch) for ID lookupCGO_ENABLED=0uses a basic Go implementation (e.g. reads from/etc/passwd) - which does not include other ID registries the host may be aware of
Deployment
While a CGO_ENABLED=1 binary may be smaller in size, it relies on delivering a host OS too. Comparing Docker images:
ubuntu:20.04is 73.9MB (glibc: GNU-libc)alpine:3.12.1is 5.57MB (musllibc)
so adding an OS (even a minimal one) adds this extra baggage. alpine looks attractive in its minimal size - but its libc may not be compatible with packages that rely on glibc.
CGO_ENABLED=0 is, however, ideal for scratch docker image deployments - as no host OS needs to be bundled.
However, if your application imports a package with C-code (e.g. go-sqlite3) then your build must enable CGO.
Answer from colm.anseo on Stack OverflowWhat is the consequence of using CGO_ENABLED=0?
With CGO_ENABLED=0 you got a staticaly-linked binary (see: https://en.wikipedia.org/wiki/Static_build) so it will run without any external dependencies (you can buld your dockers from 'scratch' image) Like that: https://github.com/s0rg/microapp/blob/master/Dockerfile
More on reddit.comgo - When using CGO_ENABLED is must and what happens - Stack Overflow
What is the difference between a binary built with CGOENABLED=0 from the default mode?
make.bash: clarify that CGO_ENABLED=0 sets default into resulting compiler
Videos
Is it a bad idea to set this environment variable to 0? From what I read, setting it to 1 means there should be a gcc compiler installed in the working system. Is this a correct interpretation? Why is it 1 by default?
I was trying to containerise my go application using the below docker file:
FROM golang:latest as builder
ENV GOOS=linux
COPY ./ /go/src/hello_world
WORKDIR /go/src/hello_world
RUN go build .
FROM alpine:latest
WORKDIR /usr/home
COPY --from=builder /go/src/hello_world/hello_world /usr/home
ENTRYPOINT ["./hello_world"]
This was giving me error but based on my findings I put ENV CGO_ENABLED=0 during build and it started working fine.
When is it required to be set to 1 and why did I have to explicitly set to 0 in my case?
P.S. I am very new to go so any resources on this would be appreciated.
With CGO_ENABLED=0 you got a staticaly-linked binary (see: https://en.wikipedia.org/wiki/Static_build) so it will run without any external dependencies (you can buld your dockers from 'scratch' image) Like that: https://github.com/s0rg/microapp/blob/master/Dockerfile
From what I read, setting it to 1 means there should be a gcc compiler installed in the working system. Is this a correct interpretation? Why is it 1 by default?
It doesn't actually mean you must have a gcc compiler available. It means a couple things. Some parts of the runtime like the dns resolver and user lookups use glibc so it will link against those if you use certain stdlib packages. There are some pure Go replacements that swap in if you disable cgo. But if you use any other 3rd party packages that do have cgo bindings to C then it would be expected that you have a compiler when you build. But you would find that out pretty quick if you disabled cgo and your project could no longer build those dependencies .
Many things are only available as C libraries, and re-implementing that all in Go would be costly. cgo has its downsides, but it can be a good trade-off. Even the standard library uses it (net for DNS lookups, os/user for user lookups) because it doesn't re-implement 100% of the behaviour in Go.
Cross-compiling C code is still rather hard; you'll need the target architecture's C compiler and toolchain (e.g. CC=aarch64-linux-musl-gccgo build to build an arm64 binary). None of that is installed by default so for most people cgo simply won't work when cross-compiling; they need to take manual steps to set it up first.
cgo often isn't strictly required (like in the net and os/user packages), so disabling it by default seems the most user-friendly option.
But there are no such constraints on the native platform, and it's expected to work by default without any user setup; so why not enable it by default?
If you're running on an Alpine image, it is impossible to compile and run Go programs in Alpine images right away. You must disable CGO by setting the environment variable CGO_ENABLED to false (the default value is true).
You can do this either by:
- Adding
go env -w CGO_ENABLED=0like Robert mentions in his comment or, - Setting the env value prefixing it in the
gocommand, e.g.:CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags '-s -w' -tags lambda.norpc -o bin/<YOUR_FUNC>/bootstrap <YOUR_PATH>/main.go
See: https://megamorf.gitlab.io/2019/09/08/alpine-go-builds-with-cgo-enabled/