added nexus package manager

This commit is contained in:
rezayaghobi-dev
2026-08-27 14:02:31 +03:30
parent 00836e5b7c
commit e02f7b4bce
5 changed files with 250 additions and 0 deletions

View File

@@ -13,6 +13,8 @@ A self-hosted homelab running on Docker Compose, built and maintained on an Ubun
![K3s](https://img.shields.io/badge/K3s-FFC61C?style=flat&logo=k3s&logoColor=white) ![K3s](https://img.shields.io/badge/K3s-FFC61C?style=flat&logo=k3s&logoColor=white)
![Vagrant](https://img.shields.io/badge/Vagrant-1868F2?style=flat&logo=vagrant&logoColor=white) ![Vagrant](https://img.shields.io/badge/Vagrant-1868F2?style=flat&logo=vagrant&logoColor=white)
![GitLab](https://img.shields.io/badge/GitLab-FC6D26?style=flat&logo=gitlab&logoColor=white) ![GitLab](https://img.shields.io/badge/GitLab-FC6D26?style=flat&logo=gitlab&logoColor=white)
![Traefik](https://img.shields.io/badge/Traefik-36CFD1?style=flat&logo=traefik&logoColor=white)
![Nexus](https://img.shields.io/badge/Nexus-BC1E2D?style=flat&logo=sonatype&logoColor=white)
## Overview ## 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) | | **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) | | **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) | | **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) | | **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) | | **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) | | **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/ │ ├── vaultwarden/
│ │ ├── docker-compose.yml │ │ ├── docker-compose.yml
│ │ └── vw-data/ │ │ └── vw-data/
│ ├── nexus/
│ │ ├── compose.yml
│ │ └── .env.example
│ ├── nginx-proxy-manager/ │ ├── nginx-proxy-manager/
│ │ └── compose.yaml │ │ └── compose.yaml
│ ├── semaphore/ │ ├── semaphore/

View 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

View 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}"

View File

@@ -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) - [Reverse Proxy](Reverse-Proxy.md) (historical — Nginx Proxy Manager)
- [Traefik](Traefik.md) (current reverse proxy) - [Traefik](Traefik.md) (current reverse proxy)
- [Vaultwarden](Vaultwarden.md) (password manager) - [Vaultwarden](Vaultwarden.md) (password manager)
- [Nexus](Nexus.md) (Docker registry & artifact repository)
- [Semaphore](Semaphore.md) - [Semaphore](Semaphore.md)
- [DockScope](DockScope.md) - [DockScope](DockScope.md)
- [Ansible Playbooks](AnsiblePlaybooks.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 | | **Automation** | n8n + **Semaphore** (Ansible Web UI) + **Ansible** (update playbooks) || **Media** | Plex + Sonarr/Radarr/Prowlarr + qBittorrent |
| **Disk health** | Hard Disk Sentinel | | **Disk health** | Hard Disk Sentinel |
| **AI Agent** | OpenHands + Ollama | | **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** | | **CI/CD** | Self-hosted **GitLab** (Omnibus) + Docker-executor **Runner** |
| **Orchestration (K8s)** | Single-node **K3s** cluster, monitored via the same Prometheus/Grafana/Loki stack | | **Orchestration (K8s)** | Single-node **K3s** cluster, monitored via the same Prometheus/Grafana/Loki stack |

156
docs/Nexus.md Normal file
View 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)