Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Installing on Alpine Linux #1102

Open
neilstuartcraig opened this issue May 25, 2016 · 27 comments
Open

Installing on Alpine Linux #1102

neilstuartcraig opened this issue May 25, 2016 · 27 comments
Labels
feature requests I want a new feature in nvm! installing node Issues with installing node/io.js versions.

Comments

@neilstuartcraig
Copy link
Contributor

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

@ljharb
Copy link
Member

ljharb commented May 25, 2016

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 ljharb added installing node Issues with installing node/io.js versions. feature requests I want a new feature in nvm! labels May 25, 2016
@neilstuartcraig
Copy link
Contributor Author

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!

@ljharb
Copy link
Member

ljharb commented May 25, 2016

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.

@neilstuartcraig
Copy link
Contributor Author

OK cool, thanks for that - seems like a reasonably easy win then. I have filed nodejs/node#6965.

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

Thanks again!

@neilstuartcraig
Copy link
Contributor Author

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 :-)

@ljharb
Copy link
Member

ljharb commented May 25, 2016

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.

@neilstuartcraig
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@ljharb
Copy link
Member

ljharb commented May 30, 2016

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

@neilstuartcraig
Copy link
Contributor Author

neilstuartcraig commented May 31, 2016

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

@travi
Copy link

travi commented Oct 2, 2016

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.

@ljharb
Copy link
Member

ljharb commented Oct 2, 2016

@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!

@travi
Copy link

travi commented Oct 2, 2016

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.

@bpmccurdy
Copy link

bpmccurdy commented Nov 7, 2019

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 👍

@ljharb
Copy link
Member

ljharb commented Jan 6, 2020

@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?

@asolopovas
Copy link
Contributor

asolopovas commented Feb 26, 2020

@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

kienstra added a commit to studiopress/genesis-custom-blocks that referenced this issue Jun 6, 2020
@takase1121
Copy link

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.

@ljharb
Copy link
Member

ljharb commented Aug 30, 2020

@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.

@takase1121
Copy link

takase1121 commented Aug 31, 2020

@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.

@ivangrozni
Copy link

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.

@kevinrodriguez-io
Copy link

kevinrodriguez-io commented Apr 29, 2022

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

@asolopovas
Copy link
Contributor

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.

@ljharb
Copy link
Member

ljharb commented Sep 19, 2023

@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.

@asolopovas
Copy link
Contributor

asolopovas commented Sep 30, 2023

#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?

@ljharb
Copy link
Member

ljharb commented Sep 30, 2023

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?

@asolopovas
Copy link
Contributor

asolopovas commented Oct 3, 2023 via email

@ljharb
Copy link
Member

ljharb commented Oct 4, 2023

Then we should have a test for that failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature requests I want a new feature in nvm! installing node Issues with installing node/io.js versions.
Projects
None yet
Development

No branches or pull requests

8 participants