Harbor es un registro de contenedores de código abierto, proyecto graduado de la CNCF, que añade al registro de Docker proyectos con control de acceso por roles, análisis de vulnerabilidades con Trivy, cuentas robot para CI y replicación entre registros. En este tutorial instalarás Harbor en Ubuntu 24.04 con el instalador oficial basado en Docker Compose, lo protegerás con un certificado de Let's Encrypt que se renueva solo y publicarás tu primera imagen en un proyecto privado.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 4 GB de RAM, 2 vCPU y 40 GB libres en disco (las imágenes ocupan rápido; dimensiona según tu uso).
  • Un usuario no root con privilegios sudo.
  • Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial de Docker.
  • Un dominio o subdominio, por ejemplo registry.your_domain, con un registro A apuntando a la IP del servidor.
  • Los puertos 80 y 443 libres en el servidor (sin Nginx ni Apache escuchando) y abiertos en el firewall.

Comprueba que Docker y Compose están disponibles:

docker --version
docker compose version
Docker version 28.x.x, build ...
Docker Compose version v2.x.x

Paso 1: Obtener el certificado TLS

Docker exige HTTPS para cualquier registro que no sea local, así que empieza por el certificado. Abre los puertos web en UFW:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Instala Certbot y solicita el certificado en modo standalone, en el que Certbot levanta su propio servidor temporal en el puerto 80. Funciona porque Harbor todavía no está en marcha:

sudo apt update
sudo apt install certbot
sudo certbot certonly --standalone -d registry.your_domain
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/registry.your_domain/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/registry.your_domain/privkey.pem

Paso 2: Descargar el instalador de Harbor

Harbor publica dos instaladores: el online, que descarga las imágenes desde Docker Hub durante la instalación, y el offline, que las incluye en el archivo. Usa el online. Consulta la última versión estable en https://github.com/goharbor/harbor/releases y guárdala en una variable, sustituyendo VERSION por el número real con su v inicial (por ejemplo, con el formato v2.x.y):

HARBOR_VERSION=VERSION

Descarga el instalador y descomprímelo en /opt:

cd /tmp
curl -fLO "https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-online-installer-${HARBOR_VERSION}.tgz"
sudo tar -xzf "harbor-online-installer-${HARBOR_VERSION}.tgz" -C /opt
ls /opt/harbor
LICENSE  common.sh  harbor.yml.tmpl  install.sh  prepare

Paso 3: Configurar harbor.yml

Toda la configuración de Harbor vive en harbor.yml. Crea el archivo a partir de la plantilla:

cd /opt/harbor
sudo cp harbor.yml.tmpl harbor.yml
sudo nano harbor.yml

Modifica estas claves (el resto puede quedarse con su valor por defecto):

hostname: registry.your_domain

http:
  port: 80

https:
  port: 443
  certificate: /etc/letsencrypt/live/registry.your_domain/fullchain.pem
  private_key: /etc/letsencrypt/live/registry.your_domain/privkey.pem

harbor_admin_password: your_admin_password

database:
  password: your_db_password

data_volume: /data
  • hostname: el nombre con el que accederán los clientes. Debe coincidir con el certificado.
  • harbor_admin_password: contraseña inicial del usuario admin. Usa una robusta; podrás cambiarla desde la interfaz.
  • database.password: contraseña de la base de datos PostgreSQL interna. Cambia el valor por defecto de la plantilla.
  • data_volume: directorio del host donde Harbor guarda imágenes, base de datos y configuración generada.

Como el archivo contiene contraseñas, restringe sus permisos:

sudo chmod 600 harbor.yml

Paso 4: Instalar Harbor con Trivy

El script install.sh genera la configuración a partir de harbor.yml (con el script prepare), descarga las imágenes y arranca los contenedores con Docker Compose. La opción --with-trivy añade el escáner de vulnerabilidades:

sudo ./install.sh --with-trivy

El proceso tarda unos minutos. Al final verás:

[Step 5]: starting Harbor ...
...
----Harbor has been installed and started successfully.----

Comprueba que todos los contenedores están en marcha y sanos:

sudo docker compose ps

Todos los servicios (harbor-core, harbor-db, harbor-portal, nginx, redis, registry, trivy-adapter y otros) deben aparecer como Up y, cuando lo tengan, (healthy). Comprueba también la API de salud:

curl -s https://registry.your_domain/api/v2.0/health
{"status":"healthy","components":[{"name":"core","status":"healthy"}, ...]}

Los contenedores de Harbor usan la política restart: always, así que vuelven a arrancar solos cuando se reinicia el servidor.

Paso 5: Crear un proyecto privado

Abre https://registry.your_domain e inicia sesión como admin con la contraseña de harbor_admin_password. En Harbor, las imágenes se organizan en proyectos, que son la unidad de permisos.

  1. Ve a Projects > New Project.
  2. Escribe myproject como nombre y deja Access Level sin marcar como Public, para que sea privado.
  3. Pulsa OK.
  4. Entra en el proyecto, abre Configuration y marca Automatically scan images on push. Opcionalmente marca Prevent vulnerable images from running con severidad Critical para impedir que se descarguen imágenes con vulnerabilidades críticas.
  5. Guarda los cambios.

El proyecto library que ya existe es público; no subas a él imágenes internas.

Para las personas, crea usuarios en Administration > Users y añádelos al proyecto en Members con el rol adecuado: Developer puede subir y bajar imágenes, Guest solo descargarlas y Maintainer además gestiona la configuración del proyecto.

Paso 6: Subir la primera imagen

