Files
homelab/docs/Nexus.md
2026-08-27 14:02:31 +03:30

8.0 KiB

Nexus — Artifact Repository & Docker Registry

Sonatype Nexus3 serves as the homelab's self-hosted artifact repository and Docker registry pull-through cache, fronted by Traefik.

See docker-compose/nexus/ for the deployment files.

Why

Docker Hub rate limits, regional access issues (see GitLab CI/CD for the registry.gitlab.com geo-block), and the desire for a single place to host internal Docker images all pointed toward a local registry. Nexus3 does that plus more — it can proxy Docker Hub, host private images, and serve as a general Maven/npm/pypi repository if needed.

What it does

Capability Purpose
Docker Hub pull-through cache Containers pull from docker.home.server instead of Hub directly — avoids rate limits and works even when Hub is slow or blocked
Docker registry (group repo) Single endpoint that combines the Hub cache + any private repos
Artifact repository Maven, npm, pypi, raw, and other formats — ready for future use
Web UI Browse cached images, manage repositories, view connection status

Setup

First boot

cd docker-compose/nexus
cp .env.example .env   # edit with your values
docker compose up -d

Nexus takes 1-2 minutes to fully start (JVM warmup). The health check waits up to 90 seconds before checking.

Initial admin password

The admin password is set via NEXUS_SECURITY_INITIAL_PASSWORD in the .env file. After first login at https://nexus.home.server, Nexus prompts you to change it.

Important: The initial password in .env is only used on first boot. After that, Nexus stores its own password in its data volume. Changing the .env value afterward does nothing — you change the password through the Nexus UI or REST API.

Docker registry setup

The Docker registry requires creating a connector port inside Nexus after first boot:

  1. Log in to the Nexus web UI
  2. Go to Server Administration → Docker
  3. Create a HTTP connector on port 5005 (or whatever nexus_docker_connector_port is set to in .env)
  4. Create a Docker (proxy) repository pointing at Docker Hub
  5. Create a Docker (group) repository that includes the proxy repo
  6. The group repo is what clients pull from — its external URL is https://docker.home.server

Configuring Docker daemon on clients

Add the registry as an insecure registry (since it uses a self-signed cert):

{
  "insecure-registries": ["docker.home.server"]
}

Or use the --registry-mirror flag in the Docker daemon config (set via the Ansible docker role).

Architecture

┌──────────────────────────────────────────┐
│              Nexus3 (port 8081)          │
│                                          │
│  ┌─────────────┐  ┌──────────────────┐   │
│  │ Docker Hub  │  │ Docker Group     │   │
│  │ Proxy Repo  │◄─┤ (pull-through    │   │
│  │             │  │  cache + private)│   │
│  └─────────────┘  └───────┬──────────┘   │
│                           │              │
│  ┌────────────────────────┴──────────┐   │
│  │        HTTP Connector :5005       │   │
│  └────────────────────────┬──────────┘   │
└───────────────────────────┼──────────────┘
                            │
                   ┌────────┴────────┐
                   │  Traefik :443   │
                   │  docker.home.   │
                   │  server         │
                   └────────┬────────┘
                            │
                   ┌────────┴────────┐
                   │  Clients pull   │
                   │  from here      │
                   └─────────────────┘

Two Traefik routes

Nexus exposes two domains through Traefik:

Domain Backend Port Purpose
nexus.home.server 8081 Web UI and REST API
docker.home.server 5005 Docker registry (pull-through cache)

Both are routed via Traefik labels in the compose file — no dynamic config YAML needed since Nexus is a proper Docker container on web_net.

Resource usage

Nexus is the heaviest single container in the stack. Current tuning for an 8GB / DDR3 / 4-core host:

Setting Value Notes
Heap (-Xms/-Xmx) 1536m Below Sonatype's 8GB recommendation but reasonable
Direct memory 512m For blob store I/O
Memory limit 2560m Hard cap — container can't exceed this
Memory reservation 1536m Soft guarantee for scheduling

With the rest of the stack running, free -h typically shows ~3.3GB available. Nexus idles around 1-1.5GB after warmup.

Do not run Nexus alongside GitLab Omnibus — together they exceed the host's RAM. The Ansible provisioning playbook can set up either one; pick based on what you need more.

Networking

  • web_net — front-facing network shared with Traefik (HTTPS access)
  • app_net — internal network for app-to-registry communication (GitLab CI, other services that push/pull images)

The two-network setup keeps the registry accessible to internal services without exposing it through Traefik unnecessarily — though the Docker registry route IS exposed via Traefik for convenience.

Security notes

  • Admin password is set on first boot via environment variable, then managed through the Nexus UI
  • Self-signed TLS cert — clients need insecure-registries or equivalent trust configuration
  • The Docker registry endpoint is LAN-only via Pi-hole DNS
  • Nexus stores all data in a Docker named volume (nexus_data) — back this up regularly
  • REST API access is available at https://nexus.home.server/service/rest/ — protected by Nexus authentication

Backups

The nexus_data volume contains all repository data, configuration, and the database. To back up:

docker compose stop nexus
tar czf /backups/nexus-$(date +%Y%m%d).tar.gz -C /var/lib/docker/volumes/nexus_data _data
docker compose start nexus

For a hot backup (without stopping), use the Nexus REST API task system to create a backup task through the UI.

Troubleshooting

  • Slow startup — Nexus takes 1-2 minutes on first boot. The health check has a 90-second start_period before it starts checking. Be patient.
  • "Connection refused" on docker.home.server — the Docker registry connector port (5005) is created inside Nexus after first boot through the UI. Until that's done, the Docker registry route returns 500.
  • Memory pressure — if the host starts swapping, reduce nexus_heap and nexus_mem_limit. Nexus works fine with less memory if you're only using it as a Docker pull-through cache.
  • Geo-blocked registries — if you're proxying registry.gitlab.com and it's geo-blocked (see GitLab CI/CD), the proxy repo will fail. Use a different upstream or skip that proxy.

Lessons learned

  • Nexus's Docker registry requires a two-step setup: deploy the container first, then create the HTTP connector and repositories through the web UI. There's no way to fully automate the initial repo setup via environment variables — the REST API works but the UI is simpler for first-time setup.
  • Running Nexus alongside GitLab Omnibus on an 8GB host is not viable — both are JVM-based and hungry for memory. Pick one, or upgrade the hardware.
  • The pull-through cache pattern is genuinely useful even on a home network — it's not just about rate limits, but also about having a local copy when Hub is slow or you're working offline.

← Back to Home