Skip to content

Install node.js without using sudo


Update: 2012/02/21

As Jonathan pointed out below, the information in this post has been made obsolete by the now supported ‘–prefix’ option in the Node.js configure scripts. As the instructions point out:

./configure --prefix=/your/chosen/path

The preceding code should be sufficient to install Node.js as a local user, and without a need for sudo.

The original post is being left intact for posterity (pun maybe intended) and because it has become the the most popular on this blog to date. Thanks for reading; hope you find something useful here.

The default installation instructions for node.js require using sudo, in most cases. Here’s how to install it as a non-root user. If you are already familiar with why running as a non-root user may be preferable, and have a good grasp of how to build software from source, you’ll probably get the most out of the Short version.

As Julian Gautier pointed out, the “git clone” commands in this post will clone the master branch (bleeding edge development version) of node. Presently, this branch holds v4, which is not backwards compatible with some of the available node packages.

If you need backwards compatibility, you may want to clone the v2 branch. Just replace the git clone commands below with the following:

$ git clone -b v0.2

Thanks to Julian for pointing this out!

Short version:

$ git clone
$ cd node
$ mkdir ~/opt
$ export PREFIX=~/opt; ./configure
$ make
$ make install
$ echo 'export PATH=~/opt/bin:${PATH}' >> ~/.bashrc


If those commands seem obtuse, keep reading, all will be explained in the Long version.

Long version:

Why you should care.

Installing node as a root user means that installing npm, and npm packages also requires the use of sudo. So, you’ll be installing 3rd party libraries of code that have root access to your entire filesystem. But, you’re reading all of the source code for the libraries you use, right? Right.

While in practice, if you trust the libraries you’re using, everything should run along just fine. However, on the Ruby side of life, RVM has made a spoiled rotten developer out of me. It provides the simplest way I know to easily manage multiple versions of Ruby, and keep each version sequestered away with its own libraries and binaries. I use it with gemsets, to keep projects sandboxed, and avoid the hassle of version conflicts between dependency requirements for different projects.

Ndistro seems to fill a similar use case, but it’s newer, and I haven’t spent as much time with it, yet.

At any rate, trying to learn and work on node.js has been a little painful, since things don’t work precisely as expected (that is, with the same kind of seamless workflow which is easily achieved using RVM + Ruby + RubyGems + (your favorite framework) + Autotest…) At the very least, it seems like installing node, npm, and related packages should not require the use of ‘sudo’ – what happens when we try it out of the box?

After cloning the project, configuring the build, and giving the ‘make install’ command, ugliness ensues.

Cannot create folder '/usr/local/include/node/'
(original error: [Errno 13] Permission denied: '/usr/local/include/node/')
make: *** [install] Error 1

It’s trying to create files in ‘/usr/local’, which is owned by none other than ‘root’, see?

$ ls -la /usr
drwxr-xr-x 10 root root 4096 2010-07-21 14:18 local

So it looks like we’ll need to change the install path somehow. Reading through the configuration script shows it’s executing another script called ‘waf-light’, which in turn calls another script, which…

Long story short,

the build/install process ultimately looks at a file named ‘wscript‘ in the project root. Right around line 550 in this file looks to be the relevant part. It checks for the codesence of an environment variable called ‘PREFIX‘ and installs the project there. It stands to reason then, that if we set that variable to something in our home directory, ~/opt, for example, that it will install there. Shall we?

Create the desired install directory

$ mkdir ~/opt

Set the ‘PREFIX’ environment variable

$ export PREFIX=~/opt

Clone the repository

$ git clone

Enter the project directory

$ cd node

Run the configuration script

$ ./configure

If all goes well, you should see something like

'configure' finished successfully (1.298s)

Now build the project with make

$ make

Expect output like

'build' finished successfully (57.711s)

Then install the project

$ make install


'install' finished successfully (0.186s)


$ node
The program 'node' is currently not installed. You can install it by typing:
sudo apt-get install node

FAIL. Why?

Because it’s not in our ‘PATH‘ environment variable yet! So, add ‘~/opt/bin‘ to ‘PATH‘ and it should find the node.js binary (executable program.)

$ export PATH=~/opt/bin:${PATH}
$ which node

Does it run?

$ node
Type '.help' for options.

Does it ever.

