Skip to content

Build natively on arm64 (Apple Silicon, Asahi)#352

Open
synt-or wants to merge 2 commits into
azimuttapp:mainfrom
synt-or:arm64-native-upstream
Open

Build natively on arm64 (Apple Silicon, Asahi)#352
synt-or wants to merge 2 commits into
azimuttapp:mainfrom
synt-or:arm64-native-upstream

Conversation

@synt-or
Copy link
Copy Markdown

@synt-or synt-or commented Apr 30, 2026

Why

The Dockerfile and docker-compose pin --platform=linux/amd64, forcing
Rosetta on Apple Silicon (slow, segfaults on mix local.hex, see #295)
and making the build unusable on Linux/aarch64 (NixOS/Asahi, where no
Rosetta exists).

Root cause: upstream Elm 0.19.1 only ships an amd64 Linux binary.
Removing the platform pin alone is not enough — the Elm download step
fails on arm64.

Approach

Replaces the official Elm binary with @lydell/elm@0.19.1-14,
an npm wrapper maintained by the author of elm-tooling that ships
native binaries for linux_arm64, darwin_arm64, linux_x64,
darwin_x64 and win32_x64 via optionalDependencies. Same Elm
0.19.1 compiler, just packaged with multi-arch support.

A pnpm.overrides entry redirects the transitively-pulled elm
package (via elm-spa, elm-test, elm-review, etc.) to the same
multi-arch package.

Changes

  • Drop --platform=linux/amd64 in both Dockerfile stages and
    docker-compose.yml.
  • Replace wget … binary-for-linux-64-bit.gz with
    npm install -g @lydell/elm@0.19.1-14.
  • Add pnpm.overrides: { "elm": "npm:@lydell/elm@0.19.1-14" } in
    package.json.
  • Add libpq-dev to the builder stage (the libpq node module's
    node-gyp build needs pg_config; on arm64 there's no prebuild).
  • Mark elm-coverage as neverBuiltDependencies: its binwrap
    installer has no arm64 binary, and the package is dev-only — never
    invoked by build:docker.
  • Regenerate pnpm-lock.yaml to reflect the override.

Relation to existing work

Refs #295, #311, #314.

This complements/replaces #314 (open since July 2024, approved but never
merged). #314 added a separate builder_arm64 stage relying on a
third-party rebuilt-Elm image (dwayne/elm-for-linux-arm64); that
image was reported broken on Linux arm64 / Asahi by @h3x4d3c1m4l in
Jan 2025. The approach here uses a single builder stage and an npm
package that is officially packaged by an Elm-tooling maintainer with
explicit support for both linux_arm64 and darwin_arm64, so it works
identically on Apple Silicon and Asahi.

Test plan

  • Native build on macOS arm64 (Docker Desktop):
    docker compose up --build produces an arm64 image; backend serves
    Azimutt on :4000; od -An -tx1 -j18 -N2 /app/erts-*/bin/beam.smp
    returns b7 00 (EM_AARCH64), confirming no Rosetta.
  • Native build on Linux aarch64 (NixOS / Asahi). Author has access
    to a tester for this and will report.
  • Native build on linux/amd64. Cross-building amd64 from an arm64
    Docker Desktop reproduces Segmentation fault on docker-compose up #295 (BEAM under Rosetta segfaults at
    mix local.hex), so the author cannot self-verify amd64 here.
    Would appreciate a maintainer running docker compose up --build
    on an amd64 host. The diff is no-op for amd64 hosts apart from
    swapping the Elm package, and @lydell/elm ships an official
    linux_x64 binary.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Docker image now supports multiple CPU architectures (no forced amd64), improving portability.
    • Build environment dependencies expanded for more reliable builds.
    • Elm tooling switched to an npm-distributed package for consistent installation.
    • Package manager configuration updated to force a specific Elm resolver and skip building a coverage tool for smoother installs.

The Dockerfile and docker-compose pinned linux/amd64, which forced
Rosetta on Apple Silicon and made the build unusable on Linux/aarch64
(e.g. NixOS on Asahi, where no Rosetta exists). Root cause: the upstream
Elm 0.19.1 release ships an amd64-only Linux binary, so even on an
arm64 host the Elm step segfaulted under emulation.

Changes:
- Drop the --platform=linux/amd64 pins in Dockerfile (both stages) and
  docker-compose.yml — the build now follows the host architecture.
- Replace the official Elm binary download with @lydell/elm@0.19.1-14,
  an npm wrapper that ships native binaries for linux_arm64,
  darwin_arm64, linux_x64, darwin_x64 (and win32_x64) via
  optionalDependencies. Adds a pnpm.overrides entry so transitively
  pulled `elm` (via elm-spa, elm-test, elm-review, etc.) resolves to
  the same multi-arch package.
- Add libpq-dev to the builder stage. The libpq node module's node-gyp
  build needs pg_config; on amd64 prebuilt binaries existed, but on
  arm64 it falls back to compile-from-source.
- Mark elm-coverage as neverBuiltDependencies. Its binwrap installer
  has no arm64 binary and the package is dev-only, never used by
  build:docker.
- Regenerate pnpm-lock.yaml so the resolution reflects the override.

This complements/replaces azimuttapp#314 (open since July 2024 with an arm64 path
relying on a third-party Elm image reported broken on Asahi in Jan 2025
by @h3x4d3c1m4l). Refs azimuttapp#295, azimuttapp#311, azimuttapp#314.