Desde tu equipo o cualquier servidor con Docker, inicia sesión en el registro:

docker login registry.your_domain
Login Succeeded

Descarga una imagen pequeña, etiquétala con la ruta del proyecto y súbela:

docker pull alpine:3.20
docker tag alpine:3.20 registry.your_domain/myproject/alpine:3.20
docker push registry.your_domain/myproject/alpine:3.20
The push refers to repository [registry.your_domain/myproject/alpine]
...
3.20: digest: sha256:... size: 528

En la interfaz, entra en Projects > myproject > Repositories: aparece myproject/alpine con la etiqueta 3.20. Como activaste el análisis al subir, la columna Vulnerabilities mostrará el resultado de Trivy al cabo de unos segundos. La primera vez tarda más porque Trivy descarga su base de datos de vulnerabilidades.

Comprueba la descarga desde otra máquina, o borrando antes la copia local:

docker image rm registry.your_domain/myproject/alpine:3.20
docker pull registry.your_domain/myproject/alpine:3.20

Paso 7: Crear una cuenta robot para CI

No uses cuentas personales en los pipelines. Harbor ofrece cuentas robot con permisos acotados a un proyecto:

  1. En Projects > myproject > Robot Accounts, pulsa New Robot Account.
  2. Escribe ci como nombre, elige una caducidad y marca los permisos de Repository Push y Pull.
  3. Pulsa Add y copia el secreto: solo se muestra una vez.

El nombre completo de la cuenta tiene el formato robot$myproject+ci. Como contiene $, escríbelo entre comillas simples para que el shell no lo interprete. En CI, guarda el secreto en una variable protegida y úsalo así:

echo "$HARBOR_ROBOT_SECRET" | docker login registry.your_domain -u 'robot$myproject+ci' --password-stdin

Paso 8: Renovar el certificado automáticamente

Certbot instala un temporizador que intenta renovar los certificados dos veces al día. En este montaje hay dos detalles: el modo standalone necesita el puerto 80, que ahora ocupa Harbor, y Harbor copia el certificado a su propia configuración al ejecutar prepare, así que tras renovarlo hay que regenerarla. Resuélvelo con scripts de hook, que Certbot solo ejecuta cuando realmente va a renovar.

Crea el hook previo, que detiene Harbor:

sudo nano /etc/letsencrypt/renewal-hooks/pre/harbor-stop.sh
#!/usr/bin/env bash
set -euo pipefail
cd /opt/harbor
docker compose down

Y el hook posterior, que regenera la configuración con el certificado nuevo y vuelve a arrancar Harbor:

sudo nano /etc/letsencrypt/renewal-hooks/post/harbor-start.sh
#!/usr/bin/env bash
set -euo pipefail
cd /opt/harbor
./prepare
docker compose up -d

Hazlos ejecutables y prueba la renovación en seco:

sudo chmod +x /etc/letsencrypt/renewal-hooks/pre/harbor-stop.sh /etc/letsencrypt/renewal-hooks/post/harbor-start.sh
sudo certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/registry.your_domain/fullchain.pem (success)

Durante la prueba Harbor se detiene y arranca de nuevo; comprueba después con sudo docker compose ps en /opt/harbor que todos los servicios han vuelto. La renovación real provoca una parada de uno o dos minutos cada 60 días aproximadamente.

Mantenimiento

Borrar etiquetas en la interfaz no libera espacio en disco: las capas quedan huérfanas hasta que se ejecuta la recolección de basura. Prográmala en Administration > Clean Up > Garbage Collection, por ejemplo semanalmente. Para que no se acumulen etiquetas antiguas, define en cada proyecto una Tag Retention (por ejemplo, conservar las 10 etiquetas más recientes por repositorio).

Para hacer una copia de seguridad coherente, detén Harbor y copia el directorio de datos junto con la configuración:

cd /opt/harbor
sudo docker compose down
sudo tar -czf "/var/backups/harbor-$(date +%F).tar.gz" /data /opt/harbor/harbor.yml
sudo docker compose up -d

Los logs de todos los componentes están en /var/log/harbor/. Para un servicio concreto, usa sudo docker compose logs core (o registry, nginx, trivy-adapter) desde /opt/harbor.

Solución de problemas

certbot falla con Could not bind TCP port 80: algo escucha ya en el puerto 80, normalmente Harbor o un servidor web. Comprueba con sudo ss -ltnp 'sport = :80' y detén el servicio que lo ocupe.

docker login devuelve x509: certificate signed by unknown authority o un error de nombre: el hostname de harbor.yml no coincide con el nombre del certificado, o estás usando la IP en lugar del dominio.

docker push falla con unauthorized: unauthorized to access repository: el usuario no es miembro del proyecto con rol Developer o superior, o la ruta de la imagen no incluye el nombre del proyecto.

Después de cambiar harbor.yml no se aplica nada: los cambios solo se aplican al regenerar la configuración. Ejecuta sudo docker compose down, sudo ./prepare y sudo docker compose up -d desde /opt/harbor.

Conclusión

Tienes un registro Harbor privado en Ubuntu 24.04, servido con HTTPS y renovación automática del certificado, que analiza cada imagen con Trivy al subirla y ofrece cuentas robot para tus pipelines. Como siguientes pasos, puedes hacer que tu pipeline de Jenkins o GitLab CI publique las imágenes en Harbor, configurar la replicación con otro registro en Administration > Registries y Replications, o usar Harbor como caché de Docker Hub creando un proyecto de tipo Proxy Cache.