--- title: "Harbor Registry Setup Guide" description: "Step-by-step guide to deploying Harbor as a self-hosted Docker registry on a Proxmox LXC. Covers installation, NPM reverse proxy config, Gitea Actions integration, vulnerability scanning, backup strategy, and troubleshooting." type: guide domain: server-configs tags: [harbor, docker-registry, proxmox, lxc, gitea-actions, vulnerability-scanning, docker] --- # Harbor Docker Registry Setup Guide Complete guide to setting up Harbor on a Proxmox LXC for self-hosted Docker registry. ## Prerequisites - Proxmox LXC with Ubuntu 22.04 - 2 CPU cores, 4GB RAM, 50GB disk - Docker and docker-compose installed - Domain name (e.g., registry.manticorum.com) ## Quick Setup ### 1. Create LXC Container ```bash # On Proxmox host pct create 227 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst \ --hostname harbor \ --cores 2 \ --memory 4096 \ --swap 512 \ --net0 name=eth0,bridge=vmbr0,ip=10.10.0.227/24,gw=10.10.0.1 \ --rootfs local-lvm:50 \ --unprivileged 1 \ --features nesting=1 \ --onboot 1 \ --start 1 ``` ### 2. Install Docker ```bash ssh root@10.10.0.227 apt update && apt install -y curl curl -fsSL https://get.docker.com | sh systemctl enable docker ``` ### 3. Download Harbor ```bash cd /opt wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz tar xzvf harbor-offline-installer-v2.10.0.tgz cd harbor ``` ### 4. Configure Harbor ```bash cp harbor.yml.tmpl harbor.yml # Edit harbor.yml nano harbor.yml ``` **Key settings to change:** ```yaml hostname: registry.manticorum.com # Your domain # HTTPS (configure after NPM setup, start with HTTP for now) # https: # port: 443 # certificate: /path/to/cert # private_key: /path/to/key # Or disable HTTPS initially # Comment out entire https section harbor_admin_password: YourSecurePassword123 database: password: YourDBPassword123 data_volume: /mnt/harbor-data ``` ### 5. Install Harbor ```bash ./install.sh ``` ### 6. Access Harbor Open: `http://10.10.0.227` (or `http://registry.manticorum.com` if DNS configured) **Default login:** - Username: `admin` - Password: `YourSecurePassword123` (what you set) ### 7. Configure NPM Reverse Proxy In Nginx Proxy Manager (10.10.0.16): **Proxy Host:** - Domain: `registry.manticorum.com` - Scheme: `http` - Forward Hostname: `10.10.0.227` - Forward Port: `80` - Websockets: ✅ Enabled - Block Common Exploits: ✅ Enabled - SSL: Let's Encrypt **Custom Nginx Configuration:** ```nginx # Increase timeouts for large image uploads proxy_read_timeout 900; proxy_send_timeout 900; client_max_body_size 0; # No upload limit # Required for Docker registry 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; ``` ## Using Your Registry ### 1. Login from Dev Machine ```bash docker login registry.manticorum.com # Username: admin # Password: YourSecurePassword123 ``` ### 2. Tag and Push Image ```bash # Tag existing image docker tag manticorum67/paper-dynasty:latest registry.manticorum.com/paper-dynasty/bot:latest # Push to your registry docker push registry.manticorum.com/paper-dynasty/bot:latest ``` ### 3. Pull from Production ```bash # On sba-bots docker login registry.manticorum.com docker pull registry.manticorum.com/paper-dynasty/bot:latest ``` ### 4. Update docker-compose ```yaml services: paper-dynasty: # Old: image: manticorum67/paper-dynasty:latest # New: image: registry.manticorum.com/paper-dynasty/bot:latest ``` ## Integrating with Gitea Actions Update your workflow to push to both registries: ```yaml - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Harbor uses: docker/login-action@v3 with: registry: registry.manticorum.com username: ${{ secrets.HARBOR_USERNAME }} password: ${{ secrets.HARBOR_PASSWORD }} - name: Build and push uses: docker/build-push-action@v5 with: context: . push: ${{ github.ref == 'refs/heads/main' }} tags: | manticorum67/paper-dynasty:latest manticorum67/paper-dynasty:v${{ steps.meta.outputs.version }} registry.manticorum.com/paper-dynasty/bot:latest registry.manticorum.com/paper-dynasty/bot:v${{ steps.meta.outputs.version }} ``` ## Harbor Features ### Create Projects 1. Login to Harbor UI 2. Click **New Project** 3. Name: `paper-dynasty` 4. Access Level: Private or Public ### Enable Vulnerability Scanning 1. Go to **Administration** → **Interrogation Services** 2. Enable **Trivy** scanner 3. Set scan on push: ✅ Enabled Now images are auto-scanned for CVEs! ### Set Up Replication Replicate between Harbor and Docker Hub: 1. **Administration** → **Replications** 2. **New Replication Rule** - Name: `sync-to-dockerhub` - Source: Local - Destination: Docker Hub (add endpoint first) - Trigger: Event Based ### Garbage Collection Free up disk space from deleted images: 1. **Administration** → **Garbage Collection** 2. Schedule: Daily at 2 AM 3. Dry run first to see what would be deleted ## Backup Strategy ### What to Backup 1. **Harbor database** (PostgreSQL) 2. **Image storage** (`/mnt/harbor-data`) 3. **Configuration** (`/opt/harbor/harbor.yml`) ### Backup Script ```bash #!/bin/bash BACKUP_DIR="/mnt/backups/harbor" DATE=$(date +%Y%m%d) # Stop Harbor cd /opt/harbor docker-compose down # Backup database docker exec harbor-db pg_dumpall -U postgres > $BACKUP_DIR/harbor-db-$DATE.sql # Backup data (incremental) rsync -av /mnt/harbor-data/ $BACKUP_DIR/harbor-data/ # Backup config cp /opt/harbor/harbor.yml $BACKUP_DIR/harbor-config-$DATE.yml # Start Harbor docker-compose up -d # Keep last 7 days find $BACKUP_DIR -name "harbor-db-*.sql" -mtime +7 -delete ``` ## Monitoring ### Check Harbor Status ```bash cd /opt/harbor docker-compose ps # View logs docker-compose logs -f ``` ### Disk Usage ```bash du -sh /mnt/harbor-data # By project du -sh /mnt/harbor-data/docker/registry/v2/repositories/* ``` ### API Health Check ```bash curl -k https://registry.manticorum.com/api/v2.0/health ``` ## Troubleshooting ### "401 Unauthorized" on push **Problem:** Docker login not working **Solution:** ```bash # Clear old credentials rm ~/.docker/config.json # Login again docker login registry.manticorum.com ``` ### "413 Request Entity Too Large" **Problem:** Nginx upload limit **Solution:** Add to NPM custom config: ```nginx client_max_body_size 0; ``` ### Disk space full **Problem:** Old images filling disk **Solution:** ```bash # Run garbage collection cd /opt/harbor docker-compose exec core /harbor/garbage-collection.sh # Or via UI: Administration → Garbage Collection → Run Now ``` ### Can't pull from registry **Problem:** Firewall or network issue **Solution:** ```bash # Test connection telnet 10.10.0.227 80 # Check Harbor logs docker-compose logs registry ``` ## Advanced: High Availability For production-critical registries, set up HA: 1. Multiple Harbor instances 2. Shared storage (NFS, S3, Minio) 3. Load balancer in front 4. Database replication ## Cost Analysis **LXC Resources:** - CPU: 2 cores = $0 (spare capacity) - RAM: 4GB = $0 (spare capacity) - Disk: 50GB = $0 (local storage) - Bandwidth: Internal = $0 **Total ongoing cost: $0/month** **Docker Hub Pro alternative: $5/month** **Time investment:** - Setup: 2-3 hours - Maintenance: 30 min/month - Break-even: 3 months of learning value ## Next Steps 1. ✅ Set up Harbor on LXC 227 2. ✅ Configure NPM reverse proxy 3. ✅ Test push/pull from dev machine 4. ✅ Update one project to use Harbor 5. ✅ Set up Gitea Actions to push to both registries 6. ✅ Configure vulnerability scanning 7. ✅ Set up automated backups --- **Created:** 2026-02-04 **For:** Manticorum Home Lab **Reference:** Paper Dynasty as first use case