# Server Introduction

Hardware specifications, storage layout, network topology, and the base Docker environment that underpins the entire Centerpoint stack.

# Overview & Hardware

Overview & Hardware
  tags: [hardware, centerpoint, specs]
---

## Overview

Centerpoint is the primary homelab server — a Mini PC form factor running a full
self-hosted Docker stack. It acts as the central compute node for all containerised
services, reverse proxying, media, AI inference, and automation in the homelab.

| Property     | Value                                  |
|--------------|----------------------------------------|
| Hostname     | `centerpoint`                          |
| Role         | Primary Docker host                    |
| Form Factor  | Mini PC (NUC-style)                    |
| IP Address   | `192.168.1.85`                         |
| Tailscale IP | `100.117.158.101`                      |
| OS           | Ubuntu 24.04.4 LTS (Noble Numbat)      |
| Kernel       | `6.17.0-35-generic`                    |

## CPU

| Property          | Value                            |
|-------------------|----------------------------------|
| Model             | Intel Core Ultra 9 285H          |
| Core Architecture | P-cores only (no hyperthreading) |
| Physical Cores    | 16                               |
| Sockets           | 1                                |
| Threads           | 16 (1 per core)                  |

The 285H is an Intel Meteor Lake H-series mobile processor with a dedicated Neural
Processing Unit (NPU). The absence of E-cores or hyperthreading means all 16 logical
CPUs are full performance cores, which benefits parallel container workloads.

## Memory

| Property  | Value |
|-----------|-------|
| Total RAM | 96 GB |

At time of writing, approximately 33 GB is actively in use with ~9 GB free and ~49 GB
used as page cache — normal for a long-running Linux system running a large Docker
stack.

## Notes

- Uptime is typically measured in weeks; the system runs continuously.
- The NPU in the 285H is not currently leveraged by any container workload — it is
  available for future OpenVINO or AI inference optimisation.

---
*Last Updated: 2026-06-16*

# Storage Layout

## Overview

Centerpoint uses a three-tier storage strategy:

1. **Local NVMe** — fast system and application data storage
2. **Ceph OSD block devices** — two NVMe drives contributing to a distributed Ceph
   cluster for object/block storage across the homelab
3. **NFS mounts from UnRAID** — bulk media and data storage from the NAS server at
   `192.168.1.119`

## Local NVMe Drives

### System Drive — `nvme1n1` (1.8 TB)

The primary system disk, GPT-partitioned with LVM.

| Partition              | Size   | Mount Point | Purpose              |
|------------------------|--------|-------------|----------------------|
| `nvme1n1p1`            | 1 GB   | `/boot/efi` | EFI System Partition |
| `nvme1n1p2`            | 2 GB   | `/boot`     | Boot partition       |
| `nvme1n1p3` (LVM PV)  | 1.8 TB | —           | LVM physical volume  |
| `ubuntu-vg/ubuntu-lv` | 1.8 TB | `/`         | Root filesystem      |

**Current usage:** 520 GB used / 1.3 TB free

This drive holds the OS, all Docker image layers (`/var/lib/docker`), container
volumes, and the compose project files under `/home/jeeves/docker/`.

### Ceph OSD — `nvme0n1` (931.5 GB)

Configured as a Ceph OSD block device under LVM management.
Mounted at `/media/jeeves/1TB_Vol2`.

The volume appears nearly empty at the filesystem level because Ceph manages the
block device directly — actual utilised capacity is tracked by the Ceph cluster,
not the OS mount point.

### Ceph OSD — `nvme2n1` (931.5 GB)

Second Ceph OSD block device, LVM-managed.
Mounted at `/media/jeeves/1TB_Vol1`. Shows ~115 GB used at the OS level; the
remainder is managed by Ceph.

> Both Ceph OSD drives contribute to a distributed storage pool shared across the
> homelab. Ceph provides replication and data protection at the cluster level rather
> than at the individual host level.

## NFS Mounts — UnRAID (`192.168.1.119`)

UnRAID at `192.168.1.119` hosts a 38 TB storage pool (~32 TB in use) and exports
three NFS shares, auto-mounted on Centerpoint at boot.

| Mount Point       | NFS Source                               | Consumer Services                  |
|-------------------|------------------------------------------|------------------------------------|
| `/mnt/Photos`     | `192.168.1.119:/mnt/user/Photos`         | Immich                             |
| `/mnt/data`       | `192.168.1.119:/mnt/user/Data`           | General / miscellaneous            |
| `/mnt/Multimedia` | `192.168.1.119:/mnt/user/Multimedia`     | Plex, Jellyfin, Stash, Audiobookshelf |

These paths are bind-mounted into media containers — the media files themselves
are never stored locally on Centerpoint.

## Notes / Gotchas

- If UnRAID is offline or rebooting, NFS mounts will stall and any container with
  a bind mount into `/mnt/*` will hang or fail to start until the mount recovers.
- Docker named volumes (databases, config state, etc.) all reside on the system
  NVMe under `/var/lib/docker/volumes/` — these are **not** automatically backed up
  to UnRAID.
- The Ceph cluster should be monitored independently; degraded OSD status does not
  surface through Docker or the OS mounts in any obvious way.

---
*Last Updated: 2026-06-16*

# Networking

## Overview

Centerpoint is reachable via four distinct paths depending on the use case.

| Path              | Address / Domain                                        | Use Case                          |
|-------------------|---------------------------------------------------------|-----------------------------------|
| LAN (physical)    | `192.168.1.85`                                          | Direct IP access, management      |
| Tailscale VPN     | `100.117.158.101`                                       | Secure remote access              |
| Traefik (internal)| `*.home.local`                                          | Named HTTPS on LAN                |
| Traefik (external)| `*.jeeves5454.ddns.net` / `*.jeevesconsults.ca`         | Internet-facing HTTPS             |

## Physical Network

| Property         | Value                        |
|------------------|------------------------------|
| Interface        | `enp47s0`                    |
| IP Address       | `192.168.1.85/24`            |
| Default Gateway  | `192.168.1.1` (Unifi router) |
| Subnet           | `192.168.1.0/24`             |

The IP is assigned via DHCP with a static lease on the Unifi gateway, making it
functionally static.

## Tailscale Mesh VPN

Centerpoint runs as a Tailscale node and **exit node**, allowing remote devices to
route all traffic through the home network.

| Peer        | Tailscale IP      | Platform | Notes                           |
|-------------|-------------------|----------|---------------------------------|
| centerpoint | `100.117.158.101` | Linux    | This host — exit node           |
| corsec      | `100.101.27.107`  | Linux    | HA OS server — also exit node   |
| halcyon     | `100.81.166.2`    | Windows  | Active peer (direct connection) |
| Mobile      | varies            | iOS      | Occasional peers                |

## Docker Network Architecture

Docker maintains approximately 25 bridge networks on the host. Each application
stack follows a consistent isolation pattern:

| Network          | Purpose                                                          |
|------------------|------------------------------------------------------------------|
| `traefik-net`    | Shared bridge — all Traefik-fronted containers attach here       |
| `*-internal`     | Per-stack isolated networks for app-to-database communication    |
| `media-network`  | Shared bridge for media stack containers                         |
| `bridge`         | Docker default (not used for production workloads)               |

**Typical stack pattern:**
- Application container → attached to both `traefik-net` and `<stack>-internal`
- Database / cache sidecar → attached to `<stack>-internal` only (never on Traefik)

## DNS

**Internal (`*.home.local`):** Resolved by AdGuard Home on the LAN. A wildcard DNS
record points `*.home.local` → `192.168.1.85` so Traefik receives all requests and
routes by hostname.

**External (`*.jeeves5454.ddns.net`):** DDNS via No-IP, keeps the external hostname
updated with the home WAN IP.

**External (`*.jeevesconsults.ca`):** Managed via DNS provider with appropriate
A / CNAME records pointing to the home WAN.

## Notes / Gotchas

- If AdGuard Home is down, all `*.home.local` DNS resolution fails. Services remain
  accessible via direct IP but HTTPS by hostname will not work.
- Tailscale must be running (`tailscaled`) for the `100.x` address to be reachable.
- The large number of `br-*` interfaces visible on `ip addr` is normal — Docker
  creates one per network.
- Traefik handles all TLS termination; individual containers do not need to manage
  certificates.

---
*Last Updated: 2026-06-16*

# Docker Environment

## Overview

All services on Centerpoint run as Docker containers, managed through Docker Compose
project files and monitored via Portainer EE. Traefik v3 serves as the reverse proxy
and TLS termination point for every service.

## Docker Engine

| Property           | Value                 |
|--------------------|-----------------------|
| Docker Version     | 29.5.3                |
| Docker Compose     | v5.1.4                |
| Storage Driver     | `overlayfs`           |
| Docker Root        | `/var/lib/docker`     |
| Total Containers   | 112                   |
| Running            | 97                    |
| Stopped            | 15                    |
| Images             | 108                   |

## Project Structure

All compose projects live under `/home/jeeves/docker/`, with one subdirectory per
logical stack:

```
/home/jeeves/docker/
├── adguard/
├── ai-stack/         ← Ollama, Open Web UI, Faster-Whisper, Kokoro
├── arr/              ← Sonarr, Radarr, Prowlarr, Bazarr, NZBGet, etc.
├── authentik/
├── bookstack/
├── crowdsec/
├── homepage/
├── immich/
├── paperless/
├── traefik/
└── ...               (one directory per stack)
```

Each directory contains a `docker-compose.yml` and any local config files or
bind-mount targets specific to that stack.

## Container Management — Portainer EE

Portainer Enterprise Edition provides the web UI for container lifecycle management,
log viewing, stack deployment, and environment monitoring.

Portainer connects to the Docker daemon via a `dockerproxy` sidecar container
(Tecnativa Docker Socket Proxy) rather than mounting the Docker socket directly.
This limits the API surface exposed to Portainer and reduces the blast radius of
any container compromise.

## Traefik v3 — Reverse Proxy

Traefik is the single ingress point for all named HTTP/HTTPS traffic. It runs
permanently on `traefik-net` and discovers routes automatically from Docker container
labels — no manual reload required when stacks are added or removed.

| Property         | Value                                                      |
|------------------|------------------------------------------------------------|
| HTTP port        | `80` — auto-redirects all traffic to HTTPS                 |
| HTTPS port       | `443`                                                      |
| Dashboard port   | `8080` — internal only (`traefik.home.local`)              |
| Static config    | `/home/jeeves/docker/traefik/traefik.yml`                  |
| Dynamic config   | `/home/jeeves/docker/traefik/dynamic.yml` (file-watched)   |
| Access logs      | `/var/log/traefik/access.log` (JSON, buffered)             |

### Certificate Resolvers

| Resolver      | Scope              | Method         | Certificate Authority              |
|---------------|--------------------|----------------|------------------------------------|
| `letsencrypt` | External domains   | HTTP challenge | Let's Encrypt                      |
| `step-ca`     | `*.home.local`     | ACME           | Internal Step-CA (`ca.home.local`) |

Internal certificates have a 720-hour (30-day) duration and auto-renew via Traefik's
built-in ACME client against the Step-CA instance.

### Active Plugins

| Plugin                                          | Version | Purpose                                                  |
|-------------------------------------------------|---------|----------------------------------------------------------|
| `PascalMinder/geoblock`                         | v0.3.6  | Country-level block on external-facing routes            |
| `maxlerebourg/crowdsec-bouncer-traefik-plugin`  | v1.3.0  | Blocks IPs flagged by the local CrowdSec LAPI            |

### Standard Routing Pattern

Each service defines Traefik labels in its own `docker-compose.yml`. The typical
pattern for a dual-route service (internal + external) is:

```yaml
labels:
  - "traefik.enable=true"

  # External route — Let's Encrypt TLS + security middleware
  - "traefik.http.routers.<name>-ext.rule=Host(`<svc>.jeeves5454.ddns.net`)"
  - "traefik.http.routers.<name>-ext.entrypoints=websecure"
  - "traefik.http.routers.<name>-ext.tls.certresolver=letsencrypt"
  - "traefik.http.routers.<name>-ext.middlewares=authentik-auth@docker,plex-geoblock@file,crowdsec-bouncer@file"

  # Internal route — Step-CA TLS, no extra middleware
  - "traefik.http.routers.<name>-int.rule=Host(`<svc>.home.local`)"
  - "traefik.http.routers.<name>-int.entrypoints=websecure"
  - "traefik.http.routers.<name>-int.tls.certresolver=step-ca"

  # Backend service port
  - "traefik.http.services.<name>-svc.loadbalancer.server.port=<port>"
```

Services that are internal-only omit the `-ext` router entirely. Services that
require OAuth2 authentication on external routes add `authentik@file` middleware.

## Notes / Gotchas

- `traefik-net` is an **externally created** network and must exist before any
  Traefik-fronted stack is started:
  ```bash
  docker network create traefik-net
  ```
- Never restart `dockerproxy` while Portainer is actively being used — it will lose
  its Docker connection until the proxy is back up.
- Compose files use the stack subdirectory as their working directory — relative bind
  mount paths resolve from there.
- `overlayfs` can accumulate orphaned image layers over time. Prune periodically:
  ```bash
  docker image prune
  docker volume prune   # caution — only remove truly unused volumes
  ```

---
*Last Updated: 2026-06-16*