Initial commit
This commit is contained in:
37
.env.example
Normal file
37
.env.example
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Home Server Environment Configuration
|
||||||
|
# Copy this file to .env and fill in your values:
|
||||||
|
# cp .env.example .env
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# --- General ---
|
||||||
|
TZ=Europe/Berlin
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
|
||||||
|
# --- Domain Names ---
|
||||||
|
DOMAIN_HA=ha.doerflingers.com
|
||||||
|
DOMAIN_GITEA=git.doerflingers.com
|
||||||
|
DOMAIN_HOME=home.doerflingers.com
|
||||||
|
|
||||||
|
# --- WireGuard VPN ---
|
||||||
|
# Your public domain or DynDNS hostname for VPN connections
|
||||||
|
WG_SERVERURL=home.doerflingers.com
|
||||||
|
# Comma-separated list of client configs to generate (names or count)
|
||||||
|
WG_PEERS=phone,laptop,tablet
|
||||||
|
# Internal subnet for VPN clients
|
||||||
|
WG_INTERNAL_SUBNET=10.13.13.0
|
||||||
|
# UDP port for WireGuard (must match FritzBox port forwarding)
|
||||||
|
WG_PORT=51820
|
||||||
|
|
||||||
|
# --- Gitea ---
|
||||||
|
GITEA_SSH_PORT=2222
|
||||||
|
# Secret key for Gitea (generate with: openssl rand -hex 32)
|
||||||
|
GITEA_SECRET_KEY=CHANGE_ME_GENERATE_WITH_openssl_rand_hex_32
|
||||||
|
# Internal token (generate with: openssl rand -hex 32)
|
||||||
|
GITEA_INTERNAL_TOKEN=CHANGE_ME_GENERATE_WITH_openssl_rand_hex_32
|
||||||
|
|
||||||
|
# --- Nginx Proxy Manager ---
|
||||||
|
# Default admin login (change after first login!)
|
||||||
|
# Email: admin@example.com
|
||||||
|
# Password: changeme
|
||||||
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Gitea data
|
||||||
|
gitea/data/
|
||||||
|
|
||||||
|
# Nginx Proxy Manager data
|
||||||
|
nginx-proxy-manager/data/
|
||||||
|
nginx-proxy-manager/letsencrypt/
|
||||||
|
|
||||||
|
# WireGuard config (contains private keys!)
|
||||||
|
wireguard/config/
|
||||||
|
|
||||||
|
# Environment file (contains secrets)
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
backups/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
682
README.md
Normal file
682
README.md
Normal file
@@ -0,0 +1,682 @@
|
|||||||
|
# 🏠 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
|
||||||
|
```
|
||||||
121
docker-compose.yml
Normal file
121
docker-compose.yml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Home Server Docker Compose Stack
|
||||||
|
# Services: Nginx Proxy Manager, Home Assistant, Gitea, WireGuard
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cp .env.example .env # then edit .env with your values
|
||||||
|
# docker compose up -d
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy-network:
|
||||||
|
name: proxy-network
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.20.0.0/24
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ===========================================================================
|
||||||
|
# Nginx Proxy Manager — Reverse proxy with built-in Let's Encrypt
|
||||||
|
# Admin UI: http://<server-ip>:81
|
||||||
|
# Default login: admin@example.com / changeme
|
||||||
|
# ===========================================================================
|
||||||
|
nginx-proxy-manager:
|
||||||
|
image: jc21/nginx-proxy-manager:latest
|
||||||
|
container_name: nginx-proxy-manager
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80" # HTTP
|
||||||
|
- "443:443" # HTTPS
|
||||||
|
- "81:81" # Admin UI
|
||||||
|
volumes:
|
||||||
|
- ./nginx-proxy-manager/data:/data
|
||||||
|
- ./nginx-proxy-manager/letsencrypt:/etc/letsencrypt
|
||||||
|
networks:
|
||||||
|
- proxy-network
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "/usr/bin/check-health" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# Home Assistant — Smart home platform
|
||||||
|
# Internal URL: http://homeassistant:8123
|
||||||
|
# ===========================================================================
|
||||||
|
homeassistant:
|
||||||
|
image: ghcr.io/home-assistant/home-assistant:stable
|
||||||
|
container_name: homeassistant
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ:-Europe/Berlin}
|
||||||
|
volumes:
|
||||||
|
- ./homeassistant:/config
|
||||||
|
networks:
|
||||||
|
- proxy-network
|
||||||
|
depends_on:
|
||||||
|
- nginx-proxy-manager
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# Gitea — Lightweight Git hosting
|
||||||
|
# Internal URL: http://gitea:3000
|
||||||
|
# SSH: Port 2222 (mapped from container 22)
|
||||||
|
# ===========================================================================
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
container_name: gitea
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- USER_UID=${PUID:-1000}
|
||||||
|
- USER_GID=${PGID:-1000}
|
||||||
|
- GITEA__database__DB_TYPE=sqlite3
|
||||||
|
- GITEA__database__PATH=/data/gitea/gitea.db
|
||||||
|
- GITEA__server__DOMAIN=${DOMAIN_GITEA:-git.doerflingers.com}
|
||||||
|
- GITEA__server__SSH_DOMAIN=${DOMAIN_GITEA:-git.doerflingers.com}
|
||||||
|
- GITEA__server__SSH_PORT=${GITEA_SSH_PORT:-2222}
|
||||||
|
- GITEA__server__ROOT_URL=https://${DOMAIN_GITEA:-git.doerflingers.com}/
|
||||||
|
- GITEA__server__LFS_START_SERVER=true
|
||||||
|
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY:-}
|
||||||
|
- GITEA__security__INTERNAL_TOKEN=${GITEA_INTERNAL_TOKEN:-}
|
||||||
|
volumes:
|
||||||
|
- ./gitea/data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "${GITEA_SSH_PORT:-2222}:22"
|
||||||
|
networks:
|
||||||
|
- proxy-network
|
||||||
|
depends_on:
|
||||||
|
- nginx-proxy-manager
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# WireGuard — Modern VPN server
|
||||||
|
# External: UDP port 51820 (must be forwarded in FritzBox)
|
||||||
|
# ===========================================================================
|
||||||
|
wireguard:
|
||||||
|
image: lscr.io/linuxserver/wireguard:latest
|
||||||
|
container_name: wireguard
|
||||||
|
restart: unless-stopped
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- SYS_MODULE
|
||||||
|
environment:
|
||||||
|
- PUID=${PUID:-1000}
|
||||||
|
- PGID=${PGID:-1000}
|
||||||
|
- TZ=${TZ:-Europe/Berlin}
|
||||||
|
- SERVERURL=${WG_SERVERURL:-home.doerflingers.com}
|
||||||
|
- SERVERPORT=${WG_PORT:-51820}
|
||||||
|
- PEERS=${WG_PEERS:-phone,laptop,tablet}
|
||||||
|
- PEERDNS=auto
|
||||||
|
- INTERNAL_SUBNET=${WG_INTERNAL_SUBNET:-10.13.13.0}
|
||||||
|
- ALLOWEDIPS=0.0.0.0/0
|
||||||
|
volumes:
|
||||||
|
- ./wireguard/config:/config
|
||||||
|
- /lib/modules:/lib/modules:ro
|
||||||
|
ports:
|
||||||
|
- "${WG_PORT:-51820}:51820/udp"
|
||||||
|
networks:
|
||||||
|
- proxy-network
|
||||||
|
sysctls:
|
||||||
|
- net.ipv4.conf.all.src_valid_mark=1
|
||||||
1
homeassistant/automations.yaml
Normal file
1
homeassistant/automations.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
36
homeassistant/configuration.yaml
Normal file
36
homeassistant/configuration.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Home Assistant Configuration
|
||||||
|
# https://www.home-assistant.io/docs/configuration/
|
||||||
|
|
||||||
|
homeassistant:
|
||||||
|
name: Home
|
||||||
|
unit_system: metric
|
||||||
|
time_zone: Europe/Berlin
|
||||||
|
currency: EUR
|
||||||
|
|
||||||
|
# Enable the frontend
|
||||||
|
frontend:
|
||||||
|
|
||||||
|
# Enable configuration UI
|
||||||
|
config:
|
||||||
|
|
||||||
|
# HTTP configuration — required for reverse proxy
|
||||||
|
http:
|
||||||
|
use_x_forwarded_for: true
|
||||||
|
trusted_proxies:
|
||||||
|
# Docker proxy-network subnet
|
||||||
|
- 172.20.0.0/24
|
||||||
|
# WireGuard VPN subnet
|
||||||
|
- 10.13.13.0/24
|
||||||
|
|
||||||
|
# Discover devices on the network
|
||||||
|
# Note: discovery may be limited in container mode
|
||||||
|
# For full discovery, consider using host network mode
|
||||||
|
# (but that conflicts with reverse proxy setup)
|
||||||
|
|
||||||
|
# Enable the API
|
||||||
|
api:
|
||||||
|
|
||||||
|
# Automations, scripts, scenes
|
||||||
|
automation: !include automations.yaml
|
||||||
|
script: !include scripts.yaml
|
||||||
|
scene: !include scenes.yaml
|
||||||
1
homeassistant/scenes.yaml
Normal file
1
homeassistant/scenes.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Scenes
|
||||||
1
homeassistant/scripts.yaml
Normal file
1
homeassistant/scripts.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Scripts
|
||||||
88
scripts/backup.sh
Normal file
88
scripts/backup.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
###############################################################################
|
||||||
|
# backup.sh — Backup all persistent data for the home-server stack
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/backup.sh [backup-directory]
|
||||||
|
#
|
||||||
|
# Default backup directory: ./backups/
|
||||||
|
# Creates a timestamped tar.gz archive of all service data.
|
||||||
|
###############################################################################
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
BACKUP_DIR="${1:-$PROJECT_DIR/backups}"
|
||||||
|
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
||||||
|
BACKUP_FILE="$BACKUP_DIR/home-server-backup_${TIMESTAMP}.tar.gz"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${YELLOW}=== Home Server Backup ===${NC}"
|
||||||
|
echo "Timestamp: $TIMESTAMP"
|
||||||
|
echo "Project: $PROJECT_DIR"
|
||||||
|
echo "Backup to: $BACKUP_FILE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create backup directory if it doesn't exist
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Check if services are running and warn
|
||||||
|
if command -v docker &> /dev/null; then
|
||||||
|
RUNNING=$(docker compose -f "$PROJECT_DIR/docker-compose.yml" ps --status running -q 2>/dev/null | wc -l)
|
||||||
|
if [ "$RUNNING" -gt 0 ]; then
|
||||||
|
echo -e "${YELLOW}WARNING: $RUNNING container(s) are running.${NC}"
|
||||||
|
echo "For a consistent backup, consider stopping services first:"
|
||||||
|
echo " docker compose down"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue anyway? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Backup cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Directories to back up
|
||||||
|
DIRS_TO_BACKUP=(
|
||||||
|
"nginx-proxy-manager"
|
||||||
|
"homeassistant"
|
||||||
|
"gitea"
|
||||||
|
"wireguard"
|
||||||
|
".env"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build tar arguments — only include directories/files that exist
|
||||||
|
TAR_ARGS=()
|
||||||
|
for item in "${DIRS_TO_BACKUP[@]}"; do
|
||||||
|
if [ -e "$PROJECT_DIR/$item" ]; then
|
||||||
|
TAR_ARGS+=("$item")
|
||||||
|
echo -e " ${GREEN}✓${NC} Including: $item"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⊘${NC} Skipping (not found): $item"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#TAR_ARGS[@]} -eq 0 ]; then
|
||||||
|
echo -e "${RED}ERROR: No data directories found to back up.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Creating archive..."
|
||||||
|
|
||||||
|
tar -czf "$BACKUP_FILE" -C "$PROJECT_DIR" "${TAR_ARGS[@]}"
|
||||||
|
|
||||||
|
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}=== Backup complete ===${NC}"
|
||||||
|
echo "File: $BACKUP_FILE"
|
||||||
|
echo "Size: $BACKUP_SIZE"
|
||||||
|
echo ""
|
||||||
|
echo "To restore, run:"
|
||||||
|
echo " ./scripts/restore.sh $BACKUP_FILE"
|
||||||
82
scripts/restore.sh
Normal file
82
scripts/restore.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
###############################################################################
|
||||||
|
# restore.sh — Restore home-server stack data from a backup archive
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/restore.sh <backup-file.tar.gz>
|
||||||
|
#
|
||||||
|
# This will extract the backup into the project directory, overwriting
|
||||||
|
# existing service data.
|
||||||
|
###############################################################################
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo -e "${RED}Usage: $0 <backup-file.tar.gz>${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Available backups:"
|
||||||
|
if [ -d "$PROJECT_DIR/backups" ]; then
|
||||||
|
ls -lh "$PROJECT_DIR/backups/"*.tar.gz 2>/dev/null || echo " (none found)"
|
||||||
|
else
|
||||||
|
echo " (no backups directory)"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_FILE="$1"
|
||||||
|
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo -e "${RED}ERROR: Backup file not found: $BACKUP_FILE${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}=== Home Server Restore ===${NC}"
|
||||||
|
echo "Backup: $BACKUP_FILE"
|
||||||
|
echo "Target: $PROJECT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if services are running
|
||||||
|
if command -v docker &> /dev/null; then
|
||||||
|
RUNNING=$(docker compose -f "$PROJECT_DIR/docker-compose.yml" ps --status running -q 2>/dev/null | wc -l)
|
||||||
|
if [ "$RUNNING" -gt 0 ]; then
|
||||||
|
echo -e "${RED}ERROR: $RUNNING container(s) are still running.${NC}"
|
||||||
|
echo "Please stop all services before restoring:"
|
||||||
|
echo " docker compose down"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}WARNING: This will overwrite existing data in:${NC}"
|
||||||
|
echo " - nginx-proxy-manager/"
|
||||||
|
echo " - homeassistant/"
|
||||||
|
echo " - gitea/"
|
||||||
|
echo " - wireguard/"
|
||||||
|
echo " - .env"
|
||||||
|
echo ""
|
||||||
|
read -p "Are you sure? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Restore cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Extracting backup..."
|
||||||
|
|
||||||
|
tar -xzf "$BACKUP_FILE" -C "$PROJECT_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}=== Restore complete ===${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Review your .env file and adjust if needed"
|
||||||
|
echo " 2. Start services: docker compose up -d"
|
||||||
|
echo " 3. Verify all services: docker compose ps"
|
||||||
Reference in New Issue
Block a user