Verified on macOS arm64: native arm64 image (567MB), `docker compose up`
serves Azimutt on :4000, and the BEAM ELF e_machine is AArch64
(`od -An -tx1 -j18 -N2 /app/erts-*/bin/beam.smp` returns `b7 00`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Remove hardcoded linux/amd64 platform pins from Docker build and compose; expand builder dependencies (add ca-certificates, gnupg, libpq-dev and use apt --no-install-recommends); switch Elm installation from a downloaded linux-x64 binary to the @lydell/elm npm package and add pnpm overrides.

Changes

Cohort / File(s) Summary
Dockerfiles & Compose
Dockerfile, docker-compose.yml
Removed --platform=linux/amd64 pins; builder apt install uses --no-install-recommends and adds ca-certificates, gnupg, libpq-dev.
Elm installation & tooling
Dockerfile
Replaced direct Elm linux-x64 binary download with npm install of @lydell/elm and symlinked its elm binary to /usr/local/bin/elm.
Package manager config
package.json
Added top-level pnpm section with overrides to alias elm@lydell/elm@0.19.1-14 and neverBuiltDependencies including elm-coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through layers, light and spry,

Removed the pins that kept me shy.
Elm now rides the npm stream,
Packages snug, no brittle dream.
A jaunty hop — cross-platform sky! 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR: enabling native builds on arm64 architectures by removing platform pins and replacing the amd64-only Elm binary with a multi-architecture npm package.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Dockerfile (1)

39-40: ⚡ Quick win

Add --no-install-recommends to apt installs.

This reduces image bloat and attack surface, and it also resolves the static-analysis findings on these install steps.

Proposed Dockerfile tweak
-RUN apt-get update -y && apt-get install -y build-essential git curl wget libpq-dev && apt-get clean && rm -f /var/lib/apt/lists/*_*
+RUN apt-get update -y && apt-get install -y --no-install-recommends build-essential git curl wget libpq-dev && apt-get clean && rm -f /var/lib/apt/lists/*_*

-RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs
+RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y --no-install-recommends nodejs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 39 - 40, The two Dockerfile RUN commands installing
packages should include the --no-install-recommends flag to avoid pulling
recommended packages; update the first RUN that calls apt-get install -y
build-essential git curl wget libpq-dev and the second RUN that installs nodejs
after the NodeSource setup to use apt-get install -y --no-install-recommends so
image bloat and attack surface are reduced and static-analysis warnings are
resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Dockerfile`:
- Around line 39-40: The two Dockerfile RUN commands installing packages should
include the --no-install-recommends flag to avoid pulling recommended packages;
update the first RUN that calls apt-get install -y build-essential git curl wget
libpq-dev and the second RUN that installs nodejs after the NodeSource setup to
use apt-get install -y --no-install-recommends so image bloat and attack surface
are reduced and static-analysis warnings are resolved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 64f81dfa-d4ab-4050-b4f8-b8c16bc432c0

📥 Commits

Reviewing files that changed from the base of the PR and between 3f57300 and 27b7202.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • Dockerfile
  • docker-compose.yml
  • package.json
💤 Files with no reviewable changes (1)
  • docker-compose.yml

Per CodeRabbit review on PR azimuttapp#352 — reduces image bloat and attack
surface. Adds `ca-certificates` and `gnupg` explicitly: they were
previously pulled in via Recommends and are needed by the NodeSource
setup_20.x script that runs in the next layer (it imports a GPG key
and adds an apt repo over HTTPS).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
synt-or added a commit to synt-or/azimutt that referenced this pull request Apr 30, 2026
Per CodeRabbit review on PR azimuttapp#352 — reduces image bloat and attack
surface. Adds `ca-certificates` and `gnupg` explicitly: they were
previously pulled in via Recommends and are needed by the NodeSource
setup_20.x script that runs in the next layer (it imports a GPG key
and adds an apt repo over HTTPS).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Dockerfile (1)

39-44: ⚡ Quick win

Use --no-install-recommends on the NodeSource install too.

The first apt step already opts out of recommends, but this nodejs install still pulls the default recommendation set. That partially undoes the image-size/attack-surface reduction you just introduced.

Proposed fix
-RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs
+RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y --no-install-recommends nodejs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 39 - 44, The NodeSource installation line currently
runs "curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get
install -y nodejs" which installs recommended packages; update that RUN to pass
--no-install-recommends to the apt-get install command for nodejs and preserve
the existing cleanup (apt-get clean && rm -f /var/lib/apt/lists/*_*) so the
image keeps the reduced size/attack-surface behavior (refer to the RUN that
invokes the NodeSource script and the subsequent apt-get install -y nodejs).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Dockerfile`:
- Around line 39-44: The NodeSource installation line currently runs "curl -fsSL
https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs"
which installs recommended packages; update that RUN to pass
--no-install-recommends to the apt-get install command for nodejs and preserve
the existing cleanup (apt-get clean && rm -f /var/lib/apt/lists/*_*) so the
image keeps the reduced size/attack-surface behavior (refer to the RUN that
invokes the NodeSource script and the subsequent apt-get install -y nodejs).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 821c592d-9e32-47e3-b00b-49fde4b19deb

📥 Commits

Reviewing files that changed from the base of the PR and between 27b7202 and e0c7a98.

📒 Files selected for processing (1)
  • Dockerfile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant