A self-hosted dependency proxy caches packages from external registries (npm, PyPI, Docker Hub, Maven Central) on your local network. This reduces download times, protects against upstream outages, and lets you control which package versions enter your development and production environments. While full package registries host private packages, a dependency proxy specifically focuses on caching external registries for faster, more reliable dependency resolution.

Why Run a Dependency Proxy?

Self-hosting a dependency proxy solves several common problems:

  • Build reliability — CI/CD pipelines don’t fail when npmjs.org or PyPI goes down
  • Faster builds — cached packages download from your local network in milliseconds instead of seconds
  • Bandwidth savings — hundreds of developers pulling the same packages wastes external bandwidth
  • Security control — audit and approve packages before they enter your environment
  • Air-gapped support — isolated environments with no internet access can still install dependencies
  • Version pinning — lock specific package versions to prevent supply chain attacks

Comparison Table

FeatureVerdaccio (npm)DevPI (PyPI)Nexus Repository (Multi-format)
Package formatsnpm, BowerPyPI (Python)npm, PyPI, Maven, Docker, NuGet, RubyGems, Go, apt, yum
Proxy cachingYes (upstream proxy)Yes (mirror + proxy)Yes (proxy repositories)
Private packagesYesYesYes
Upstream fallbackAutomaticAutomaticAutomatic
Docker proxyNoNoYes
Docker ComposeSimple (single container)Moderate (2 containers)Moderate (single container)
Web UIBasic package browserFull web UIFull management console
LDAP/SSOVia pluginsBuilt-inBuilt-in (LDAP, SAML, OIDC)
RBACBasicPer-index ACLsFull role-based access control
Storage sizeSmall (npm only)Small (PyPI only)Large (all formats)
Best forNode.js teamsPython teamsMulti-language organizations

Verdaccio: Self-Hosted npm Proxy Registry

Verdaccio is the most popular self-hosted npm proxy registry. It acts as a caching proxy for the public npm registry, storing downloaded packages locally so subsequent requests are served from your cache.

Docker Compose for Verdaccio

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
version: "3.8"

services:
  verdaccio:
    image: verdaccio/verdaccio:5
    container_name: verdaccio
    restart: unless-stopped
    ports:
      - "4873:4873"
    volumes:
      - verdaccio-storage:/verdaccio/storage
      - ./config.yaml:/verdaccio/conf/config.yaml:ro

volumes:
  verdaccio-storage:

Verdaccio Configuration for Proxy Caching

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# config.yaml
storage: /verdaccio/storage
plugins: /verdaccio/plugins

web:
  enable: true
  title: Verdaccio Dependency Proxy

uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    max_fails: 40
    maxage: 30m
    timeout: 10s
    fail_timeout: 10m
    cache: true

packages:
  '@*/*':
    access: $all
    publish: $authenticated
    proxy: npmjs

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

log:
  type: stdout
  format: pretty
  level: http

Configuring npm to Use Verdaccio

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Point your project to the local proxy
npm set registry http://localhost:4873/

# Or use per-project .npmrc
echo "registry=http://localhost:4873/" > .npmrc

# Verify the proxy is caching packages
npm install express
# First install: downloads from npmjs.org
# Second install: served from local Verdaccio cache

Docker Registry Proxy with Verdaccio

While Verdaccio itself doesn’t proxy Docker images, you can pair it with a Docker registry proxy for container dependencies:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
services:
  registry-proxy:
    image: registry:2
    container_name: docker-proxy
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io
    volumes:
      - docker-proxy-data:/var/lib/registry

volumes:
  docker-proxy-data:

DevPI: Self-Hosted PyPI Proxy

DevPI is a powerful PyPI caching proxy and private package index server. It mirrors packages from PyPI and caches them locally, providing fast, reliable access to Python dependencies.

Docker Compose for DevPI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: "3.8"

services:
  devpi-server:
    image: devpteam/devpi-server:latest
    container_name: devpi-server
    restart: unless-stopped
    ports:
      - "3141:3141"
    environment:
      - DEVPI_SERVERDIR=/data
      - DEVPI_INITIAL_USER=root
      - DEVPI_INITIAL_PASSWORD=test
    volumes:
      - devpi-data:/data

  devpi-web:
    image: devpteam/devpi-web:latest
    container_name: devpi-web
    restart: unless-stopped
    ports:
      - "80:80"
    environment:
      - DEVPI_URL=http://devpi-server:3141
    depends_on:
      - devpi-server

volumes:
  devpi-data:

Configuring pip to Use DevPI

1
2
3
4
5
6
7
8
9
# Configure pip to use the local proxy
pip config set global.index-url http://localhost:3141/root/pypi/+simple/
pip config set global.trusted-host localhost

# Or use a requirements file with explicit index
pip install -r requirements.txt --index-url http://localhost:3141/root/pypi/+simple/

# Install a package (first time downloads from PyPI, subsequent from cache)
pip install requests

DevPI Mirror Configuration

DevPI supports full PyPI mirroring — pre-caching all packages rather than just those you request:

1
2
3
4
5
6
7
8
# Create a mirror index
devpi index root/mirror mirror=true

# Trigger a full mirror (downloads all of PyPI)
devpi refresh --index root/mirror

# For most teams, proxy mode (on-demand caching) is sufficient
# Full mirroring is useful for air-gapped environments

Nexus Repository: Multi-Format Dependency Proxy

Sonatype Nexus Repository is the most comprehensive dependency management platform, supporting proxy caching for over 15 package formats including npm, PyPI, Maven, Docker, NuGet, and more.

Docker Compose for Nexus Repository

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: "3.8"

services:
  nexus:
    image: sonatype/nexus3:latest
    container_name: nexus-repository
    restart: unless-stopped
    ports:
      - "8081:8081"
      - "8082:8082"
      - "8083:8083"
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m
    volumes:
      - nexus-data:/nexus-data

volumes:
  nexus-data:

Setting Up Proxy Repositories in Nexus

After initial setup (default credentials: admin / admin123), configure proxy repositories through the Nexus web UI at http://localhost:8081:

  1. Navigate to Administration → Repositories → Create Repository
  2. Select the format (npm, PyPI, Docker, Maven, etc.)
  3. Choose proxy as the repository type
  4. Set the remote URL (e.g., https://registry.npmjs.org/ for npm)
  5. Configure caching settings (TTL, storage blob store)
  6. Create a group repository that combines your proxy + hosted repositories

Nexus Docker Proxy Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Pull Docker images through the Nexus proxy
docker pull localhost:8083/nginx:latest

# Configure Docker daemon to use Nexus as registry mirror
# /etc/docker/daemon.json
{
  "registry-mirrors": ["http://localhost:8083"]
}

sudo systemctl restart docker

When to Choose Each Tool

Choose Verdaccio when:

  • Your team works primarily with Node.js/JavaScript
  • You need a lightweight, easy-to-setup npm proxy
  • You want simple package publishing alongside proxy caching
  • Storage requirements are modest (npm packages are small)

Choose DevPI when:

  • Your team works primarily with Python
  • You need PyPI caching with advanced index management
  • You want to create private PyPI indexes with inheritance chains
  • You need full-text search across cached packages

Choose Nexus Repository when:

  • Your organization uses multiple programming languages
  • You need a single proxy for npm + PyPI + Maven + Docker + more
  • You require enterprise features (LDAP, SSO, RBAC, audit logging)
  • You want Docker registry proxy alongside package proxying
  • You need content type validation and security rules

Self-Hosted Dependency Proxy Best Practices

Storage Planning

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Monitor proxy cache size
du -sh /var/lib/verdaccio/storage/     # Verdaccio
du -sh /var/lib/devpi/data/           # DevPI
du -sh /opt/sonatype/sonatype-work/   # Nexus

# Clean old cached packages (Verdaccio)
verdaccio-storage-clean --max-age 30d

# Clean old cached packages (Nexus via API)
curl -X POST -u admin:admin123 \
  http://localhost:8081/service/rest/v1/script/cleanup/run

Security Considerations

  • Package validation — verify checksums for cached packages to prevent tampering
  • Access control — restrict who can publish to hosted repositories
  • Upstream pinning — pin proxy to specific upstream registries to prevent cache poisoning
  • Rate limiting — configure rate limits to prevent abuse of your proxy
  • Audit logging — enable logging of all proxy requests for compliance

For teams managing dependency security, also see our guides on dependency vulnerability scanning and dependency automation.

FAQ

What is the difference between a dependency proxy and a package registry?

A package registry stores and serves packages (both public and private). A dependency proxy specifically sits between your development environment and external registries, caching packages as they are requested. Many tools (Verdaccio, Nexus) serve both roles — they act as a proxy for external packages AND host private packages. The key difference is intent: a proxy focuses on caching upstream packages, while a registry focuses on hosting your own.

How much disk space does a dependency proxy need?

For a small team (5-10 developers): npm proxy typically needs 10-50 GB, PyPI proxy needs 20-100 GB. For larger teams or full mirrors: npm full mirror requires 200+ GB, PyPI full mirror requires 4+ TB. Most teams use on-demand proxy caching (only caching what is requested), which keeps storage manageable. Nexus with multiple format proxies typically needs 100-500 GB for a medium-sized organization.

Can I use multiple dependency proxies for different ecosystems?

Yes. A common pattern is Verdaccio for npm, DevPI for PyPI, and Docker Registry for container images — each optimized for its ecosystem. Alternatively, Nexus Repository can handle all formats in a single instance, reducing operational complexity but requiring more resources.

How do I handle dependency proxy failures?

Configure fallback to upstream registries. Both Verdaccio and DevPI automatically fall back to the upstream registry if the local cache doesn’t have a package. For critical CI/CD pipelines, configure a second proxy as a backup, or keep a local mirror of your most critical packages.

Does a dependency proxy replace private package registries?

No. A dependency proxy caches public packages from upstream registries. If you need to host private packages (internal libraries, proprietary code), you need a package registry with hosting capability. Most proxy tools also support private package hosting, so you can use the same instance for both purposes.

How do I pre-warm a dependency proxy cache?

Create a script that installs all your project dependencies, triggering cache population:

1
2
3
4
5
6
# Pre-warm npm cache
cat package.json | jq -r '.dependencies, .devDependencies | keys[]' | \
  xargs -I{} npm install {} --registry http://localhost:4873/

# Pre-warm PyPI cache
cat requirements.txt | xargs -I{} pip install {} --index-url http://localhost:3141/root/pypi/+simple/