# GHC + Alpine + Docker + static linking \< 15 MB

**URL:** <https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332>\
**Category:** Show and Tell\
**Created:** [February 2, 2025, 10:55pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332 "2025-02-02T22:55:44Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 2, 2025, 10:55pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/1 "2025-02-02T22:55:44Z")

</div>

Hi all!

> **[GitHub - ocramz/alpine-ghc-static](https://github.com/ocramz/alpine-ghc-static)**
>
> Contribute to ocramz/alpine-ghc-static development by creating an account on GitHub.

I recently had to revisit this way of deploying binaries and was pleasantly surprised ! 15 MB for a completely portable Hello world Haskell container, building in 30 seconds.

Golfed down the setup to a reasonable few lines (Dockerfile, build.sh and GH Actions file) for your reading pleasure.

Enjoy!

---

<div class="post-metadata">

**Author:** ![MangoIV](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mangoiv/32/3519_2.png) [@MangoIV](https://discourse.haskell.org/u/MangoIV)\
**Post date:** [February 3, 2025, 3:59pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/2 "2025-02-03T15:59:11Z")

</div>

You can get down the binary size _substantially_ by using upx (thanks Hecaté for the tip). Cabal-audit static binaries weigh 4Mb (the build takes longer than 30 seconds though ;))

> <https://github.com/MangoIV/cabal-audit/blob/main/nix/static.nix>

---

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 4, 2025, 4:53am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/3 "2025-02-04T04:53:09Z")

</div>

Great, I had forgotten about UPX. In my PoC above I just `strip` the binary. Thanks!

---

<div class="post-metadata">

**Author:** ![janus](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/janus/32/1060_2.png) [@janus](https://discourse.haskell.org/u/janus)\
**Post date:** [February 4, 2025, 6:47am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/4 "2025-02-04T06:47:35Z")

</div>

First off, thank you for your educational post.

> [@ocramz](#):
>
> completely portable

What does this mean? Docker is just a wrapper for various Linux container features, right?

There is no portability across CPU architectures, and there doesn’t seem to be a stable image format either.

For example

```haskell
$ docker run -it gcc:5.1 /bin/bash
docker: [DEPRECATION NOTICE] 
Docker Image Format v1 and 
Docker Image manifest version 2, schema 1 support
is disabled by default and will be 
removed in an upcoming release. 
Suggest the author of docker.io/library/gcc:5.1 to upgrade the image to the OCI Format or Docker Image manifest v2, schema 2.
More information at https://docs.docker.com/go/deprecated-image-specs/.

```

(`gcc:5.2` works, for comparison)

You can’t run old CentOS images either, without adjusting your host kernel flags and adding `vsyscall=emulate`, whatever that means. See [Bash in CentOS 5\* and 6\* images crashes if Docker host system is “too new”](https://github.com/CentOS/sig-cloud-instance-images/issues/62).

I find reproducibility more interesting than portability. If I can’t reproduce the binary (e.g. because the apt mirrors went down), I can’t port it to a new architecture either.

In my experience, the Docker ecosystem is extremely vulnerable to these kinds of problems, since there is no restriction on network access in containers.

inb4 nix

---

<div class="post-metadata">

**Author:** ![Kleidukos](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kleidukos/32/1213_2.png) [@Kleidukos](https://discourse.haskell.org/u/Kleidukos)\
**Post date:** [February 4, 2025, 10:58am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/5 "2025-02-04T10:58:28Z")

</div>

Cheers, always a pleasure. 🙂  
For others: Note that UPX creates “shims” that make it look like the binary is static, but actually links will be resolved at run-time (IIRC?), so this does not spare you from having to have a fully-static binary **first** and then use UPX on it.

---

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 4, 2025, 4:11pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/6 "2025-02-04T16:11:29Z")

</div>

The plot thickens! apparently GHC doensn’t like to statically link libraries that contain TH. Will investigate…

---

<div class="post-metadata">

**Author:** ![MangoIV](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mangoiv/32/3519_2.png) [@MangoIV](https://discourse.haskell.org/u/MangoIV)\
**Post date:** [February 4, 2025, 4:52pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/7 "2025-02-04T16:52:54Z")

</div>

You’re out of luck with any GHC built by Hadrian afaiu

(That’s why the ghc version in nixpkgs’ pkgsStatic is pinned to the last version that is still built with make)

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [February 4, 2025, 5:33pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/8 "2025-02-04T17:33:25Z")

</div>

Do you have a commit where you are encountering this issue?

---

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 5, 2025, 7:01am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/9 "2025-02-05T07:01:04Z")

</div>

Hi, unfortunately not as this shows up in a proprietary library.

I tried this approach as suggested by Ben : [#20168: Can't build statically linked executable when Template Haskell is used · Issues · Glasgow Haskell Compiler / GHC · GitLab](https://gitlab.haskell.org/ghc/ghc/-/issues/20168) (starting from a bindist, configure with DYNAMIC\_GHC\_PROGRAMS=NO etc.) but GHC on Alpine complains about missing linker symbols and I haven’t managed to pin down the issue. I get errors similar to this one: [#24044: GHC 9.6.3: Linux (x86-64): Alpine (Static, GMP bignum implementation): GHCi: unknown symbol `\_\_gmpn\_rshift' · Issues · Glasgow Haskell Compiler / GHC · GitLab](https://gitlab.haskell.org/ghc/ghc/-/issues/24044)

There is a [proposal](https://gitlab.haskell.org/ghc/ghc/-/issues/19770) to validate statically-built GHC in CI which I think would be useful, especially if the validation is done together with actual Hackage libraries.

---

<div class="post-metadata">

**Author:** ![OrenGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/orengithub/32/4495_2.png) [@OrenGitHub](https://discourse.haskell.org/u/OrenGitHub)\
**Post date:** [February 7, 2025, 8:01am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/11 "2025-02-07T08:01:49Z")

</div>

Very Nice !  
I wonder what would be a minimal size for a Yesod web server -  
Currently, in my [open source SAST engine](https://github.com/OrenGitHub/dhscanner) I copy the binary generated by [cabal](https://github.com/OrenGitHub/dhscanner.1.parsers/blob/main/dhscanner.cabal) to a Ubuntu image, resulting in images around 140M - it is dynamically linked so I hesitated using alpine for the libc / muslc issues

---

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 7, 2025, 8:33am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/12 "2025-02-07T08:33:49Z")

</div>

musl is perfectly fine for dynamic linking! And if you build on top of Alpine, the base image will be much smaller than a Debian.

---

<div class="post-metadata">

**Author:** ![ocramz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ocramz/32/3713_2.png) [@ocramz](https://discourse.haskell.org/u/ocramz)\
**Post date:** [February 7, 2025, 9:55am UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/13 "2025-02-07T09:55:33Z")

</div>

> [@MangoIV](#):
>
> out of luck with any GHC built by Hadrian afaiu

Could you please elaborate on this? I really don’t see the connection.

---

<div class="post-metadata">

**Author:** ![MangoIV](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mangoiv/32/3519_2.png) [@MangoIV](https://discourse.haskell.org/u/MangoIV)\
**Post date:** [February 7, 2025, 12:13pm UTC](https://discourse.haskell.org/t/ghc-alpine-docker-static-linking-15-mb/11332/14 "2025-02-07T12:13:49Z")

</div>

I don’t know all the details but this is the issue I could find. Maybe it also applies to you.

> <https://github.com/NixOS/nixpkgs/issues/275304>
>
> \### Describe the bug
> Haskell packages in \`nixpkgs.pkgsStatic.haskell.packages.g…hc98\` are unable to be built.
> 
> \### Steps To Reproduce
> Steps to reproduce the behavior:
> 1. \`nix build nixpkgs#legacyPackages.x86\_64-linux.pkgsStatic.haskell.packages.ghc98.Diff\`
> 
> \### Expected behavior
> \`Diff\` is built, linking against \`musl\`.
> 
> 
> \### Observed behavior
> 
> \`\`\`
> nix-repl\> :b legacyPackages.x86\_64-linux.pkgsStatic.haskell.packages.ghc98.Diff error: build of '/nix/store/ickzn6az4asiwrh1wq20blm0dvcr5w17-Diff-static-x86\_64-unknown-linux-musl-0.4.1.drv' on 'ssh://ben@maurer.local' failed: builder for '/nix/store/ickzn6az4asiwrh1wq20blm0dvcr5w17-Diff-static-x86\_64-unknown-linux-musl-0.4.1.drv' failed with exit code 1;
> last 10 log lines:
> \> | ^^^^^^^^^^^^^^^^^^^^^^^^^^
> \>
> \> src/Data/Algorithm/Diff.hs:29:1: error: \[GHC-47808\]
> \> Failed to load dynamic interface file for Data.Array:
> \> Exception when reading interface file /nix/store/zb7g1q1vza1x0fmb8qk8cv9y23b9w81g-x86\_64-unknown-linux-musl-ghc-9.8.1/lib/x86\_64-unknown-linux-musl-ghc-9.8.1/lib/../lib/x86\_64-linux-ghc-9.8.1/array-0.5.6.0-inplace/Data/Array.dyn\_hi
> \> /nix/store/zb7g1q1vza1x0fmb8qk8cv9y23b9w81g-x86\_64-unknown-linux-musl-ghc-9.8.1/lib/x86\_64-unknown-linux-musl-ghc-9.8.1/lib/../lib/x86\_64-linux-ghc-9.8.1/array-0.5.6.0-inplace/Data/Array.dyn\_hi: withBinaryFile: does not exist (No such file or directory)
> \> |
> \> 29 | import Data.Array (listArray, (!))
> \> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> \> load' failed
> For full logs, run 'nix log /nix/store/ickzn6az4asiwrh1wq20blm0dvcr5w17-Diff-static-x86\_64-unknown-linux-musl-0.4.1.drv'.
> error: builder for '/nix/store/ickzn6az4asiwrh1wq20blm0dvcr5w17-Diff-static-x86\_64-unknown-linux-musl-0.4.1.drv' failed with exit code 1;
> last 10 log lines:
> \> | ^^^^^^^^^^^^^^^^^^^^^^^^^^
> \>
> \> src/Data/Algorithm/Diff.hs:29:1: error: \[GHC-47808\]
> \> Failed to load dynamic interface file for Data.Array:
> \> Exception when reading interface file /nix/store/zb7g1q1vza1x0fmb8qk8cv9y23b9w81g-x86\_64-unknown-linux-musl-ghc-9.8.1/lib/x86\_64-unknown-linux-musl-ghc-9.8.1/lib/../lib/x86\_64-linux-ghc-9.8.1/array-0.5.6.0-inplace/Data/Array.dyn\_hi
> \> /nix/store/zb7g1q1vza1x0fmb8qk8cv9y23b9w81g-x86\_64-unknown-linux-musl-ghc-9.8.1/lib/x86\_64-unknown-linux-musl-ghc-9.8.1/lib/../lib/x86\_64-linux-ghc-9.8.1/array-0.5.6.0-inplace/Data/Array.dyn\_hi: withBinaryFile: does not exist (No such file or directory)
> \> |
> \> 29 | import Data.Array (listArray, (!))
> \> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> \> load' failed
> For full logs, run 'nix log /nix/store/ickzn6az4asiwrh1wq20blm0dvcr5w17-Diff-static-x86\_64-unknown-linux-musl-0.4.1.drv'.
> 
> \`\`\`
> 
> \### Additional context
> 
> The problem here appears to manifest during building of Haddock documentation. For instance,
> \`\`\`
> $ nix repl
> nix-repl\> :lf nixpkgs#
> nix-repl\> :b legacyPackages.x86\_64-linux.haskell.lib.dontHaddock (legacyPackages.x86\_64-linux.pkgsStatic.haskell.packages.ghc98.Diff)
> 
> This derivation produced the following outputs:
> out -\> /nix/store/5096rq1664b9qq2f2g9ih0sv6is48d8z-Diff-static-x86\_64-unknown-linux-musl-0.4.1
> 
> \`\`\`
> 
> \### Notify maintainers
> 
> @nh2
