Blog

Victor is a full stack software engineer who loves travelling and building things. Most recently created Ewolo, a cross-platform workout logger.

    Setup a private npm registry

    With the recent docker fiasco it has become more and more imperative that we own our own build pipelines and vendor as much of 3rd party code as much as possible. In this article we will look at setting up a production ready private npm registry using verdaccio, an open source npm registry.

    Pre-requisites

    While verdaccio can run locally, we would like to have something that is accessible over the internet, for example: https://npm-registry.example.org. To that effect we would need host running ubuntu (or your flavor of linux) with root privileges and a webserver installed. This tutorial provides instructions for setting up a reverse proxy to verdaccio via Nginx.

    Installation

    While verdaccio comes with a docker image, in this setup we will install verdaccio directly in order to reduce the risk of docker itself. The first step then is to install the latest LTS node version via nvm. Once Node.js installed, run npm install --location=global verdaccio. Test that verdaccio is working by simply running it on the commandline: verdaccio. Note that this will create a default config file.

    We will now setup the subdomain and test that we can access our registry. With verdaccio running, we use a different session to create /etc/nginx/sites-available/npm-registry as below (change example.org to your domain):

    
    server {
      server_name npm-registry.example.org;
    
      # add Strict-Transport-Security to prevent man in the middle attacks
      add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload;";
      add_header X-Frame-Options DENY;
      add_header X-Content-Type-Options nosniff;
    
      access_log /var/log/nginx/npm-registry.example.org.log;
    
      location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-NginX-Proxy true;
    
        proxy_set_header Host            $host:$server_port;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    
        proxy_pass http://127.0.0.1:4873;
        proxy_redirect off;
    
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
      }
    }
    
    

    Enable the subdomain and setup https via certbot

    
    sudo ln -s /etc/nginx/sites-available/npm-registry /etc/nginx/sites-enabled/npm-registry
    sudo nginx -t
    sudo service nginx restart
    sudo certbot --nginx
    
    

    If everything was successful then we should be able to head to https://npm-registry.example.org and see the verdaccio installation. In order to test that this works create a .npmrc file in your local node.js project root with the following line in it: registry=https://npm-registry.example.org. This will tell npm to fetch packages from the provided registry. Running npm config get registry should also confirm this. Finally, executing npm install should now also populate the default storage (~/.config/verdaccio/storage) with npm packages on the server. We will now customise the defaults, add authentication and make the service persistent.

    Secure the private registry

    As it stands the registry is not very private as anyone with the url can start using it as a proxy. We will now customise the defaults to secure our setup.

    
    mkdir ~/verdaccio
    mv ~/.config/verdaccio/config.yaml ~/verdaccio/config.yaml
    rm ~/.config/verdaccio
    mkdir ~/verdaccio/storage
    mkdir ~/verdaccio/plugins
    
    

    Assuming that your home directory is /home/user, we will update the following config settings:

    
    # path to a directory with all packages
    storage: /home/user/verdaccio/storage
    # path to a directory with plugins to include
    plugins: /home/user/verdaccio/plugins
    
    # https://verdaccio.org/docs/configuration#authentication
    auth:
      htpasswd:
        file: /home/user/verdaccio/htpasswd
        # Maximum amount of users allowed to register, defaults to "+inf".
        # You can set this to -1 to disable registration.
        max_users: -1
        # Hash algorithm, possible options are: "bcrypt", "md5", "sha1", "crypt".
        algorithm: bcrypt # by default is crypt, but is recommended use bcrypt for new installations
        # Rounds number for "bcrypt", will be ignored for other algorithms.
        rounds: 10
    
    # https://verdaccio.org/docs/configuration#uplinks
    # a list of other known repositories we can talk to
    uplinks:
      npmjs:
        url: https://registry.npmjs.org/
    
    # Learn how to protect your packages
    # https://verdaccio.org/docs/protect-your-dependencies/
    # https://verdaccio.org/docs/configuration#packages
    packages:
      '@*/*':
        # scoped packages
        access: $authenticated
        publish: $authenticated
        unpublish: $authenticated
        proxy: npmjs
    
      '**':
        # allow all users (including non-authenticated users) to read and
        # publish all packages
        #
        # you can specify usernames/groupnames (depending on your auth plugin)
        # and three keywords: "$all", "$anonymous", "$authenticated"
        access: $authenticated
    
        # allow all known users to publish/publish packages
        # (anyone can register by default, remember?)
        publish: $authenticated
        unpublish: $authenticated
    
        # if package is not available locally, proxy requests to 'npmjs' registry
        proxy: npmjs
    
    
    # https://verdaccio.org/docs/configuration#security
    security:
      api:
        legacy: true
        jwt:
          sign:
            expiresIn: 29d
        web:
          sign:
            expiresIn: 1h # 1 hour by default
    
    

    Note that by setting max_users to -1 we disallow anyone from register themselves. We also set all access to $authenticated so that no unregistered user can use our private repository. Finally we will create our single registered user via the following:

    
    sudo apt install apache2-utils
    htpasswd -c /home/user/verdaccio/htpasswd user
    verdaccio --config /home/user/verdaccio/config.yaml
    
    

    Once verdaccio is running, trying to npm install on the local machine should now result in a code E401. It might be prudent to npm cache clean --force; rm -rf node_modules to force installation from the registry. In order to download packages from our private registry, we will authenticate our machine via: npm login --registry https://npm-registry.cpuat.work --auth-type=legacy. Once authenticated, npm i should work and packages should pop up on the provided storage location on the server as well.

    Persistence

    In order to persist verdaccio across restarts we will use pm2.

    
    npm i -g pm2
    pm2 startup
    pm2 start `which verdaccio` -- --config /home/user/verdaccio/config.yaml
    pm2 save
    
    

    Finally, we setup a cron job to flush pm2 log files in order to save space, crontab -e (change the node version to match the pm2 installation):

    
    # 04:0 on Friday - flush all log files
    5 4 * * 5 /home/user/.nvm/versions/node/v16.15.0/bin/pm2 flush > /home/user/pm2-flush.log 2>&1
    
    

    And that is about it, do not forget to add .npmrc to your .gitignore so that you do not leak your registry information :).

    HackerNews submission / discussion

    Back to the article list.

    SmallData newsletter

    Subscribe to get articles as they are published direct to your inbox!