Verdaccio Private npm Registry Installation

Verdaccio is a lightweight private npm registry that proxies the public npm registry and caches packages locally, cutting download times and enabling offline workflows. This guide covers installing Verdaccio on Linux, configuring authentication and access control, publishing packages, Docker deployment, and CI/CD integration.

Prerequisites

  • Ubuntu 20.04+/Debian 11+ or CentOS 8+/Rocky Linux 8+
  • Node.js 18+ and npm installed
  • sudo or root access
  • Domain name (optional but recommended for production)
# Install Node.js 18 if needed
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
node --version

Installing Verdaccio

# Install Verdaccio globally
sudo npm install -g verdaccio

# Verify installation
verdaccio --version

# Run once to generate default config
verdaccio &
sleep 3
kill %1
ls ~/.config/verdaccio/

For a system-wide installation with a dedicated user:

sudo useradd -r -m -d /opt/verdaccio -s /bin/bash verdaccio
sudo -u verdaccio npm install -g verdaccio

Configuration

The main config file is at ~/.config/verdaccio/config.yaml:

cat > ~/.config/verdaccio/config.yaml << 'EOF'
# Storage path for cached/published packages
storage: /opt/verdaccio/storage
plugins: /opt/verdaccio/plugins

web:
  title: My Private Registry
  # logo: /path/to/logo.png

auth:
  htpasswd:
    file: /opt/verdaccio/htpasswd
    max_users: 50          # -1 to disable new user registration

uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    timeout: 30s
    max_fails: 5
    fail_timeout: 5m

packages:
  # Private packages (@mycompany scope)
  '@mycompany/*':
    access: $authenticated
    publish: $authenticated
    unpublish: $authenticated

  # All other packages proxy to npm
  '**':
    access: $all
    publish: $authenticated
    unpublish: $authenticated
    proxy: npmjs

server:
  keepAliveTimeout: 60

middlewares:
  audit:
    enabled: true

logs:
  - {type: stdout, format: pretty, level: http}

listen:
  - 0.0.0.0:4873
EOF

sudo mkdir -p /opt/verdaccio/{storage,plugins}
sudo chown -R verdaccio:verdaccio /opt/verdaccio

Running as a Systemd Service

sudo tee /etc/systemd/system/verdaccio.service << 'EOF'
[Unit]
Description=Verdaccio Private npm Registry
After=network.target

[Service]
Type=simple
User=verdaccio
Environment=NODE_ENV=production
ExecStart=/usr/bin/verdaccio --config /opt/verdaccio/config.yaml --listen 0.0.0.0:4873
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now verdaccio
sudo systemctl status verdaccio
journalctl -u verdaccio -f

Publishing and Using Packages

Add a user to the registry:

npm adduser --registry http://localhost:4873
# Enter username, password, and email when prompted

Configure your project to use the private registry:

# Option 1: Set registry per-project in .npmrc
echo "registry=https://registry.yourdomain.com" > .npmrc

# Option 2: Set for a specific scope only
echo "@mycompany:registry=https://registry.yourdomain.com" >> ~/.npmrc

# Option 3: Set globally
npm config set registry https://registry.yourdomain.com

Publish a package:

# In your package directory
npm publish --registry https://registry.yourdomain.com

# Install from your private registry
npm install @mycompany/my-package

Authentication and Access Control

Add users via the CLI (htpasswd-based auth):

# The first login via npm adduser creates the htpasswd file
# Or create it manually
sudo apt install -y apache2-utils

htpasswd -B /opt/verdaccio/htpasswd alice
htpasswd -B /opt/verdaccio/htpasswd bob

Advanced access control with scopes:

# In config.yaml
packages:
  '@internal/*':
    access: $authenticated
    publish: alice bob     # Only alice and bob can publish
    unpublish: alice

  '@public/*':
    access: $all           # Public read access
    publish: $authenticated

  '**':
    access: $all
    proxy: npmjs

Docker Deployment

mkdir -p /opt/verdaccio/{conf,storage,plugins}
chmod -R 777 /opt/verdaccio  # Verdaccio runs as uid 10001 in Docker

cat > /opt/verdaccio/docker-compose.yml << 'EOF'
version: '3.8'
services:
  verdaccio:
    image: verdaccio/verdaccio:latest
    container_name: verdaccio
    ports:
      - "127.0.0.1:4873:4873"
    volumes:
      - ./conf:/verdaccio/conf
      - ./storage:/verdaccio/storage
      - ./plugins:/verdaccio/plugins
    environment:
      - VERDACCIO_PORT=4873
    restart: unless-stopped
EOF

# Copy config to the right place
cp ~/.config/verdaccio/config.yaml /opt/verdaccio/conf/config.yaml

docker compose up -d
docker compose logs -f verdaccio

Nginx Reverse Proxy

server {
    listen 443 ssl;
    server_name registry.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/registry.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/registry.yourdomain.com/privkey.pem;

    # Allow large package uploads
    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:4873/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-NginX-Proxy true;
    }
}
sudo certbot --nginx -d registry.yourdomain.com
sudo nginx -t && sudo systemctl reload nginx

CI/CD Integration

GitHub Actions / Woodpecker CI example:

# .github/workflows/publish.yml
steps:
  - uses: actions/checkout@v3

  - name: Setup Node.js
    uses: actions/setup-node@v3
    with:
      node-version: 18
      registry-url: https://registry.yourdomain.com

  - name: Install dependencies
    run: npm ci
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  - name: Publish
    run: npm publish
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Generate a CI token (Bearer auth):

# Login and get the token from ~/.npmrc after login
npm login --registry https://registry.yourdomain.com
grep "//registry.yourdomain.com" ~/.npmrc
# Output: //registry.yourdomain.com/:_authToken=your_token_here

Troubleshooting

Package not found / falls through to npm incorrectly:

# Verify uplink is configured
# Check storage directory permissions
ls -la /opt/verdaccio/storage/

# Test uplink connectivity
curl https://registry.npmjs.org/lodash

Authentication fails:

# Check htpasswd file exists and is readable
ls -la /opt/verdaccio/htpasswd

# Re-generate token
npm logout --registry https://registry.yourdomain.com
npm login --registry https://registry.yourdomain.com

Large packages failing to upload:

Increase client_max_body_size in Nginx (see above) and check disk space on the storage volume.

Service won't start:

journalctl -u verdaccio -n 50
# Common cause: port already in use or config syntax error
verdaccio --config /opt/verdaccio/config.yaml  # run manually to see errors

Conclusion

Verdaccio delivers a simple yet powerful private npm registry that reduces external network dependency and enables scoped package management for your organization. Its proxy and caching capabilities mean your CI/CD pipelines stay fast even during npm outages. For team use, pair it with Nginx SSL and htpasswd authentication for a production-ready setup.