Files
ServerInfrastructure/README.md
Alexander Doerflinger 2ffcbb5f60 Initial commit
2026-02-20 09:10:27 +01:00

683 lines
21 KiB
Markdown

# 🏠 Home Server — Docker Compose Stack
Self-hosted infrastructure running **Home Assistant**, **Gitea**, and **WireGuard VPN** behind **Nginx Proxy Manager** with **Let's Encrypt SSL** and **IONOS DynDNS**.
| Service | Domain | Internal Port | Purpose |
|---------|--------|--------------|---------|
| **Nginx Proxy Manager** | — (admin: `:81`) | 80, 443, 81 | Reverse proxy + SSL |
| **Home Assistant** | `ha.doerflingers.com` | 8123 | Smart home |
| **Gitea** | `git.doerflingers.com` | 3000 (web), 22 (SSH) | Git hosting |
| **WireGuard** | `home.doerflingers.com` | 51820/UDP | VPN |
---
## Table of Contents
1. [Hardware Requirements](#1-hardware-requirements)
2. [Prerequisites — Ubuntu 24.04 Setup](#2-prerequisites--ubuntu-2404-setup)
3. [IONOS DynDNS Setup](#3-ionos-dyndns-setup)
4. [FritzBox Port Forwarding](#4-fritzbox-port-forwarding)
5. [Project Setup](#5-project-setup)
6. [Launch the Stack](#6-launch-the-stack)
7. [Nginx Proxy Manager Configuration](#7-nginx-proxy-manager-configuration)
8. [Service-Specific Setup](#8-service-specific-setup)
9. [WireGuard Client Setup](#9-wireguard-client-setup)
10. [Backup & Restore](#10-backup--restore)
11. [Migration to New Hardware](#11-migration-to-new-hardware)
12. [Troubleshooting](#12-troubleshooting)
13. [Architecture Overview](#13-architecture-overview)
---
## 1. Hardware Requirements
This stack is designed to run on modest hardware. Here's the assessment for the target laptop:
| Resource | Available | Used (est.) | Status |
|----------|-----------|-------------|--------|
| CPU | i5-3210M (2C/4T, 2.5 GHz) | ~5-15% idle | ✅ |
| RAM | 3.7 GB + 3.7 GB Swap | ~1.3 GB | ✅ |
| Disk | 500 GB HDD | ~5 GB base + data | ✅ |
> **All services coexist without interference.** HTTP traffic is multiplexed by hostname through the reverse proxy, WireGuard uses a separate UDP port.
---
## 2. Prerequisites — Ubuntu 24.04 Setup
Since this is a fresh Ubuntu 24.04 install, we need to install all required packages from scratch.
### 2.1 System Update
```bash
sudo apt update && sudo apt upgrade -y
```
### 2.2 Install Essential Packages
```bash
sudo apt install -y \
ca-certificates \
curl \
gnupg \
lsb-release \
python3 \
python3-pip \
python3-venv \
git \
cron
```
### 2.3 Install Docker Engine
Follow the official Docker installation for Ubuntu. Do **not** use the `docker.io` snap package.
```bash
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine + Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
### 2.4 Post-Installation — Run Docker Without `sudo`
```bash
sudo usermod -aG docker $USER
newgrp docker
```
Verify the installation:
```bash
docker --version
docker compose version
```
### 2.5 Prevent Laptop Suspend on Lid Close
Since this is a laptop acting as a server, you want it to stay on when the lid is closed:
```bash
sudo sed -i 's/#HandleLidSwitch=suspend/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
sudo sed -i 's/#HandleLidSwitchExternalPower=suspend/HandleLidSwitchExternalPower=ignore/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind
```
### 2.6 (Optional) Enable WireGuard Kernel Module
WireGuard often needs the kernel module pre-loaded:
```bash
sudo apt install -y wireguard-tools
sudo modprobe wireguard
# Make it persistent across reboots
echo "wireguard" | sudo tee /etc/modules-load.d/wireguard.conf
```
---
## 3. IONOS DynDNS Setup
Your domains are registered at IONOS. Since your home internet has a dynamic IP address, you need DynDNS to keep the DNS records updated automatically.
### 3.1 Install the IONOS DynDNS Client
```bash
# Create a virtual environment (required on Ubuntu 24.04 which uses PEP 668)
sudo python3 -m venv /opt/dyndns-venv
sudo /opt/dyndns-venv/bin/pip install domain-connect-dyndns
```
### 3.2 Register Each Domain
You need to run the setup command **once per domain/subdomain**. Each will give you a URL to open in your browser to authorize IONOS.
```bash
# Domain for Home Assistant
sudo /opt/dyndns-venv/bin/domain-connect-dyndns setup --domain ha.doerflingers.com
# → A URL is printed. Open it in your browser.
# → Log in to IONOS, click "Erlauben" (Allow).
# → Copy the code shown and paste it back into the terminal.
# Domain for Gitea
sudo /opt/dyndns-venv/bin/domain-connect-dyndns setup --domain git.doerflingers.com
# → Same process: open URL, authorize, paste code.
# Domain for Home / VPN
sudo /opt/dyndns-venv/bin/domain-connect-dyndns setup --domain home.doerflingers.com
# → Same process: open URL, authorize, paste code.
```
### 3.3 Test the Update
```bash
sudo /opt/dyndns-venv/bin/domain-connect-dyndns update --all
```
You should see output like:
```
Read ha.doerflingers.com config.
IP x.x.x.x found in A record
New IP: x.x.x.x
A record up to date.
```
### 3.4 Set Up Automatic Updates via Cron
The cron job runs every 5 minutes to check for IP changes and update DNS if needed.
```bash
# Edit the root crontab
sudo crontab -e
```
Add this line at the bottom:
```cron
*/5 * * * * /usr/bin/flock -n /tmp/dyndns-update.lck /opt/dyndns-venv/bin/domain-connect-dyndns update --all >> /var/log/dyndns-update.log 2>&1
```
> **Note:** We use every 5 minutes instead of every 1 minute — it's unlikely your IP changes more frequently, and it reduces system load. The `flock` prevents overlapping runs.
### 3.5 Verify Cron is Running
```bash
sudo systemctl enable cron
sudo systemctl start cron
# Check it's scheduled
sudo crontab -l
```
---
## 4. FritzBox Port Forwarding
You need to forward specific ports from your FritzBox router to the laptop.
### 4.1 Find the Laptop's Local IP Address
```bash
ip addr show | grep "inet " | grep -v 127.0.0.1
```
Note the IP address (e.g., `192.168.178.XX`).
> **Tip:** In FritzBox, go to **Heimnetz → Netzwerk → [Your Laptop]** and enable **"Diesem Netzwerkgerät immer die gleiche IPv4-Adresse zuweisen"** (always assign the same IP) to prevent the IP from changing.
### 4.2 Configure Port Forwarding
In the FritzBox admin UI (`http://fritz.box`), go to **Internet → Freigaben → Portfreigaben** and add the following rules:
| Service | Protocol | External Port | Internal Port | Internal IP | Required |
|---------|----------|---------------|---------------|-------------|----------|
| HTTP | TCP | 80 | 80 | 192.168.178.XX | ✅ Yes (Let's Encrypt HTTP-01 challenge) |
| HTTPS | TCP | 443 | 443 | 192.168.178.XX | ✅ Yes (all web services) |
| WireGuard | UDP | 51820 | 51820 | 192.168.178.XX | ✅ Yes (VPN) |
| Gitea SSH | TCP | 2222 | 2222 | 192.168.178.XX | ⚡ Optional (for `git clone` via SSH) |
> **⚠️ Security Note:** Do **NOT** forward port 81. The Nginx Proxy Manager admin UI should only be accessible from your local network (or via WireGuard VPN).
---
## 5. Project Setup
### 5.1 Clone or Copy This Repository
```bash
# If using git
git clone <your-repo-url> ~/home-server
cd ~/home-server
# Or simply copy the files to your laptop
# and navigate to the directory
cd ~/home-server
```
### 5.2 Create Your Environment File
```bash
cp .env.example .env
```
Edit `.env` with your preferred editor:
```bash
nano .env
```
**Required changes:**
| Variable | What to set |
|----------|-------------|
| `TZ` | Your timezone (default: `Europe/Berlin`) |
| `WG_SERVERURL` | `home.doerflingers.com` (already set) |
| `WG_PEERS` | Comma-separated list of your VPN client names |
| `GITEA_SECRET_KEY` | Run `openssl rand -hex 32` and paste result |
| `GITEA_INTERNAL_TOKEN` | Run `openssl rand -hex 32` and paste result |
### 5.3 Set Script Permissions
```bash
chmod +x scripts/backup.sh scripts/restore.sh
```
---
## 6. Launch the Stack
### 6.1 Start All Services
```bash
docker compose up -d
```
This will pull all images (first run may take 5-10 minutes on a slow connection) and start the containers.
### 6.2 Verify Everything is Running
```bash
docker compose ps
```
Expected output:
```
NAME STATUS PORTS
gitea Up 0.0.0.0:2222->22/tcp, 3000/tcp
homeassistant Up 8123/tcp
nginx-proxy-manager Up (healthy) 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:81->81/tcp
wireguard Up 0.0.0.0:51820->51820/udp
```
### 6.3 Check Logs (if something goes wrong)
```bash
# All services
docker compose logs -f
# Specific service
docker compose logs -f homeassistant
docker compose logs -f gitea
docker compose logs -f wireguard
docker compose logs -f nginx-proxy-manager
```
---
## 7. Nginx Proxy Manager Configuration
### 7.1 Access the Admin UI
Open your browser and go to:
```
http://<laptop-local-ip>:81
```
Default credentials:
| Field | Value |
|-------|-------|
| Email | `admin@example.com` |
| Password | `changeme` |
> **You will be prompted to change these on first login. Do it immediately.**
### 7.2 Add Proxy Host for Home Assistant
1. Go to **Hosts → Proxy Hosts → Add Proxy Host**
2. Fill in the **Details** tab:
| Field | Value |
|-------|-------|
| Domain Names | `ha.doerflingers.com` |
| Scheme | `http` |
| Forward Hostname / IP | `homeassistant` |
| Forward Port | `8123` |
| Websockets Support | ✅ **Enable** (required for HA) |
3. Go to the **SSL** tab:
| Field | Value |
|-------|-------|
| SSL Certificate | Request a new SSL Certificate |
| Force SSL | ✅ Enable |
| HTTP/2 Support | ✅ Enable |
| Email for Let's Encrypt | your-email@example.com |
| Agree to ToS | ✅ Yes |
4. Click **Save**.
### 7.3 Add Proxy Host for Gitea
Same process, with these values:
| Field | Value |
|-------|-------|
| Domain Names | `git.doerflingers.com` |
| Scheme | `http` |
| Forward Hostname / IP | `gitea` |
| Forward Port | `3000` |
| Websockets Support | ✅ Enable |
SSL tab: same as above (request new cert, force SSL, HTTP/2).
### 7.4 Add Proxy Host for Home / Landing Page (Optional)
If you want `home.doerflingers.com` to point somewhere (e.g., a landing page or redirect to Home Assistant):
| Field | Value |
|-------|-------|
| Domain Names | `home.doerflingers.com` |
| Scheme | `http` |
| Forward Hostname / IP | `homeassistant` |
| Forward Port | `8123` |
Or use a **Redirection Host** to redirect `home.doerflingers.com``ha.doerflingers.com`.
> **Note:** `home.doerflingers.com` is primarily used as the WireGuard server URL. It doesn't need a proxy host unless you want it to serve a web page too.
---
## 8. Service-Specific Setup
### 8.1 Home Assistant — First Launch
1. Navigate to `https://ha.doerflingers.com` (or `http://<laptop-ip>:8123` from LAN).
2. The onboarding wizard will guide you through:
- Creating your admin account
- Setting your home location
- Discovering devices on your network
3. Home Assistant is ready to use!
> **Note on device discovery:** In container mode (non-host network), some discovery protocols (mDNS, SSDP) may not work. You can add integrations manually via **Settings → Devices & Services → Add Integration**.
### 8.2 Gitea — First Launch
1. Navigate to `https://git.doerflingers.com`.
2. The initial configuration page will appear. Most settings are pre-configured via environment variables. Verify:
| Setting | Value |
|---------|-------|
| Site Title | Your choice |
| Repository Root Path | `/data/git/repositories` |
| SQLite Database Path | `/data/gitea/gitea.db` |
| SSH Server Domain | `git.doerflingers.com` |
| SSH Port | `2222` |
| Gitea Base URL | `https://git.doerflingers.com/` |
3. Create your admin account in the **Administrator Account Settings** section.
4. Click **Install Gitea**.
---
## 9. WireGuard Client Setup
### 9.1 Locate Client Configurations
After the WireGuard container starts, it automatically generates config files for each peer defined in `WG_PEERS`:
```bash
# List generated peer configs
ls wireguard/config/peer_*/
# View a specific peer's config
cat wireguard/config/peer_phone/peer_phone.conf
```
Each peer folder also contains a **QR code** image for easy mobile setup:
```bash
# Display QR code in the terminal (for mobile scanning)
docker exec wireguard /app/show-peer phone
```
### 9.2 Install WireGuard on Your Client
| Platform | App |
|----------|-----|
| **Android** | [WireGuard](https://play.google.com/store/apps/details?id=com.wireguard.android) on Play Store |
| **iOS** | [WireGuard](https://apps.apple.com/app/wireguard/id1441195209) on App Store |
| **Windows** | [WireGuard](https://www.wireguard.com/install/) official installer |
| **macOS** | [WireGuard](https://apps.apple.com/app/wireguard/id1451685025) on App Store or `brew install wireguard-tools` |
| **Linux** | `sudo apt install wireguard-tools` |
### 9.3 Import the Configuration
- **Mobile:** Scan the QR code shown by `docker exec wireguard /app/show-peer <peername>`
- **Desktop:** Copy the `peer_<name>.conf` file to the client and import it in the WireGuard app
### 9.4 Test the VPN Connection
1. Activate the WireGuard tunnel on your client.
2. Try accessing `https://ha.doerflingers.com` — it should work even from a mobile network.
3. You can also access the Nginx Proxy Manager admin UI at `http://192.168.178.XX:81` through the VPN.
---
## 10. Backup & Restore
### 10.1 Create a Backup
```bash
# Backup all service data to ./backups/
./scripts/backup.sh
# Or specify a custom backup directory
./scripts/backup.sh /mnt/usb-drive/backups
```
The script creates a timestamped archive like `home-server-backup_20260220_080000.tar.gz`.
> **Tip:** For automated backups, add a cron job:
> ```cron
> # Daily backup at 3 AM
> 0 3 * * * /home/YOUR_USER/home-server/scripts/backup.sh /mnt/backup-drive/ >> /var/log/home-server-backup.log 2>&1
> ```
### 10.2 Restore from Backup
```bash
# Stop all services first!
docker compose down
# Restore
./scripts/restore.sh backups/home-server-backup_20260220_080000.tar.gz
# Restart services
docker compose up -d
```
---
## 11. Migration to New Hardware
Moving everything to a new server is straightforward thanks to Docker:
1. **On the old server:**
```bash
docker compose down
./scripts/backup.sh
```
2. **Copy to new server:**
```bash
scp backups/home-server-backup_*.tar.gz user@new-server:/tmp/
scp -r . user@new-server:~/home-server/ # or just clone the repo
```
3. **On the new server:**
```bash
# Install prerequisites (Section 2 of this README)
cd ~/home-server
./scripts/restore.sh /tmp/home-server-backup_*.tar.gz
# Review .env and adjust if needed (e.g., new timezone)
docker compose up -d
```
4. **Update DynDNS** — Run the DynDNS setup again on the new server (Section 3).
5. **Update FritzBox** — If the new server has a different local IP, update the port forwarding rules (Section 4).
---
## 12. Troubleshooting
### Container won't start
```bash
docker compose logs <service-name>
```
### Home Assistant shows `400 Bad Request`
This means the reverse proxy is not trusted. Verify that `homeassistant/configuration.yaml` contains the correct `trusted_proxies` subnet (`172.20.0.0/24`) matching the Docker network in `docker-compose.yml`.
```bash
# Find the actual Docker network subnet
docker network inspect proxy-network | grep Subnet
```
### Let's Encrypt certificate fails
- **Port 80 must be forwarded** in FritzBox (HTTP-01 challenge requires it).
- DNS for the domain must already point to your IP. Check: `dig +short ha.doerflingers.com`
- Wait a few minutes after setting up DynDNS for DNS propagation.
### WireGuard clients can't connect
- Verify port `51820/UDP` is forwarded in FritzBox.
- Check if the WireGuard kernel module is loaded: `lsmod | grep wireguard`
- View WireGuard logs: `docker compose logs wireguard`
### Gitea SSH clone doesn't work
- Verify port `2222/TCP` is forwarded in FritzBox.
- Clone syntax: `git clone ssh://git@git.doerflingers.com:2222/user/repo.git`
### High memory usage
```bash
# Check per-container memory usage
docker stats --no-stream
```
If running low on RAM, consider:
- Reducing `WG_PEERS` count
- Moving Gitea to a lighter database (SQLite is already the lightest)
- Adding more swap: `sudo fallocate -l 4G /swapfile2 && sudo chmod 600 /swapfile2 && sudo mkswap /swapfile2 && sudo swapon /swapfile2`
### Laptop suspends when lid is closed
See Section 2.5 — make sure `HandleLidSwitch=ignore` is set.
---
## 13. Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ INTERNET │
│ │
│ ha.doerflingers.com ──┐ │
│ git.doerflingers.com ─┤──→ Your Public IP (DynDNS-updated) │
│ home.doerflingers.com ┘ │
└────────────────────────────────┬────────────────────────────────────────┘
┌───────────┴───────────┐
│ FritzBox │
│ Port Forwarding: │
│ 80 → Laptop:80 │
│ 443 → Laptop:443 │
│ 51820/UDP → :51820 │
│ 2222 → Laptop:2222 │
└───────────┬───────────┘
┌───────────┴───────────┐
│ Ubuntu 24.04 Laptop │
│ (Docker Host) │
└───────────┬───────────┘
┌──────────────────┼──────────────────┐
│ Docker Network │
│ (proxy-network) │
│ 172.20.0.0/24 │
│ │
│ ┌─────────────────────────────┐ │
│ │ Nginx Proxy Manager │ │
│ │ :80, :443, :81 │ │
│ │ SSL termination │ │
│ │ Let's Encrypt certs │ │
│ └──────┬──────┬───────────────┘ │
│ │ │ │
│ ┌────┘ └────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Home │ │ Gitea │ │
│ │ Assistant│ │ :3000 │ │
│ │ :8123 │ │ :22→2222│ │
│ └──────────┘ └──────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ WireGuard VPN │ │
│ │ :51820/UDP │ │
│ │ Subnet: 10.13.13.0/24 │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
```
### Data Persistence
All service data is stored in local directories (bind mounts), making backups and migration simple:
| Service | Data Directory | Contains |
|---------|---------------|----------|
| Nginx Proxy Manager | `./nginx-proxy-manager/data/` + `./nginx-proxy-manager/letsencrypt/` | Proxy configs, SSL certs |
| Home Assistant | `./homeassistant/` | Configuration, automations, database |
| Gitea | `./gitea/data/` | Repositories, database, config |
| WireGuard | `./wireguard/config/` | Server + peer keys, configs |
---
## Quick Reference
```bash
# Start all services
docker compose up -d
# Stop all services
docker compose down
# Restart a single service
docker compose restart homeassistant
# View logs
docker compose logs -f
# Check resource usage
docker stats
# Update all images to latest
docker compose pull
docker compose up -d
# Backup
./scripts/backup.sh
# Restore
docker compose down
./scripts/restore.sh backups/<backup-file>.tar.gz
docker compose up -d
```