Verdaccio es un registro npm ligero y autoalojado. Sirve para publicar paquetes privados de tu organización y, a la vez, actuar como proxy con caché del registro público registry.npmjs.org, de forma que las instalaciones repetidas sean más rápidas y no dependan de Internet para los paquetes ya descargados. En este tutorial desplegarás Verdaccio 6 con Docker Compose en Ubuntu 24.04, lo publicarás detrás de Nginx con HTTPS, publicarás un paquete con scope y configurarás un pipeline de CI para usarlo.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS y al menos 1 GB de RAM, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
- Nginx y Certbot instalados (
sudo apt install nginx certbot python3-certbot-nginx). - Un subdominio como
npm.your_domaincon un registro DNS A apuntando a la IP del servidor. - Node.js y npm en la máquina desde la que publicarás paquetes (tu equipo de trabajo).
En los ejemplos, sustituye npm.your_domain por tu dominio y @your_company por el scope de npm que usará tu organización.
Paso 1: Crear la estructura de directorios
Verdaccio guarda su configuración en conf/ y los paquetes (y el fichero de usuarios) en storage/. Crea ambos directorios:
sudo mkdir -p /opt/verdaccio/{conf,storage,plugins}
La imagen oficial ejecuta Verdaccio con el usuario de UID 10001 y GID 65533, no como root. Asigna la propiedad de los directorios a ese usuario para que pueda escribir en ellos:
sudo chown -R 10001:65533 /opt/verdaccio
Paso 2: Escribir la configuración de Verdaccio
Crea el fichero de configuración:
sudo nano /opt/verdaccio/conf/config.yaml
storage: /verdaccio/storage
plugins: /verdaccio/plugins
web:
title: Registro npm de your_company
auth:
htpasswd:
file: /verdaccio/storage/htpasswd
max_users: -1
uplinks:
npmjs:
url: https://registry.npmjs.org/
timeout: 30s
maxage: 10m
max_fails: 3
fail_timeout: 5m
packages:
"@your_company/*":
access: $authenticated
publish: $authenticated
unpublish: $authenticated
"**":
access: $authenticated
publish: $authenticated
proxy: npmjs
max_body_size: 100mb
log:
type: stdout
format: pretty
level: http
Las claves importantes son:
auth.htpasswd.max_users: -1desactiva el registro libre de usuarios connpm adduser. Las cuentas las crearás tú en el paso 4.uplinks.npmjsdefine el registro público como origen.maxageindica cuánto tiempo se consideran frescos los metadatos cacheados antes de volver a consultarlos.- El bloque
@your_company/*no tieneproxy, así que esos paquetes solo pueden venir de tu registro. Así nadie puede suplantar un paquete interno publicando uno con el mismo nombre en npmjs. - El bloque
**cubre el resto de paquetes y los obtiene de npmjs a través de la caché. Conaccess: $authenticatedsolo los usuarios con cuenta pueden descargar, lo que evita que el servidor se convierta en un proxy abierto. max_body_sizesube el límite de tamaño de las peticiones para permitir publicar paquetes grandes.
Asegúrate de que el fichero pertenece al usuario del contenedor:
sudo chown 10001:65533 /opt/verdaccio/conf/config.yaml
Paso 3: Arrancar Verdaccio con Docker Compose
Crea el fichero de Compose:
sudo nano /opt/verdaccio/docker-compose.yml
services:
verdaccio:
image: verdaccio/verdaccio:6
restart: unless-stopped
ports:
- "127.0.0.1:4873:4873"
volumes:
- ./conf:/verdaccio/conf
- ./storage:/verdaccio/storage
- ./plugins:/verdaccio/plugins
El puerto 4873 solo escucha en 127.0.0.1: el acceso público pasará por Nginx con HTTPS.
Arranca el contenedor:
cd /opt/verdaccio
sudo docker compose up -d
Comprueba que responde localmente:
curl -s http://127.0.0.1:4873/-/ping
{}
Si no obtienes respuesta, revisa los logs con sudo docker compose logs verdaccio. Un error EACCES indica que los permisos del paso 1 no se aplicaron.
Paso 4: Crear los usuarios
Con max_users: -1, los usuarios se añaden directamente al fichero htpasswd. Instala la utilidad htpasswd:
sudo apt install apache2-utils
Crea el primer usuario con cifrado bcrypt (-B). La opción -c crea el fichero, así que úsala solo la primera vez:
sudo htpasswd -B -c /opt/verdaccio/storage/htpasswd your_user
Para añadir más usuarios después, repite el comando sin -c. Devuelve la propiedad del fichero al usuario del contenedor:
sudo chown 10001:65533 /opt/verdaccio/storage/htpasswd
Verdaccio relee el fichero htpasswd cuando cambia, así que no hace falta reiniciarlo.
Paso 5: Publicar Verdaccio con Nginx y HTTPS
Crea el bloque de servidor:
sudo nano /etc/nginx/sites-available/verdaccio
server {
listen 80;
listen [::]:80;
server_name npm.your_domain;
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;
}
}
La cabecera X-Forwarded-Proto es necesaria: Verdaccio la usa para generar las URL de descarga de los paquetes con https://. Sin ella, npm recibiría enlaces http:// a los tarballs.
Activa el sitio y recarga Nginx:
sudo ln -s /etc/nginx/sites-available/verdaccio /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Si usas UFW, abre HTTP y HTTPS, y después obtén el certificado:
sudo ufw allow 'Nginx Full'
sudo certbot --nginx -d npm.your_domain
Comprueba el acceso por HTTPS:
curl -s https://npm.your_domain/-/ping
{}
Paso 6: Configurar npm en tu equipo
A partir de aquí, trabaja en tu equipo de desarrollo. Inicia sesión en el registro. La opción --auth-type=legacy fuerza el inicio de sesión con usuario y contraseña, que es el que admite el plugin htpasswd:
npm login --registry https://npm.your_domain/ --auth-type=legacy
npm notice Log in on https://npm.your_domain/
Username: your_user
Password:
Logged in on https://npm.your_domain/.
npm guarda un token en ~/.npmrc. Confirma que la sesión funciona:
npm whoami --registry https://npm.your_domain/
your_user
Tienes dos formas de usar el registro:
-
Solo para tu scope. Los paquetes
@your_company/*se resuelven contra Verdaccio y el resto contra npmjs directamente:npm config set @your_company:registry https://npm.your_domain/ -
Para todo. Todos los paquetes pasan por Verdaccio y se cachean en tu servidor:
npm config set registry https://npm.your_domain/
Para fijar el comportamiento por proyecto, guarda la misma línea en un fichero .npmrc en la raíz del proyecto, por ejemplo @your_company:registry=https://npm.your_domain/, y haz commit de él. Nunca hagas commit de un .npmrc que contenga tokens.
Paso 7: Publicar e instalar un paquete privado
Crea un paquete de prueba con el scope de tu organización:
mkdir hello-lib && cd hello-lib
npm init --scope=@your_company -y
echo "module.exports = () => 'hola desde Verdaccio';" > index.js
Publícalo en tu registro:
npm publish --registry https://npm.your_domain/
npm notice Publishing to https://npm.your_domain/ with tag latest and default access
+ @your_company/[email protected]
Comprueba que el registro lo conoce:
npm view @your_company/hello-lib --registry https://npm.your_domain/
El paquete también aparece en la interfaz web de https://npm.your_domain tras iniciar sesión. Para instalarlo en otro proyecto que tenga el .npmrc con el scope configurado, basta con npm install @your_company/hello-lib.
Para verificar la caché de npmjs, consulta un paquete público a través de Verdaccio:
npm view express version --registry https://npm.your_domain/
Después de instalarlo alguna vez desde el registro, en el servidor verás su directorio en /opt/verdaccio/storage/express/.
Paso 8: Usar el registro desde CI/CD
En CI no hay sesión interactiva, así que el pipeline debe recibir un token. Usa el que npm guardó al iniciar sesión, idealmente con un usuario dedicado para CI creado como en el paso 4:
grep npm.your_domain ~/.npmrc
//npm.your_domain/:_authToken=eyJhbGciOi...
Guarda el valor tras _authToken= como secreto en tu sistema de CI con el nombre NPM_TOKEN. Después, añade al repositorio un .npmrc que lea el token de una variable de entorno. npm sustituye ${NPM_TOKEN} por su valor al ejecutarse:
@your_company:registry=https://npm.your_domain/
//npm.your_domain/:_authToken=${NPM_TOKEN}
Un ejemplo para GitHub Actions o Gitea Actions:
name: publish
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Solución de problemas
npm publish devuelve 401 o 403. Comprueba la sesión con npm whoami --registry https://npm.your_domain/. Si la sesión es correcta, revisa que el nombre del paquete encaje con un bloque de packages cuyo publish incluya a tu usuario. Recuerda que Verdaccio no permite volver a publicar una versión ya existente: incrementa la versión con npm version patch.
npm publish devuelve 413 Request Entity Too Large. El paquete supera client_max_body_size en Nginx o max_body_size en Verdaccio. Sube ambos valores, recarga Nginx y reinicia Verdaccio con sudo docker compose restart.
Los paquetes públicos no se descargan. Comprueba que el servidor alcanza npmjs con curl -sI https://registry.npmjs.org/ y revisa los logs con sudo docker compose logs verdaccio --tail 50 para ver errores del uplink.
Conclusión
Has desplegado Verdaccio con Docker, protegido con HTTPS y autenticación, y lo has usado para publicar un paquete privado y cachear paquetes de npmjs. Como siguientes pasos, puedes limitar quién publica en cada scope indicando nombres de usuario concretos en publish en lugar de $authenticated, programar copias de seguridad del directorio /opt/verdaccio/storage y apuntar todos tus pipelines al registro para acelerar las instalaciones.
