Skip to content

Node should run on Debian/Ubuntu-based images without having to manually install libatomic first #60790

Description

@1dEraNCeSIv0

What is the problem this feature will solve?

Summary

Node 25.0.0 requires the libatomic library, it's in the docs since 25.2.0.

I've tried some current official Ubuntu images (24.04, 25.10, 26.04) and Debian images (12.12, 13.12) and none comes with libatomic installed.

Consequently installing node on Debian or Ubuntu will work, but running any commands will fail.

Why should this be supported

I assume that having a seamless installation experience, at least on major linux distros, is something Node would like to provide. I'm thinking a) install nvm, b) install node via nvm, c) done, you can use node now.

This is also how, for example, the npm docs tell you to install npm. And it no longer works on Debian or Ubuntu based images unless you also install "libatomic".

Debian and Ubuntu are two of the major linux distros. Many projects are using one of their images as the basis for their official docker images. For example, the official Jenkins image is debian based and the latest version (jenkins/jenkins:2.538-jdk21) can not run node 25+, unless you first install "libatomic" into the container that is.

What is the feature you are proposing to solve the problem?

I am not familiar enough with linux or node to tell whether or not node could include libatomic. But if that was possible and practical then that would allow debian and ubuntu based images to run node again without having to install libatomic first.

What alternatives have you considered?

  1. nvm could plausibly install libatomic, that said, over at nvm the sentiment was that Node was the place to address this.
  2. debian / ubuntu could plausibly include libatomic in their images
  3. all projects that provide debian / ubuntu based images and imagine their image could be used to run node could plausibly provide libatomic
  4. the end user that wants to run node in any debian / ubuntu based image could plausibly install libatomic

Out of these I've only really considered 1 and 4 real possibilities, though with the folks at nvm saying it's not their job it's either on Node itself or the end user, probably.