This isn’t all, though. Your ‘PATH‘ environment variable will be reset each time you open a new command shell. So, if you’re using Bash, just put that export line in your ‘~/.bashrc‘ file (on OSX you’ll want to add it to your ‘~/.bash_profile‘)

$ echo 'export PATH=~/opt/bin:${PATH}' >> ~/.bashrc

when you start up a new shell, node should work as expected.

Good luck, and until next time, happy hacking!

Update, 2011/02/25 – Updated the uri of the git repository and tested the instructions with master (v0.4)

  1. There’s a bug

    – echo ‘export PATH=~/opt/bin:${PATH} >> ~/.bashrc’
    + echo ‘export PATH=~/opt/bin:${PATH}’ >> ~/.bashrc

  2. That is, it’s a bug in the “short version”. The long version has the correct code.

  3. Thanks for blogging this, and I can’t wait to try it out, but “codeferable” is not a word. At least not yet. I think you did a global search/replace to turn pre tags into code tags and missed an edge case.

  4. Although come to think of it there does to be a trend at conferences away from presentations and towards codesentations

  5. Sorry to spam you but the post also contains “codecisely.”

    • seeflanigan permalink

      You are codecisely right! In this case, I was not trying to coin new terms. It was indeed a simple case of %s/pre/code/g, and is now fixed. Nice catch, and thanks for the heads up!

  6. Julian Gautier permalink

    FYI this version clones master from the node repository which is node v3 which is incompatible with alot of the exisintg node packages. It maybe be a good idea to note that and/or update it to clone a node v2 tag.

  7. Greg Reimer permalink

    Thanks for this post. Something I wasn’t quite clear on: are you saying that a root-owned node installation requires root-executed npm commands? And that’s why one might want to install a self-owned copy of node?

    • seeflanigan permalink

      That’s exactly why. Glad you enjoyed the post!

  8. andrew permalink

    i had trouble running julian’s command this one from stack over flow helped greatly

    mkdir $BRANCH
    cd $BRANCH
    git init
    git remote add -t $BRANCH -f origin $REMOTE_REPO
    git checkout $BRANCH

  9. Thanks very much for this. I’ve used your help to build Node.js for Optware, where the root file system is read only and you install software by mounting a USB drive as /opt and dropping things in there.

    It was hard, but it made the moment that I got Hello World back on port 8124 all the more satisfying!

    • seeflanigan permalink

      Great! I’m glad it helped… It’s amazing to me that a post I wrote on a whim quite some time ago seems to have helped a lot of people. What a good feeling! Thanks for letting me know it’s useful to you 🙂

  10. niteria permalink

    If you have:

    Build failed: -> task failed (err #2):
    {task: uv uv.h -> uv.a

    on 50/76, try symlinking gcc and ar to ~/opt/
    $ ln -s `which gcc` ~/opt/gcc
    $ ln -s `which ar` ~/opt/ar

    It solved it for me.

  11. The node.js wiki has been updated with non-sudo installation instructions:

  12. reda permalink


  13. Vitaly Peressada permalink

    Thanks for the tip. I hope that PREFIX is documented somewhere by the node team. In enterprise world developers rarely get root access.

  14. Jonathan permalink

    What’s wrong with ./configure –prefix=”…”

    • seeflanigan permalink

      Great question. As far as I can tell, nothing. In fact, that looks to be the current convention. I’m updating this post accordingly. Thanks for pointing that out!

      • No problem. Thanks for the great post and for keeping it up to date!

  15. InaChes permalink


    I am trying to install node on a machine on which I don’t have root rights and I encountered a problem while using your steps. When performing ‘make install’ I get the following error:

    ~/node$ make install
    make -C out BUILDTYPE=Release V=1
    make[1]: Entering directory `/home/user/node/out’
    make[1]: Nothing to be done for `all’.
    make[1]: Leaving directory `/home/user/node/out’
    ln -fs out/Release/node node
    /usr/bin/python tools/ install
    installing /usr/local/share/man/man1/node.1
    Traceback (most recent call last):
    File “tools/”, line 157, in
    File “tools/”, line 152, in run
    if cmd == ‘install’: return files(install)
    File “tools/”, line 122, in files
    action([‘doc/node.1’], ‘share/man/man1/’)
    File “tools/”, line 79, in install
    def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
    File “tools/”, line 79, in
    def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
    File “tools/”, line 69, in try_copy
    File “tools/”, line 44, in try_mkdir_r
    File “/usr/lib/python2.6/”, line 157, in makedirs
    mkdir(name, mode)
    OSError: [Errno 13] Permission denied: ‘/usr/local/share/man/man1’
    make: *** [install] Error 1

    What should I do in this case?
    Thank you very much!

    • seeflanigan permalink

      Hi, thank you for your comment, and good question!

      I’m not exactly sure why it’s trying to put the manual pages in /usr/local/share/man/man1.

      Is there any kind of flag or option in the configuration script to set the path for man pages/documentation?

      • InaChes permalink


        I found some details about the man configuration in /node/deps/npm/Makefile:

        cli_mandocs = $(shell find doc/cli -name ‘*.md’ \
        |sed ‘s|.md|.1|g’ \
        |sed ‘s|doc/cli/|man/man1/|g’ ) \
        man/man1/README.1 \

        api_mandocs = $(shell find doc/api -name ‘*.md’ \
        |sed ‘s|.md|.3|g’ \
        |sed ‘s|doc/api/|man/man3/|g’ )

        cli_htmldocs = $(shell find doc/cli -name ‘*.md’ \
        |grep -v ‘’ \
        |sed ‘s|.md|.html|g’ \
        |sed ‘s|doc/cli/|html/doc/|g’ ) \
        html/doc/README.html \

        api_htmldocs = $(shell find doc/api -name ‘*.md’ \
        |sed ‘s|.md|.html|g’ \
        |sed ‘s|doc/api/|html/api/|g’ )

        mandocs = $(api_mandocs) $(cli_mandocs)

        htmldocs = $(api_htmldocs) $(cli_htmldocs)


        # use `npm install ronn` for this to work.
        man/man1/README.1: scripts/ package.json
        scripts/ $< $@

        man/man1/%.1: doc/cli/ scripts/ package.json
        @[ -d man/man1 ] || mkdir -p man/man1
        scripts/ $< $@

        man/man3/%.3: doc/api/ scripts/ package.json
        @[ -d man/man3 ] || mkdir -p man/man3
        scripts/ $< $@

        Should I comment/delete these lines?


      • seeflanigan permalink

        Sorry for the late reply, but good question. Have you tried commenting them?

        It seems like the end result might be that you wouldn’t get documentation though. You probably still want it, just installed in your local path instead of a shared one.

        It’s also possible that this post is no longer the best guide for this. Have you tried the method described here?

        If that works better, please let me know so I can update this entry.

        Good luck!

  16. Ian Tearle permalink

    Are you able to help me with this error, or at least point me to a resource that may give me some ideas?

    make -C out BUILDTYPE=Release V=1
    make[1]: Entering directory `/nfs/c01/h14/mnt/****/users/.home/node/out’
    make[1]: Nothing to be done for `all’.
    make[1]: Leaving directory `/nfs/c01/h14/mnt/****/users/.home/node/out’
    ln -fs out/Release/node node
    /usr/bin/python tools/ install
    installing /usr/local/bin/node
    Traceback (most recent call last):
    File “tools/”, line 156, in
    File “tools/”, line 151, in run
    if cmd == ‘install’: return files(install)
    File “tools/”, line 122, in files
    action([‘out/Release/node’], ‘bin/node’)
    File “tools/”, line 79, in install
    def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
    File “tools/”, line 79, in
    def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
    File “tools/”, line 71, in try_copy
    return shutil.copy2(source_path, target_path)
    File “/usr/lib/python2.6/”, line 95, in copy2
    copyfile(src, dst)
    File “/usr/lib/python2.6/”, line 51, in copyfile
    with open(dst, ‘wb’) as fdst:
    IOError: [Errno 30] Read-only file system: ‘/usr/local/bin/node’
    make: *** [install] Error 1

    Many thanks.

  17. Ian Tearle permalink

    May have figured it out from Node instructions, running ‘./configure –prefix=~/opt/node ‘ adds the path correctly, the instructions above was not writing to the file, I had node_prefix: ” in my config. Running the above I was able to make install successfully.

Trackbacks & Pingbacks

  1. Instalando nodejs e npm no Ubuntu sem sudo « Enrico Batista da Luz
  2. nodejs and jasmine-tool | mikelippold
  3. Install npm and node.js modules as a non-root user | wckuo(Wen-Chun KUO)
  4. Installing BrowserQuest on Amazon EC2 « web3d-blog
  5. NodeJS | Pearltrees

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: