added nexus package manager
This commit is contained in:
@@ -13,6 +13,8 @@ A self-hosted homelab running on Docker Compose, built and maintained on an Ubun
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
@@ -36,6 +38,7 @@ This is a real, running homelab — not a demo. It's deployed on a home Ubuntu s
|
||||
| **OpenHands** | `docker-compose/openhands/` | Self-hosted AI coding agent (OpenHands/agent-canvas) backed by Ollama — see [AI Coding Agent](docs/AI-Agent.md) |
|
||||
| **Traefik** | `docker-compose/traefik/` | Reverse proxy with Docker daemon auto-discovery and full config-as-code — replaces Nginx Proxy Manager — see [Traefik](docs/Traefik.md) |
|
||||
| **Vaultwarden** | `docker-compose/vaultwarden/` | Self-hosted Bitwarden-compatible password manager — see [Vaultwarden](docs/Vaultwarden.md) |
|
||||
| **Nexus** | `docker-compose/nexus/` | Sonatype Nexus3 — Docker registry pull-through cache and artifact repository, fronted by Traefik — see [Nexus](docs/Nexus.md) |
|
||||
| **Semaphore** | `docker-compose/semaphore/` | Self-hosted Ansible UI (alternative to AWX) — see [Semaphore Guide](docs/Semaphore.md) |
|
||||
| **Ansible playbooks** | `ansible/update-playbooks/` | Shared update role (pull → recreate → health-check → prune) reused across every stack, run via Semaphore — see [Ansible Playbooks](docs/AnsiblePlaybooks.md) |
|
||||
| **Server provisioning** | `ansible/preparing-playbook/` | Full server preparation playbook (packages, hardening, Docker, Nexus/Traefik) tested against Vagrant first — see [Ansible README](ansible/README.md) |
|
||||
@@ -118,6 +121,9 @@ homelab/
|
||||
│ ├── vaultwarden/
|
||||
│ │ ├── docker-compose.yml
|
||||
│ │ └── vw-data/
|
||||
│ ├── nexus/
|
||||
│ │ ├── compose.yml
|
||||
│ │ └── .env.example
|
||||
│ ├── nginx-proxy-manager/
|
||||
│ │ └── compose.yaml
|
||||
│ ├── semaphore/
|
||||
|
||||
21
docker-compose/nexus/.env.example
Normal file
21
docker-compose/nexus/.env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# service image tag (latest stable as of Aug 2026)
|
||||
nexus_image_tag=3.90.5
|
||||
|
||||
# domain variables - internal, self-signed *.home.server (no public DNS, no ACME)
|
||||
nexus_main_domain=nexus.home.server
|
||||
nexus_docker_domain=docker.home.server
|
||||
|
||||
# restart policy
|
||||
restart_policy=always
|
||||
|
||||
# nexus admin password (first-login password; change it after setup)
|
||||
nexus_admin_pass=CHANGE_ME
|
||||
|
||||
# --- Resource tuning for an 8GB / DDR3 / 4-core host ---
|
||||
nexus_heap=1536m
|
||||
nexus_direct_mem=512m
|
||||
nexus_mem_limit=2560m
|
||||
nexus_mem_reservation=1536m
|
||||
|
||||
# Docker registry group repo's internal HTTP connector port (created in Nexus UI, see SETUP.md)
|
||||
nexus_docker_connector_port=5005
|
||||
64
docker-compose/nexus/compose.yml
Normal file
64
docker-compose/nexus/compose.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
networks:
|
||||
web_net:
|
||||
name: web_net
|
||||
external: true
|
||||
app_net:
|
||||
name: app_net
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
nexus_data:
|
||||
name: nexus_data
|
||||
external: false
|
||||
|
||||
services:
|
||||
nexus:
|
||||
image: sonatype/nexus3:${nexus_image_tag}
|
||||
container_name: nexus
|
||||
hostname: nexus
|
||||
restart: ${restart_policy}
|
||||
mem_limit: ${nexus_mem_limit}
|
||||
mem_reservation: ${nexus_mem_reservation}
|
||||
environment:
|
||||
NEXUS_SECURITY_INITIAL_PASSWORD: ${nexus_admin_pass}
|
||||
INSTALL4J_ADD_VM_PARAMS: >-
|
||||
-Xms${nexus_heap} -Xmx${nexus_heap}
|
||||
-XX:MaxDirectMemorySize=${nexus_direct_mem}
|
||||
-XX:+UseG1GC
|
||||
-XX:+UseContainerSupport
|
||||
-Djava.util.prefs.userRoot=/nexus-data/javaprefs
|
||||
volumes:
|
||||
- nexus_data:/nexus-data
|
||||
networks:
|
||||
- web_net
|
||||
- app_net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8081/service/rest/v1/status"]
|
||||
interval: 30s
|
||||
retries: 3
|
||||
timeout: 10s
|
||||
start_period: 90s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=web_net"
|
||||
|
||||
# --- Web UI / REST API - self-signed, no ACME (internal-only domain) ---
|
||||
- "traefik.http.routers.http-repo.rule=Host(`${nexus_main_domain}`)"
|
||||
- "traefik.http.routers.http-repo.entrypoints=web"
|
||||
- "traefik.http.routers.http-repo.service=repo"
|
||||
- "traefik.http.routers.repo.rule=Host(`${nexus_main_domain}`)"
|
||||
- "traefik.http.routers.repo.entrypoints=websecure"
|
||||
- "traefik.http.routers.repo.tls=true"
|
||||
- "traefik.http.routers.repo.service=repo"
|
||||
- "traefik.http.services.repo.loadBalancer.server.port=8081"
|
||||
|
||||
# --- Docker registry group repo (Docker Hub pull-through cache) ---
|
||||
# Port created INSIDE Nexus after first boot - see SETUP.md.
|
||||
- "traefik.http.routers.http-docker-repo.rule=Host(`${nexus_docker_domain}`)"
|
||||
- "traefik.http.routers.http-docker-repo.entrypoints=web"
|
||||
- "traefik.http.routers.http-docker-repo.service=docker-repo"
|
||||
- "traefik.http.routers.docker-repo.rule=Host(`${nexus_docker_domain}`)"
|
||||
- "traefik.http.routers.docker-repo.entrypoints=websecure"
|
||||
- "traefik.http.routers.docker-repo.tls=true"
|
||||
- "traefik.http.routers.docker-repo.service=docker-repo"
|
||||
- "traefik.http.services.docker-repo.loadBalancer.server.port=${nexus_docker_connector_port}"
|
||||
@@ -16,6 +16,7 @@ Documentation for a self-hosted homelab running entirely on Docker Compose. This
|
||||
- [Reverse Proxy](Reverse-Proxy.md) (historical — Nginx Proxy Manager)
|
||||
- [Traefik](Traefik.md) (current reverse proxy)
|
||||
- [Vaultwarden](Vaultwarden.md) (password manager)
|
||||
- [Nexus](Nexus.md) (Docker registry & artifact repository)
|
||||
- [Semaphore](Semaphore.md)
|
||||
- [DockScope](DockScope.md)
|
||||
- [Ansible Playbooks](AnsiblePlaybooks.md)
|
||||
@@ -36,6 +37,8 @@ Documentation for a self-hosted homelab running entirely on Docker Compose. This
|
||||
| **Automation** | n8n + **Semaphore** (Ansible Web UI) + **Ansible** (update playbooks) || **Media** | Plex + Sonarr/Radarr/Prowlarr + qBittorrent |
|
||||
| **Disk health** | Hard Disk Sentinel |
|
||||
| **AI Agent** | OpenHands + Ollama |
|
||||
| **Registry** | **Nexus3** (Docker Hub pull-through cache + artifact repo) |
|
||||
| **Passwords** | **Vaultwarden** (Bitwarden-compatible, 30+ char passwords) |
|
||||
| **CI/CD** | Self-hosted **GitLab** (Omnibus) + Docker-executor **Runner** |
|
||||
| **Orchestration (K8s)** | Single-node **K3s** cluster, monitored via the same Prometheus/Grafana/Loki stack |
|
||||
|
||||
|
||||
156
docs/Nexus.md
Normal file
156
docs/Nexus.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 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/](../docker-compose/nexus/) for the deployment files.
|
||||
|
||||
## Why
|
||||
|
||||
Docker Hub rate limits, regional access issues (see [GitLab CI/CD](GitLab-CICD.md) 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
|
||||
|
||||
```bash
|
||||
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):
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```bash
|
||||
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](GitLab-CICD.md)), 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](Home.md)
|
||||
Reference in New Issue
Block a user