- homelab-documentation.md: Complete infrastructure docs - CLAUDE.md: Claude Code guidance - README.md: Quick reference Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
15 KiB
Mikkel's Homelab Infrastructure
Overview
This document describes the complete homelab infrastructure spanning Hetzner cloud servers, home NAS, and future expansion plans.
Core Infrastructure
core.georgsen.dk (Primary Server)
| Attribute | Value |
|---|---|
| Provider | Hetzner (AX52) |
| Location | Helsinki, Finland |
| CPU | AMD Ryzen 7 3700X |
| RAM | 64GB ECC |
| Storage | 2x 1TB NVMe (RAID0) - ZFS (rpool) |
| OS | Proxmox VE |
| Public IP | 65.108.14.165/26 |
| IPv6 | 2a01:4f9:6a:4a4f::/64 |
Warning: Storage is RAID0 — no local redundancy. PBS backups to Synology are critical.
Network Bridges
| Bridge | Purpose | Subnet | Gateway |
|---|---|---|---|
| vmbr0 | Public internet | 65.108.14.165/26 | 65.108.14.129 |
| vmbr1 | Internal services | 10.5.0.0/24 | 10.5.0.254 |
| vmbr2 | Hetzner vSwitch | 10.9.1.0/24 | 10.9.1.1 |
vSwitch Details
- Cloud Network: #11828854
- Subnet: 10.9.1.0/24
- Routes to: 10.9.0.0/16 via 10.9.1.1
- MTU: 1400
Network Configuration
/etc/network/interfaces (core)
auto lo
iface lo inet loopback
auto enp9s0
iface enp9s0 inet manual
auto enp9s0.4000
iface enp9s0.4000 inet manual
mtu 1400
auto vmbr0
iface vmbr0 inet static
address 65.108.14.165/26
gateway 65.108.14.129
bridge-ports enp9s0
bridge-stp off
bridge-fd 0
iface vmbr0 inet6 static
address 2a01:4f9:6a:4a4f::/64
gateway fe80::1
auto vmbr1
iface vmbr1 inet static
address 10.5.0.254/24
bridge-ports none
bridge-stp off
bridge-fd 0
auto vmbr2
iface vmbr2 inet static
address 10.9.1.2/24
bridge-ports enp9s0.4000
bridge-stp off
bridge-fd 0
mtu 1400
up ip route add 10.9.0.0/16 via 10.9.1.1 dev vmbr2
down ip route del 10.9.0.0/16 via 10.9.1.1 dev vmbr2
Port Forwarding (iptables)
| External Port | Internal Destination | Purpose |
|---|---|---|
| 80 | 10.5.0.1:80 | NPM HTTP |
| 443 | 10.5.0.1:443 | NPM HTTPS |
NAT masquerade enabled for 10.5.0.0/24 → vmbr0
DHCP (dnsmasq)
- Range: 10.5.0.100 - 10.5.0.200
- Lease time: 24h
Virtual Machines
VM 200: mail.georgsen.dk (Stalwart Mail Server)
| Attribute | Value |
|---|---|
| Type | VM (KVM) |
| IP | 65.108.14.164 (dedicated public IP) |
| Bridge | vmbr0 (direct) |
| Software | Stalwart Mail Server |
| Webmail | Snappymail (via dockge) |
Current domains: dataloes.dk (building reputation before adding more)
Planned domains: georgsen.dk, microsux.dk, dataloes.dk
LXC Containers
Container Overview
| VMID | Name | IP | Purpose | Status |
|---|---|---|---|---|
| 100 | npm | 10.5.0.1 | Nginx Proxy Manager | Running |
| 101 | dockge | 10.5.0.10 | Docker Compose Manager | Running |
| 102 | mgmt | 10.5.0.102 | Management/Automation (Claude Code) | Running |
| 103 | postgresql01 | DHCP | PostgreSQL (community) | Running |
| 104 | redis01 | DHCP | Redis (community) | Running |
| 105 | sentry | DHCP | Defense Intelligence System | Running |
| 106 | pbs | 10.5.0.6 | Proxmox Backup Server | Running |
| 107 | pve-scripts-local | DHCP | Community Scripts Web UI | Running |
| 108 | jukebox | DHCP (→10.5.0.184) | Music Player (custom project) | Running |
| 110 | sense.microsux.dk | DHCP | CBD Vendor Locator | Stopped |
| 111 | dev | DHCP | Development container | Running |
| 112 | dataloes | 10.5.0.112 | dataloes.dk website | Stopped |
| 113 | general | 10.5.0.113 | Decomissioned | Stopped |
| 114 | forgejo | 10.5.0.14 | Git server (Forgejo) | Running |
| 115 | dns | 10.5.0.2 | DNS server (Technitium) | Running |
| 1000 | tailscale | 10.5.0.x + 10.9.1.10 | Tailscale relay | Running |
Container Details
100: NPM (Nginx Proxy Manager)
- Purpose: Reverse proxy with automatic Let's Encrypt certificates
- IP: 10.5.0.1
- Ports: 80, 443, 81 (admin)
- SSL: Let's Encrypt (HTTP challenge)
Proxy Hosts:
| Domain | Destination | SSL |
|---|---|---|
| dataloes.dk, www.dataloes.dk | http://10.5.0.112:80 | Let's Encrypt |
| dev01.georgsen.dk | http://10.5.0.113:3001 | Let's Encrypt |
| dns.georgsen.dk | http://10.5.0.2:5380 | Let's Encrypt |
| dockge.georgsen.dk | http://10.5.0.10:5001 | Let's Encrypt |
| git.georgsen.dk | http://10.5.0.14:3000 | Let's Encrypt |
| jukebox.georgsen.dk | http://10.5.0.184:4000 | Let's Encrypt |
| pbs.georgsen.dk | https://10.5.0.6:8007 | Let's Encrypt |
| status.georgsen.dk | http://10.5.0.10:3001 | Let's Encrypt |
| webmail.georgsen.dk | http://10.5.0.10:8888 | Let's Encrypt |
101: Dockge
- Purpose: Docker Compose stack management
- IP: 10.5.0.10
- Port: 5001
Running Stacks:
# Snappymail (webmail for Stalwart)
services:
snappymail:
image: djmaze/snappymail:latest
container_name: snappymail
restart: unless-stopped
ports:
- 8888:8888
volumes:
- ./data:/var/lib/snappymail
# Uptime Kuma (status monitoring)
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- 3001:3001
volumes:
- ./data:/app/data
105: Sentry (Defense Intelligence)
- Purpose: Custom defense intelligence system for monitoring military contracting opportunities
- Focus: Ground equipment (EV conversion, diesel upgrades, specialty vehicles, bomb loaders, Humvee engines)
- Databases: Uses postgresql01 and redis01
106: PBS (Proxmox Backup Server)
- Purpose: Centralized backup for all PVE nodes
- IP (vmbr1): 10.5.0.6
- Tailscale IP: 100.115.85.120
- Web UI: https://10.5.0.6:8007
Configuration:
- Container type: Privileged (required for NFS/CIFS)
- Features: nesting=1
- AppArmor: unconfined
- TUN device enabled (for Tailscale)
Datastore:
- Name:
synology - Path:
/mnt/synology/datastore - Backend: Synology NAS via CIFS over Tailscale
Namespaces:
core.georgsen.dkpve01.warradejendomme.dkpve02.warradejendomme.dk
Retention Policy:
- Keep Daily: 7
- Keep Weekly: 4
- Keep Monthly: 3
Schedule:
- Prune: 21:00
- GC: 22:30
- core backup: 01:00
- pve01 backup: 01:30
- pve02 backup: 02:00
108: JukeBox
- Purpose: Custom music player
- Source: https://github.com/mikl0s/JukeBox
- Port: 4000
114: Forgejo (Git Server)
- Purpose: Self-hosted Git server (GitHub alternative)
- IP: 10.5.0.14
- Port: 3000
- URL: https://git.georgsen.dk
- Software: Forgejo 10.0.1
- Storage: 60GB
115: Technitium DNS
- Purpose: Local DNS server with web UI and API
- IP: 10.5.0.2
- Ports: 53 (DNS), 5380 (Web UI)
- URL: https://dns.georgsen.dk
- Features: Authoritative DNS for internal zone, upstream forwarder
1000: Tailscale
- Purpose: Tailscale access to vmbr1 and vmbr2 networks
- IPs: vmbr1 (DHCP) + 10.9.1.10 (vmbr2)
- Tailscale IP: 100.99.33.100
Backup Architecture
┌─────────────────────────────────────────────────────────────────┐
│ BACKUP FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ core.georgsen.dk ────────┐ │
│ (01:00 daily) │ │
│ ▼ │
│ pve01.warradejendomme.dk ──► PBS (10.5.0.6) │
│ (01:30 daily) │ │ │
│ │ │ CIFS over Tailscale │
│ pve02.warradejendomme.dk ┘ ▼ │
│ (02:00 daily) Synology NAS │
│ (100.105.26.130) │
│ │
│ Retention: 7 daily, 4 weekly, 3 monthly │
│ │
└─────────────────────────────────────────────────────────────────┘
PBS Container Config (/etc/pve/lxc/106.conf)
arch: amd64
cores: 2
features: nesting=1
hostname: pbs
memory: 2048
net0: name=eth0,bridge=vmbr1,gw=10.5.0.254,hwaddr=BC:24:11:BF:BD:74,ip=10.5.0.6/24,type=veth
onboot: 1
ostype: debian
rootfs: local-zfs:subvol-106-disk-0,size=50G
swap: 512
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
lxc.cap.drop:
lxc.apparmor.profile: unconfined
PBS fstab (/etc/fstab on PBS)
//100.105.26.130/pbs-backup /mnt/synology cifs credentials=/etc/pbs-smb.creds,uid=34,gid=34,vers=3.0,_netdev,x-systemd.automount,x-systemd.after=tailscaled.service,x-systemd.requires=tailscaled.service 0 0
PBS Systemd Dependencies
# /etc/systemd/system/proxmox-backup-proxy.service.d/wait-for-mount.conf
[Unit]
After=mnt-synology.mount tailscaled.service
Requires=mnt-synology.mount
# /etc/systemd/system/proxmox-backup.service.d/wait-for-mount.conf
[Unit]
After=mnt-synology.mount tailscaled.service
Requires=mnt-synology.mount
Synology NFS/CIFS Settings
- Share:
/volume1/pbs-backup - Protocol: CIFS (SMB 3.0)
- Squash: Map all users to admin
- Tailscale IP allowed: 100.115.85.120
Other PVE Nodes
pve01.warradejendomme.dk
- Purpose: Friend's server (former job), needs backups
- Backup namespace:
pve01.warradejendomme.dk - PBS connection: Via Tailscale (100.115.85.120:8007)
pve02.warradejendomme.dk
- Purpose: Friend's server (former job), needs backups
- Backup namespace:
pve02.warradejendomme.dk - PBS connection: Via Tailscale (100.115.85.120:8007)
Tailscale Network
| Device | Tailscale IP | Notes |
|---|---|---|
| pbs-1 (PBS) | 100.115.85.120 | PBS container |
| nas01 (Synology) | 100.105.26.130 | Home NAS, backup target |
| tailscale-pve01 | 100.99.33.100 | core's Tailscale LXC |
| pve01 | 100.99.118.54 | Friend's server |
| pve02 | 100.82.87.108 | Friend's server |
| dev | 100.85.227.17 | |
| sentry | 100.83.236.113 | |
| mge-t14 | 100.112.71.15 | |
| mikflix | 100.84.253.6 | |
| xanderryzen | 100.71.118.78 | |
| nvr01 | 100.118.17.103 | Exit node |
| tailscalemg | 100.115.101.65 | Exit node |
Tailscale config: SSH enabled on all devices where possible
DNS
External DNS
Provider: dns.services (Danish, free, has API for future automation)
Domains:
- georgsen.dk
- dataloes.dk
- microsux.dk
- warradejendomme.dk
Internal DNS (Technitium)
- Server: 10.5.0.2
- Web UI: https://dns.georgsen.dk or http://10.5.0.2:5380
- API: http://10.5.0.2:5380/api/
- Internal zone: lab.georgsen.dk
- Upstream: Cloudflare (1.1.1.1), Google (8.8.8.8), Quad9 (9.9.9.9)
Internal DNS Records (lab.georgsen.dk):
| Name | IP |
|---|---|
| npm | 10.5.0.1 |
| dns | 10.5.0.2 |
| pbs | 10.5.0.6 |
| dockge | 10.5.0.10 |
| forgejo | 10.5.0.14 |
| mgmt | 10.5.0.102 |
Home Infrastructure
Synology NAS (nas01)
- Tailscale IP: 100.105.26.130
- Purpose: Backup target, general NAS
- PBS Share: /volume1/pbs-backup
- Note: To be replaced in 2026 (noisy)
Future: Intel NUC Cluster (3 nodes)
Planned home PVE cluster:
| Node | RAM | NVMe | SATA SSD | HDD |
|---|---|---|---|---|
| Node 1 | 16GB | 500GB | 512GB | - |
| Node 2 | 16GB | 500GB | 512GB | - |
| Node 3 | 16GB | 2TB | 512GB | 5TB (CCTV) |
User Conventions
Standard User Setup
When creating containers/VMs:
# Create group and user with UID/GID 1000
groupadd -g 1000 georgsen
useradd -u 1000 -g 1000 -m -s /bin/bash mikkel
# Passwordless sudo
apt install -y sudo
echo "mikkel ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/mikkel
chmod 440 /etc/sudoers.d/mikkel
# SSH key
mkdir -p /root/.ssh /home/mikkel/.ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIOQrK06zVkfY6C1ec69kEZYjf8tC98icCcBju4V751i mikkel@georgsen.dk" | tee /root/.ssh/authorized_keys /home/mikkel/.ssh/authorized_keys
chmod 700 /root/.ssh /home/mikkel/.ssh
chmod 600 /root/.ssh/authorized_keys /home/mikkel/.ssh/authorized_keys
chown -R mikkel:georgsen /home/mikkel/.ssh
Projects
Sentry (Defense Intelligence)
Custom system monitoring military contracting news for business opportunities.
- Focus: Ground equipment (NOT aircraft/naval)
- Services: EV conversion, diesel upgrades, specialty vehicles, bomb loaders, Humvee engine upgrades
JukeBox
Custom music player: https://github.com/mikl0s/JukeBox
Sense Vendor Locator
CBD retailer locator for Denmark (sense.microsux.dk)
dataloes.dk
Personal company website
Preferences
- Coding: Python, Batch
- UI Style: 256-color terminal retro aesthetic
- Development: Ask clarifying questions, prefer understanding over workarounds
- Tools: Claude Code for development projects
Maintenance Notes
Cleanup Tasks
-
Remove orphaned iptables rules (Coolify is gone):
iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 6001 -j DNAT --to-destination 10.5.0.1:6001 iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 6002 -j DNAT --to-destination 10.5.0.1:6002 netfilter-persistent save -
Containers to evaluate:
- 110 (sense.microsux.dk) - Consider consolidating
- 112 (dataloes) - Stopped
- 113 (general) - Decomissioned, can remove
-
DHCP vs Static IPs:
- Containers .112 and .113 have static IPs inside DHCP range (100-200)
- Consider moving static assignments to .2-.99 range
Quick Reference
Service URLs
| Service | URL |
|---|---|
| PVE (core) | https://65.108.14.165:8006 |
| PBS | https://pbs.georgsen.dk or https://10.5.0.6:8007 |
| NPM Admin | http://10.5.0.1:81 |
| Dockge | https://dockge.georgsen.dk |
| DNS Admin | https://dns.georgsen.dk or http://10.5.0.2:5380 |
| Forgejo | https://git.georgsen.dk or http://10.5.0.14:3000 |
| Status | https://status.georgsen.dk |
| Webmail | https://webmail.georgsen.dk |
| JukeBox | https://jukebox.georgsen.dk |
Important IPs
| Resource | IP |
|---|---|
| core public | 65.108.14.165 |
| mail public | 65.108.14.164 |
| NPM | 10.5.0.1 |
| DNS | 10.5.0.2 |
| PBS | 10.5.0.6 |
| Dockge | 10.5.0.10 |
| Forgejo | 10.5.0.14 |
| Synology (Tailscale) | 100.105.26.130 |
| PBS (Tailscale) | 100.115.85.120 |
PBS Credentials
- Web UI: root@pam
- API Token: root@pam!pve
- Fingerprint: f6:5d:3f:63:0e:15:38:10:38:7a:33:82:3a:c0:b0:59:98:a7:04:7d:8d:ed:64:7d:fc:85:ab:8f:87:a4:48:eb
PVE API (from mgmt container)
- Token: root@pam!mgmt
- Config: ~/.config/pve/credentials
- Helper: ~/bin/pve (list, status, start, stop, create-ct)
NPM API (from mgmt container)
- Config: ~/repos/nginx-proxy-manager-Bash-API/npm-api.conf
- Helper: ~/bin/npm-api (--host-list, --host-create, --host-delete, --cert-list)
DNS API (from mgmt container)
- Config: ~/.config/dns/credentials
- Helper: ~/bin/dns (list, records, add, delete, lookup)
- Default zone: lab.georgsen.dk
Last updated: 2026-01-14