Activity

  1. richardlau commented on Nov 20, 2025

    @richardlau
    Member

    Ref: #37219 (comment)

    Upstream V8 avoids this by providing a custom libcxx which includes atomic functions. There is reference in an upstream V8 issue with a desire to retire the option to not use the custom libcxx (which Node.js is currently using), so we may need to switch eventually.

  2. EricMCornelius commented on Dec 5, 2025

    @EricMCornelius

    This will absolutely break reasonable assumptions for single-executable-application deployments using the official node runtime binaries if not rectified.

  3. linghengqian commented on Jan 2, 2026

    @linghengqian

    Should the documentation of NodeJS mention somewhere that in common Linux distributions, what command should be used to install libatomic.so.1? In the amd64 architecture of Ubuntu 24.04, there are 57 system dependencies that contain this file. For reference, see https://packages.ubuntu.com/search?suite=noble&arch=amd64&searchon=contents&keywords=libatomic.so.1 .

    The current error logs of node --version do not show this.

    node: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory
  4. polarathene commented on Feb 19, 2026

    @polarathene

    I am not familiar enough with linux or node to tell whether or not node could include libatomic.
    But if that was possible and practical then that would allow debian and ubuntu based images to run node again without having to install libatomic first.

    TL;DR: If building Node with zig cc/zig c++ this should remove the dependency IIRC, that compiler specifically special cases libatomic regardless of static/shared link request, as it builds it's own on-demand for the build target and bundles that in statically AFAIK.

    A similar dependency issue is with python-build-standalone project's Python distribution, although they statically link libatomic into their binary instead of a dynamic link. The problem over there is some other project's like Nuitka read in build metadata to link the dep, even when it shouldn't be necessary as it was a static dep, not dynamic. That project has built their binaries for distribution on rather old build environments where libatomic was needed for a dependency, and discusses if that's really still necessary.

    libatomic is it needed for modern hardware? If so how feasible is static linking?

    I'm not too familiar with using libatomic myself. I have heard that there might be a concern with not linking to a shared library at runtime? But that might depend on what else is running?:

    SchrodingerZhu/snmalloc-rs#187 (comment)

    I remember snmalloc emits references to certain __atomic* symbols.
    Many platforms does not ship a static version of libatomic due to its nature (there is some datum inside libatomic that ought to be shared by processes).

    Apparently it's also not likely to be needed on modern systems, but rather for hardware/architectures well over a decade old?

    SchrodingerZhu/snmalloc-rs#187 (comment)

    Ideally, I don't think we should even link against it if users know that their program is to run only on modern enough hardware.

    libatomic is a portability support library.
    The compiler emits function call to symbols inside libatomic instead of raw atomic instruction to make sure that the produced binary can be run with different hardware capabilities.

    Clearly that's more of a difficult call for NodeJS, but such platforms could perhaps have alternative legacy build support? 🤷‍♂️


    Zig via zig cc/zig c++ could solve this?

    With Zig building NodeJS you can avoid the hassle of building in a rather dated environment to reduce glibc (I see it's targeting glibc 2.28 on the copy I have installed from proto CLI).

    Zig can specify a glibc version in it's -gnu targets, whilst also providing the convenience of omitting libatomic and libgcc_s libraries (if you raise the glibc target up to 2.34 from around Aug 2021, you'd additionally have other glibc related libraries omitted which isn't zig specific but a change from glibc itself).

    This Dec 2024 commit for Zig 0.14.0 onwards documents how Zig is handling libatomic:

    compiler: Classify libatomic as an alias for compiler-rt.

    This is a library that ships with GCC and provides fallback implementations of atomic intrinsics where necessary.
    Since we do the same in our compiler-rt implementation, and since some build systems insist on passing -latomic even for Clang (which zig cc masquerades as), just satisfy this dependency by way of compiler-rt.

    Which implies Clang doesn't need this either? I haven't checked, it may be an opt-in feature there for compiler-rt?

    The associated Zig issue that was resolved by that referenced commit:

    It makes sense for us to recognize and treat libatomic specially as it's a compiler-provided library.
    Our compiler-rt implementation should be able to satisfy it

    Related reference that covers gcc libs handled by Zig too:

    I'm pretty sure we can (and should) satisfy libgcc_eh by linking Zig's bundled libunwind, just as we do for libgcc_s.

  5. linghengqian commented on Feb 19, 2026

    @linghengqian

    I personally think the current issue is actually a duplicate of nodejs/build#4091 . Will this bug automatically disappear once all builds are migrated to gcc 13.1+? I don't believe that switching to zig cc/zig c++ halfway through the Clang migration will enable support for platforms that clang doesn't support.

  6. polarathene commented on Feb 20, 2026

    @polarathene

    I personally think the current issue is actually a duplicate of nodejs/build#4091 .

    I don't see how that's related? There isn't a discussion there about libatomic? Could you please clarify?

    Your prior comment references Ubuntu and a package to install, but I don't think you're familiar with the convention (see the Fedora example below), you just need apt-get install libatomic1 in your runtime environment which will resolve to the correct package for your architecture.


    I don't believe that switching to zig cc/zig c++ halfway through the Clang migration will enable support for platforms that clang doesn't support

    I've not looked into if Clang has the same behaviour as Zig to avoid an explicit libatomic link when it's not necessary for the platform target, is that something you can confirm?

    EDIT: I'm pretty sure you still have to explicitly link by default for Clang, and depending on distro, like with GCC you'd need libatomic.a present which may need a separate package installed.

    Will this bug automatically disappear once all builds are migrated to gcc 13.1+?

    Is that a release that does the same as I described with Zig but with GCC? Nope.

    EDIT: See my next comment, I cover both Ubuntu and Fedora since they differ a tad. Short version is I don't think GCC or Clang provide the same convenience I described that Zig offers for libatomic.

  7. polarathene commented on Feb 20, 2026

    @polarathene

    I just checked in Fedora and it has GCC 15 with only a libatomic.so bundled (as a linker script) for linking to /usr/lib64/libatomic.so.1 (the runtime dep linked based on DT_SONAME value, which should resolve a symlink to the full versioned lib in a runtime environment).

    • To get the libatomic.a you'd need to install libatomic-static. Ideal as drops the runtime requirement.
    • To get the libatomic.so.1 you'd need to install libatomic. This the current situation users have an issue with.

    Here's an example for Fedora

    # Reproduction environment via a Fedora 43 container with GCC installed:
    $ docker run --rm -it fedora:43
    $ dnf instally -yq gcc
    
    # 32-bit is available:
    $ ls /usr/lib/gcc/x86_64-redhat-linux/15/32/libatomic*
    /usr/lib/gcc/x86_64-redhat-linux/15/32/libatomic.a  /usr/lib/gcc/x86_64-redhat-linux/15/32/libatomic.so
    
    # Otherwise only a linker script stub:
    $ ls /usr/lib/gcc/x86_64-redhat-linux/15/libatomic*
    /usr/lib/gcc/x86_64-redhat-linux/15/libatomic.so
    
    $ cat /usr/lib/gcc/x86_64-redhat-linux/15/libatomic*
    INPUT ( /usr/lib64/libatomic.so.1.2.0 )
    
    $ ls /usr/lib64/libatomic.so.1.2.0
    ls: cannot access '/usr/lib64/libatomic.so.1.2.0': No such file or directory
    
    # Dynamic link:
    $ dnf provides '*/libatomic.so*'
    libatomic-15.2.1-2.fc43.x86_64 : The GNU Atomic library
    Repo         : updates-testing
    Matched From :
    Filename     : /usr/lib64/libatomic.so.1
    Filename     : /usr/lib64/libatomic.so.1.2.0
    
    # Static link:
    $ dnf provides '*/libatomic.a'
    
    libatomic-static-15.2.1-7.fc43.x86_64 : The GNU Atomic static library
    Repo         : updates
    Matched From :
    Filename     : /usr/lib/gcc/x86_64-redhat-linux/15/libatomic.a

    Clang (at least in Fedora) is configured to include the GCC paths, such that it gets the libatomic.a available in the same manner as GCC.

    So I don't think anything has changed there from what I've said in my previous comment?

    • Whereas if you dnf install -yq zig you do not need the separate libatomic/libatomic-static library and can use zig cc or zig c++ as the compiler and it'll handle the libatomic for you, no need to -l libatomic either (although AFAIK that'd be expected for GCC/Clang).
    • This was the point I was trying to make, that it was simpler for a build environment to use zig, but explicitly static linking libatomic with GCC/Clang with a separate package works too.

    For reference, here is GCC 13 on Ubuntu 24.04 with only the libgcc-13-dev package (dep of gcc package) installed, which includes the mentioned libatomic1 runtime package and libatomic.a (_as part of libgcc-13-dev):

    $ docker run --rm -it ubuntu:24.04
    $ apt-get -qq update && apt-get -qq install libgcc-13-dev
    $ ls -l /usr/lib/gcc/x86_64-linux-gnu/13/libatomic*
    -rw-r--r-- 1 root root 139488 Dec 18 20:59 /usr/lib/gcc/x86_64-linux-gnu/13/libatomic.a
    lrwxrwxrwx 1 root root     40 Dec 18 20:59 /usr/lib/gcc/x86_64-linux-gnu/13/libatomic.so -> ../../../x86_64-linux-gnu/libatomic.so.1
    
    # Symlink of a symlink (not a linker script like on Fedora):
    $ readlink --canonicalize /usr/lib/gcc/x86_64-linux-gnu/13/libatomic.so
    /usr/lib/x86_64-linux-gnu/libatomic.so.1.2.0
    
    # View the `DT_SONAME` that will get linked and required at runtime (`libatomic.so.1`)
    # `libatomic.so` is only relevant for link time during builds (but some devs get confused and think it's required for runtime)
    $ apt-get -qq install patchelf
    $ patchelf --print-soname /usr/lib/gcc/x86_64-linux-gnu/13/libatomic.so
    libatomic.so.1

    There's nothing special about GCC 13 in Ubuntu there, go back to Ubuntu 20.04 with GCC 9 and it's the same.

    So in a build environment you'd have libatomic.a already with the gcc package installed. You just need to indicate to static link that libatomic.a as a preference over libatomic.so (dynamic is preferred by default when available), so that'd be handled by either:

    • Direct linker flags: -Bstatic -l atomic -Bdynamic
    • Linker flags for the compiler to pass on: -Wl,-Bstatic -l atomic -Wl,-Bdynamic (there is also -l:libatomic.a as an alternative but Zig's C/C++ linker isn't compatible with it)

    The link you provided talked about RHEL, so I'd assume it's similar to Fedora more than Ubuntu, but I've not looked. Most likely scenario is the dependency is just -l atomic instead of advised above to ensure the static lib is linked instead.

    Hopefully that better clarifies why I suggested Zig.

    • With Zig you don't have to think about your distro as much, what version of glibc your build environment is linking, or the libatomic linking issues above.
    • Zig will sort you out nicely there as it's a self-contained toolchain. It does have 100% parity in linker option support though, I have had some software fail to link as a result but that's generally rare.
    • I ran into an libatomic issue with python-build-standalone + Nuitka + Zig due to -l:libatomic.a, so this topic is quite familiar for me 😅
  8. added
    v26.xIssues that can be reproduced on v26.x or PRs targeting the v26.x-staging branch.
    and removed on May 1, 2026
  9. alexsch01 commented on May 10, 2026

    @alexsch01
    Contributor

    This would be a blocker for v26 LTS, right?

  10. linghengqian commented on May 10, 2026

    @linghengqian

    This would be a blocker for v26 LTS, right?

    I don't think this will hinder the release of the new LTS version, because I don't even know which git to put the PR that solves this problem into.

  11. alexsch01 commented on May 14, 2026

    @alexsch01
    Contributor

    I made PR: #63270
    it's more of a POC with compiling with clang on Linux

  12. laverdet commented on Jun 15, 2026

    @laverdet
    Contributor

    I'm a bit confused by this one. I'm seeing on the v8 lists that the node team is still supporting gcc compilation, since the v8 team officially dropped support for it. But it looks like the distributed nodejs binaries are now being built with clang.

    There is a long-standing issue in clang where std::atomic generates runtime calls under libstdc++. gcc doesn't emit the same runtime calls.

    root[15:09:39] [/workspace] $ clang++ --version
    Debian clang version 22.1.4 (1)
    Target: aarch64-unknown-linux-gnu
    Thread model: posix
    InstalledDir: /usr/lib/llvm-22/bin
    
    root[15:11:14] [/workspace] $ cat a.cc 
    #include <atomic>
    struct foo_t { uint32_t left, right; };
    int main() {
      std::atomic<foo_t> foo;
      foo_t one, two;
      foo.compare_exchange_strong(one, two);
    }
    
    root[15:11:18] [/workspace] $ clang++ -stdlib=libc++ a.cc
    root[15:11:21] [/workspace] $ clang++ -stdlib=libstdc++ a.cc
    /usr/bin/aarch64-linux-gnu-ld.bfd: /tmp/a-e6eab6.o: in function `std::atomic<foo_t>::compare_exchange_strong(foo_t&, foo_t, std::memory_order, std::memory_order)':
    a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x204): undefined reference to `__atomic_compare_exchange'
    /usr/bin/aarch64-linux-gnu-ld.bfd: a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x24c): undefined reference to `__atomic_compare_exchange'
    /usr/bin/aarch64-linux-gnu-ld.bfd: a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x294): undefined reference to `__atomic_compare_exchange'
    /usr/bin/aarch64-linux-gnu-ld.bfd: a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x334): undefined reference to `__atomic_compare_exchange'
    /usr/bin/aarch64-linux-gnu-ld.bfd: a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x37c): undefined reference to `__atomic_compare_exchange'
    /usr/bin/aarch64-linux-gnu-ld.bfd: /tmp/a-e6eab6.o:a.cc:(.text._ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_[_ZNSt6atomicI5foo_tE23compare_exchange_strongERS0_S0_St12memory_orderS3_]+0x3c4): more undefined references to `__atomic_compare_exchange' follow
    clang++: error: linker command failed with exit code 1 (use -v to see invocation)
    

    You can get around it by statically linking against libatomic but the intrinsics that gcc emits are better. I've just been using gcc to avoid the runtime calls.

  13. alexsch01 commented on Aug 27, 2026

    @alexsch01
    Contributor

    Any update on this? Are there plans to have a "custom libcxx which includes atomic functions"

  14. aral commented on Sep 12, 2026

    @aral

    This basically means that the standalone Node binaries are broken on major Linux distributions and this PR fixes it:

    #63270

    This should really be implemented before 26 hits LTS.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.v26.xIssues that can be reproduced on v26.x or PRs targeting the v26.x-staging branch.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions