nvm icon indicating copy to clipboard operation
nvm copied to clipboard

Installing on Alpine Linux

Open neilstuartcraig opened this issue 8 years ago • 27 comments

Hi

First off, thanks for NVM - I use it frequently and see it being used everywhere and it's been great. To that end, I'd really like to use NVM on my current project which is basically a CI running via Docker - i'd like to be able to use Alpine Linux as the base image.

I can get NVM to install on Alpine by doing:

apk add bash
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash

Which is great. But then the resulting Node binary won't run and I believe from doing a little reading that this is likely to be because Alpine needs musl rather than GCC built binaries.

I tried using the -s flag in nvm install but that tells me it is not usable for Node > 1.

I see Alpine and other busybox-based distros gaining a lot of traction as they're so fast, perfect for containerised use - hence my interest.

So my question is...Do you think the above is correct and if so (or if similar), can I offer help in perhaps providing Alpine-compatible binaries? I can perhaps script a compilation of all the existing versions of Node source - though it'd take some time to run - then upload them somewhere e.g. S3.

If the above is of any interest, please let me know as I am keen to help wherever necessary.

Cheers Neil

neilstuartcraig avatar May 25 '16 08:05 neilstuartcraig

I do think the above is correct.

To provide Alpine-compatible binaries, node itself would have to provide them - nvm simply installs whatever is available on nodejs.org. I think it would be great to add alpine binaries to the list of build targets that node core produces - filing an issue on https://github.com/nodejs/node would be the right place for that.

ljharb avatar May 25 '16 08:05 ljharb

Ah, I see...I thought NVM maintained a separate repo. I'll file a bug with the Node project.

Would NVM need any changes to download the correct binary for Alpine and other busybox-based OS's?

Just for note - nvm install -s 0.12 worked on Alpine after doing the above.

Thanks for the quick response!

neilstuartcraig avatar May 25 '16 08:05 neilstuartcraig

Any changes nvm would need would be minimal - solely grabbing the right OS and architecture strings, based on what node chose.

It seems like when nvm supports installing node from source >= v1.0, it will also work on alpine, and I'm continuing to work on that.

ljharb avatar May 25 '16 08:05 ljharb

OK cool, thanks for that - seems like a reasonably easy win then. I have filed https://github.com/nodejs/node/issues/6965.

For speed, binaries would be best of course but building from source would be helpful as a fallback.

Thanks again!

neilstuartcraig avatar May 25 '16 08:05 neilstuartcraig

Sorry, also just thought...are you interested in adding some info to either a separate doc or the main readme for Alpine/busybox users? How to get NVM working on these is fresh in my memory and I think it'd be useful to note how to get it installed and what the current limitations are (and why) on Alpine/busybox would be useful - it took me an hour or two of reading and trying things so maybe it might save other folks some time which is always nice :-)

neilstuartcraig avatar May 25 '16 09:05 neilstuartcraig

If it's more than a few sentences, I'd love to see a draft first, otherwise a PR to the main readme is great.

ljharb avatar May 25 '16 09:05 ljharb

OK cool - i'll draft something over the next day or two and post it here - i'll keep it as short as possible.

neilstuartcraig avatar May 25 '16 09:05 neilstuartcraig

Hi again @ljharb

Here's my draft on the above - please let me know what you think and/or whether you want me to PR it in some fashion (add to readme.md or a new doc - i'm thinking new doc might be best as it's probably only interesting to a minority). I won't be offended if you think this is complete rubbish so please let me know your honest thoughts 😼.


###Installing NVM on Alpine Linux In order to provide the best performance (and other optimisations), NVM will download and install pre-compiled binaries for Node (and NPM) when you run nvm install X. The Node project compiles, tests and hosts/provides pre-these compiled binaries which are built for mainstream/traditional Linux distributions (such as Debian, Ubuntu, CentOS, RedHat et al).

Alpine Linux, unlike mainstream/traditional Linux distributions, is based on busybox, a very compact (~5MB) Linux distribution. Busybox (and thus Alpine Linux) uses a different C/C++ stack to most mainstream/traditional Linux distributions - musl. This makes binary programs built for such mainstream/traditional incompatible with Alpine Linux, thus we cannot simply nvm install X on Alpine Linux and expect the downloaded binary to run correctly - you'll likely see "...does not exist" errors if you try that.

There is a -s flag for nvm install which requests NVM download Node source and compile it locally but currently (May 2016), this is not available for Node versions newer than v0.10 so unless you need an older Node version, this won't help you. Work is in progress on source-builds for newer Node versions but is not yet complete.

If installing NVM on Alpine Linux is still what you want or need to do, you should be able to achieve this by running the following from you Alpine Linux shell:

apk add bash
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | /bin/bash

The Node project has some desire but no concrete plans (due to the overheads of building, testing and support) to offer Alpine-compatible binaries.

As a potential alternative, Michael Hart (a Node contributor) has some Docker images for Alpine Linux with Node and optionally, NPM, pre-installed.


Cheers Neil

neilstuartcraig avatar May 27 '16 13:05 neilstuartcraig

Thanks, this looks great! I guess this can go as a section in the readme between "Compatibility issues" and "Problems"?

ljharb avatar May 30 '16 17:05 ljharb

Great. I'll send a PR as soon as I get a moment. Cheers

neilstuartcraig avatar May 31 '16 07:05 neilstuartcraig

Could you help me understand the status of this? I came with this intent of opening a separate issue, but while looking through existing issues first, this one surprised me. I seem to have no trouble installing nvm and running nvm install in a directory that contains an .nvmrc. Is this not expected to work?

It did take me a bit to get it working, but because I was not installing the nodejs package for a system level node to be available. This was causing me to run into the following error: nvm is not compatible with the npm config "prefix" option: currently set to "". Once I install the nodejs package, things work fine.

Since this is is for an alpine docker image for a Jenkins agent, I was hoping to skip the need for a system node since I was hoping nvm by itself could handle making node and npm available for the build.

Should a system node really be necessary? If not, I'm happy to open a separate issue to track that separately.

travi avatar Oct 02 '16 03:10 travi

@travi you shouldn't need the "nodejs" package for anything, nor a system node. The prefix error is likely that you have a "prefix" line in ~/.npmrc, or an env variable setting the prefix - that should be removed. A separate issue would be great!

ljharb avatar Oct 02 '16 04:10 ljharb

Ok, thanks. That at least confirms my thought that a system node should be unnecessary.

This is a clean docker image with no .npmrc outside of the project being built (with only save-exact=true as content) and no env vars related to this.

I'll get this broken into a separate issue.

travi avatar Oct 02 '16 04:10 travi

I have 3 potential options that enable using nvm on alpine with precompiled binaries in the node unofficial-builds project. https://unofficial-builds.nodejs.org/ <-- these are used in the official node:alpine docker images currently.

Option 1

I have a branch I can submit for a pull request for just readme updates https://github.com/bpmccurdy/nvm/tree/alpine-readme here are the updates:

Installing nvm on Alpine Linux

Alpine Linux, unlike mainstream/traditional Linux distributions, is based on BusyBox, a very compact (~5MB) Linux distribution. BusyBox (and thus Alpine Linux) uses a different C/C++ stack to most mainstream/traditional Linux distributions - musl. There currently is no musl based binary published in the nodejs official builds but they do publish a musl based binary in the nodejs unofficial builds which they use in the node:alpine docker image. The node:alpine docker image is a potential alternative to nvm for using node on alpine linux.

For now you can override the nvm_get_arch function to return x64-musl on x64 Alpine Distributions. Currently the Node project only has unofficial builds for x64-musl. Sorry no ARM-musl/x86-musl,etc builds for now. The Node project has some desire but no concrete plans (due to the overheads of building, testing and support) to offer Official Alpine-compatible binaries.

For more info about unofficial builds visit: https://unofficial-builds.nodejs.org/

If installing nvm on Alpine Linux is still what you want or need to do, you should be able to achieve this by running one of the following from your Alpine Linux shell:

Using Precompiled Binaries on Alpine

Pre-compiled binaries for musl based architectures are unofficially available for most node versions after node v8.16.0

cd && touch .profile
apk add --no-cache libstdc++ coreutils curl bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash
echo "export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release" >> .profile
echo "nvm_get_arch() { nvm_echo \"x64-musl\"; }" >> .profile
source .profile
  • libstdc++ - is the only package that is necessary to run node/npm once it is installed.
  • bash - is required to install nvm... BusyBox's sh has some issues with chmoding the nvm-exec file, but bash can be removed again after install if you want to slim your image.
  • coreutils/curl - is required to for nvm because BusyBox is not 100% POSIX compliant. Mainly ls not accepting a -q argument and wget not having the progress bar option.

Since there are no io.js builds available for musl you can also disable all io.js versions from showing up in nvm ls-remote by also running:

echo "export NVM_IOJS_ORG_MIRROR=https://example.com" >> .profile

Building from Source on Alpine

There is a -s flag for nvm install which requests nvm download Node source and compile it locally. This does not use anything unofficial but it is much slower and more cpu intensive to install and build each version of node.

apk add -U curl bash ca-certificates openssl ncurses coreutils python2 make gcc g++ libgcc linux-headers grep util-linux binutils findutils
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash

Similar to the pre-compiled binaries

  • libstdc++ - is the only package that is necessary to run node/npm once it is installed.

Option 2

I also have another option that detects musl automatically for people in nvm.sh so they don't have to override the nvm_get_arch function in their .profile. nvm would need to be versioned for this though. Branch: https://github.com/bpmccurdy/nvm/tree/unofficial-musl-support Assuming nodejs keeps the same naming structure (node-v13.0.1-linux-x64-musl.tar.xz) when the finally make the musl build an official distribution nvm would likely not need further updating to support it. Basically in the nvm_get_arch function I added to the end

if (ldd $(which echo) | nvm_grep -q musl); then
  NVM_ARCH="${NVM_ARCH}-musl"
fi

The readme is also updated in this branch similarly to Option 1 but without the override script and info. It would also require some more work to update the version references everywhere before it could be pulled.

Option 3

I'm also open to making a couple more adjustments to Option 2 that would have nvm automatically backup to the unofficial-build mirror if no normal distribution was found without manually having to set NVM_NODEJS_ORG_MIRROR. Which basically would fully resolve this issue.

Let me know if you like any of these options :) Sorry for such a long message, I wanted to include everything I had 👍

bpmccurdy avatar Nov 07 '19 01:11 bpmccurdy

@bpmccurdy thanks; sorry for the delay.

Noting in the docs that Alpine only requires libstdc++ to be able to install from source seems like a great option.

Can you elaborate a bit more on the nvm_get_arch change?

ljharb avatar Jan 06 '20 07:01 ljharb

@bpmccurdy thanks. This works perfectly for me, it installs node in the image and allows to change the version from the inside container. for testing purposes. I use zsh, but I think the same should work with bash.

RUN apk add --no-cache libstdc++; \
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash; \
    echo 'source $HOME/.profile;' >> $HOME/.zshrc
RUN { \
     echo 'export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release;'; \
     echo 'nvm_get_arch() { nvm_echo "x64-musl"; }'; \
     } > $HOME/.profile; \
    NVM_DIR="$HOME/.nvm"; source $HOME/.nvm/nvm.sh; source $HOME/.profile; \
    nvm install 13.8.0

or like so

RUN apk add --no-cache libstdc++; \
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash; \
    echo 'source $HOME/.profile;' >> $HOME/.zshrc; \
    echo 'export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release;' >> $HOME/.profile; \
    echo 'nvm_get_arch() { nvm_echo "x64-musl"; }' >> $HOME/.profile; \
    NVM_DIR="$HOME/.nvm"; source $HOME/.nvm/nvm.sh; source $HOME/.profile; \
    nvm install 13.8.0

asolopovas avatar Feb 26 '20 18:02 asolopovas

Taking the instructions from above, I have created a little wrapper that you can use without making changes to .profile

export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release
NVM_DIR="$HOME/.nvm"
source "$HOME/.nvm/nvm.sh"

nvm_get_arch() { nvm_echo "x64-musl"; }

nvm $@

If you want I guess you can alias this over the original nvm. Not sure if it works though.

takase1121 avatar Aug 29 '20 13:08 takase1121

@takase1121 if there's a way to detect x64-musl reliably, then I'd be happy to accept a PR to modify nvm_get_arch.

ljharb avatar Aug 30 '20 04:08 ljharb

@ljharb

Well, I found a way to do that quite reliably. I was looking around and found this. It checks output of ldd --version against musl and -musl.

In order to confirm the reliability of this method, I cloned musl and made a script to search each commit for musl in dynlink.c (The file that contains entry point for ldd). The result is as such: image

Checking commit message: image

This is where support for ldd is added to musl. Great. The commit belongs to v0.9.1 which should be around 2012. Unless your OS runs on a very old version of musl (I don't think that's very possible), it will work.

This is the script I used:

found=0
total=0
firstfound=""

for commit in $(git rev-list --reverse master)
do
    git checkout $commit > /dev/null 2>&1
    grep --include="dynlink.c" -rnw '.' -e "musl" > /dev/null
    if [ $? -eq 0 ]
    then
        if [ -z "$firstfound" ]; then firstfound="$commit"; fi
        found=$(( found + 1 ))
    fi
    total=$(( total + 1 ))
done

percentage=$(echo "scale=2; $found / $total * 100" | bc)
echo "$found out of $total commits has it, $percentage% coverage"
echo "First found in $firstfound"

However this is only a proof from musl's source code. The distro could change it themselves and that is a problem. I don't have time and resources to conduct tests on multiple machines/VMs. Hopefully somebody else can give a better proof than mine.

EDIT: I just realize this is almost duplicate of Option 2 above.

takase1121 avatar Aug 31 '20 05:08 takase1121

I was using @bpmccurdy 's option 1 but with latest docker image update nvm stopped working. There is some strange curl error (416 ). I'm using nvm version 0.39.1 and docker amazeeio/php - 8.0-cli-drupal-22.2.0, amazeeio/php - 7.4-cli-drupal-latest

$ nvm install
Found '/app/frontend/.nvmrc' with version <lts/fermium>
Downloading and installing node v14.19.1...
Downloading https://unofficial-builds.nodejs.org/download/release/v14.19.1/node-v14.19.1-linux-x64-musl.tar.xz...
########################################################################################################## 100.0%curl: (22) The requested URL returned error: 416

Binary download from https://unofficial-builds.nodejs.org/download/release/v14.19.1/node-v14.19.1-linux-x64-musl.tar.xz failed, trying source.
grep: /home/.nvm/.cache/bin/node-v14.19.1-linux-x64-musl/node-v14.19.1-linux-x64-musl.tar.xz: No such file or directory
Provided file to checksum does not exist.
Binary download failed, trying source.
Detected that you have 12 CPU core(s)
Running with 11 threads to speed up the build
Clang v3.5+ detected! CC or CXX not specified, will use Clang as C/C++ compiler!
Downloading https://unofficial-builds.nodejs.org/download/release/v14.19.1/node-v14.19.1.tar.xz...
curl: (22) The requested URL returned error: 404                                                                

Binary download from https://unofficial-builds.nodejs.org/download/release/v14.19.1/node-v14.19.1.tar.xz failed, trying source.
grep: /home/.nvm/.cache/src/node-v14.19.1/node-v14.19.1.tar.xz: No such file or directory
Provided file to checksum does not exist.

ivangrozni avatar Apr 15 '22 15:04 ivangrozni

This is pretty much what is needed on the .zshrc | .profile

export NVM_NODEJS_ORG_MIRROR=https://unofficial-builds.nodejs.org/download/release # Set up unofficial builds
export NVM_IOJS_ORG_MIRROR=https://example.com # (Optional) Disable IOJS from appearing on ls-remote
nvm_get_arch() { nvm_echo x64-musl; } # Needed to build the download URL

kevinrodriguez-io avatar Apr 29 '22 05:04 kevinrodriguez-io

why someone can't just fix this issue once and for all? Alpine is everywhere since docker is out, its just a tiny if statement and so many developers will get happier.

asolopovas avatar Sep 19 '23 10:09 asolopovas

@asolopovas nvm isn't going to point to "unofficial builds", they're unofficial.

I would be happy to accept a PR that returns the proper nvm_get_arch value, though.

ljharb avatar Sep 19 '23 23:09 ljharb

https://github.com/nvm-sh/nvm/pull/3212, some tests are failing, but for it to work NVM_NODEJS_ORG_MIRROR environment varialbe must be equale to "https://unofficial-builds.nodejs.org/download/release", or how would you like me to add the propper value than?

asolopovas avatar Sep 30 '23 22:09 asolopovas

It’d be fine to use that in tests, but to be clear - what happens when pointing to the default node version list with that arch?

ljharb avatar Sep 30 '23 23:09 ljharb

If understood you correctly, nothing should happen without unless environment variabl NVM_NODEJS_ORG_MIRROR is set to url with musl build it will fail trying to get musl build from official source

On Sun, 1 Oct 2023, 00:43 Jordan Harband, @.***> wrote:

It’d be fine to use that in tests, but to be clear - what happens when pointing to the default node version list with that arch?

— Reply to this email directly, view it on GitHub https://github.com/nvm-sh/nvm/issues/1102#issuecomment-1741883424, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABUS5IDTWTSXCX6O55PU7S3X5CVBTANCNFSM4CEZFXYA . You are receiving this because you were mentioned.Message ID: @.***>

asolopovas avatar Oct 03 '23 11:10 asolopovas

Then we should have a test for that failure.

ljharb avatar Oct 04 '23 13:10 ljharb