Compare commits

...

17 Commits

Author SHA1 Message Date
059daa77aa Fix SSL certificate renewal for Let's Encrypt
Allow ACME challenge requests before HTTPS redirect:
- Add location block for /.well-known/acme-challenge/
- Prevent immediate redirect that was blocking Let's Encrypt verification
- Fixes 'Timeout during connect' errors during certbot renewal
2025-11-15 22:30:01 +01:00
9aa881d895 Document voice assistant TTS service status
Mark TTS functionality as disabled due to onnxruntime removal
(freed 1.2GB disk space during cleanup)
2025-10-11 18:25:08 +02:00
ab4a4cfc9c Add comprehensive backup system documentation
Document complete backup strategy covering:
- Setup and initialization procedures
- What gets backed up (and what doesn't)
- Multiple restore scenarios (full reinstall, rollback, specific volumes)
- Retention policy explanation
- Troubleshooting and security notes
2025-10-11 18:24:58 +02:00
646d3e59bc Add comprehensive system backup scripts
Implement full system backup with Restic including:
- Docker volume exports (Gitea, Jellyfin, Nextcloud, Portainer)
- System configuration backup (/etc/)
- Package list exports (pacman explicit, all, AUR)
- Automated retention policy (7 daily, 4 weekly, 3 monthly, 1 yearly)
- Separate temporary directories for Docker and system data
2025-10-11 18:24:51 +02:00
ad7270aa74 Add Restic backup repository initialization script
Initialize encrypted backup repository on NAS with:
- Auto-install of Restic if needed
- Secure password generation and storage
- Repository initialization with AES-256 encryption
2025-10-11 18:24:41 +02:00
7e64557f0b Add Nextcloud reverse proxy configuration to nginx 2025-10-11 17:25:56 +02:00
acc30e7c3d Enable Nextcloud link on landing page 2025-10-11 17:25:44 +02:00
3d57327146 Re-enable Nextcloud service with Docker configuration 2025-10-11 17:25:31 +02:00
d0727bd8a7 Document dockerization decisions and status
- Mark dockerization task as complete
- Document reasons for non-dockerized services:
  - Glances/Netdata: Need full system access for monitoring
  - Syncthing: Docker permission issues with config directory
  - Nginx: Reverse proxy requires system integration
- Update service status: Copyparty now dockerized
- Remove Cockpit references (removed from system)
- Update VNC documentation to reflect temporary usage pattern
2025-10-06 22:45:58 +02:00
7eaf16f53e Update symlink setup script for all Docker services
- Add automatic directory creation in create_symlink function
- Include copyparty, portainer, and qbittorrent configs
- Add landing page symlink for easier updates
- Update verification commands to include all services
2025-10-06 22:45:36 +02:00
bab97f7166 Add Copyparty Docker configuration
- Replace config file with command-line arguments for Docker compatibility
- Enable file search, deduplication, and partial upload features
- Configure reverse proxy support with xff-src and rproxy flags
- Add password database integration with chpw support
- Map all NAS volumes with appropriate permissions
- Fix health check to use /files/ path
- Remove obsolete copyparty.conf (incompatible with Docker image)
2025-10-06 22:43:58 +02:00
6894c5f175 Update geoip-shell documentation with implementation details
- Document interactive installation process
- Add complete EU country whitelist configuration
- Include setup commands and wizard prompts
- Clarify that geoip-shell was used instead of manual iptables setup
2025-10-06 20:40:27 +02:00
6761c8903d Add Docker configuration symlink setup script
- Automates symlinking of docker-compose.yml files from repo to /opt/docker
- Includes Gitea, Jellyfin, qBittorrent, Portainer
- Symlinks daemon.json to /etc/docker
- Creates timestamped backups before replacing files
- Eliminates need for manual config copying
2025-10-06 20:40:11 +02:00
a8bbf5ea4a Disable Nextcloud service
- Renamed docker-compose.yml to .disabled
- Service can be re-enabled by renaming file back to .yml
- Nextcloud not currently needed with Copyparty in place
2025-10-06 20:39:54 +02:00
fefdc14398 Add Portainer docker-compose configuration
- Web UI for Docker container management
- Bound to localhost (reverse proxy recommended)
- Uses named volume for persistent data
- Configured for Europe/Budapest timezone
- Resource limits and health checks included
2025-10-06 20:39:33 +02:00
802d275d6e Fix Docker daemon config: Remove invalid JSON comments
- Removed comments from daemon.json (JSON doesn't support comments)
- Synced with deployed working version
- Maintains minimal working configuration with logging only
2025-10-06 20:39:21 +02:00
dc16d0302d Add installers folder to copyparty configuration
- New /installers volume for game installers and ISO files
- Accessible to both guest and hoborg users
- Complements existing torrent categorization structure
2025-10-06 20:39:01 +02:00
14 changed files with 884 additions and 103 deletions

19
TODO.md
View File

@@ -23,7 +23,7 @@
- [ ] WireGuard VPN server configuration
- [ ] UFW firewall setup and rules
- [ ] fail2ban for intrusion prevention
- [ ] Security enhancement for VNC connections (in the meantime: only run the vnc service for short time while we are using it)
- [x] VNC setup for remote desktop access *(TigerVNC installed, enabled temporarily via SSH when needed)*
## Git & Development
- [x] Gitea Docker container setup *(completed - running on port 3000)*
@@ -91,9 +91,8 @@ Lower priority - mostly using SSH or TTY anyways
- [x] Shared media folders with Copyparty (Music, Videos, shared)
- [ ] Set up self-hosted chat server (Matrix or Mattermost)
- [ ] Install monitoring and management tools *(in progress)*
- [ ] Portainer (Docker management with built-in auth)
- [ ] Glances (system monitoring with nginx basic auth)
- [ ] Cockpit (system administration with PAM auth)
- [x] Portainer (Docker management with built-in auth)
- [x] Glances (system monitoring with nginx basic auth)
- [ ] lazydocker (terminal Docker management)
- [ ] Configure nginx basic auth for Glances endpoint
- [ ] Update nginx reverse proxy config for new admin services
@@ -101,14 +100,16 @@ Lower priority - mostly using SSH or TTY anyways
- [ ] Set up Nextcloud for advanced file synchronization features
- Copyparty covers basic file sharing needs
- [x] Set up reverse proxy with SSL certificates *(completed - HTTPS working with auto-renewal)*
- [ ] Make sure all services are dockerized unless we have a good reason not to
- [x] Make sure all services are dockerized unless we have a good reason not to
- Gitea: ✅ Docker
- Jellyfin: ✅ Docker
- Copyparty: ❌ systemd service (consider dockerizing)
- Nginx: ❌ system package (fine as-is for reverse proxy)
- qBittorrent: ✅ Docker
- Portainer: ✅ Docker
- Glances: ❌ system package (web server mode)
- Cockpit: ❌ system package (system integration required)
- Copyparty: ✅ Docker
- Glances: ❌ systemd (needs full system access for accurate monitoring)
- Netdata: ❌ systemd (needs full system access for accurate monitoring)
- Syncthing: ❌ systemd (Docker permission issues with config directory)
- Nginx: ❌ system package (reverse proxy, system integration required)
## Hardware & Troubleshooting
- [ ] Fix bluetooth audio connectivity issues

View File

@@ -1,71 +0,0 @@
# Copyparty Configuration for Homelab
# DEPLOYMENT LOCATION: /home/hoborg/.config/copyparty/copyparty.conf
[global]
# Network settings
i: 127.0.0.1
p: 8082
rp-loc: /files
# Reverse proxy configuration
rproxy: -1
# Security and features
usernames
chpw
chpw-db: /home/hoborg/.config/copyparty/passwords.json
e2dsa
dedup
dotpart
# Upload settings
u2ts: c
chmod-f: 644
chmod-d: 755
# Server name
name: homelab-files
[accounts]
guest: SecurePass2024!
hoborg: AdminPass2024!
[/shared]
/mnt/nas/shared
accs:
rw: guest
rwmd: hoborg
[/documents]
/mnt/nas/documents
accs:
rwmd: hoborg
[/music]
/mnt/nas/music
accs:
rw: guest
rwmd: hoborg
[/videos]
/mnt/nas/videos
accs:
rw: guest
rwmd: hoborg
[/private]
/mnt/nas/private
accs:
rwmd: hoborg
[/pictures]
/mnt/nas/pictures
accs:
rw: guest
rwmd: hoborg
[/torrent]
/mnt/nas/torrent
accs:
rw: guest
rwmd: hoborg

View File

@@ -0,0 +1,90 @@
# Copyparty Docker Compose Configuration
# Deploy with: sudo mkdir -p /opt/docker/copyparty && sudo cp config/docker/copyparty/docker-compose.yml /opt/docker/copyparty/
# Start with: cd /opt/docker/copyparty && sudo docker-compose up -d
# COPYPARTY CONFIGURATION
# - File server with upload/download capabilities
# - WebDAV support for mobile and desktop clients
# - Bound to localhost only (reverse proxy required)
# - Configuration stored in repo at config/copyparty/copyparty.conf
services:
copyparty:
image: copyparty/ac:latest
container_name: copyparty
restart: unless-stopped
# Environment
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Budapest
# Network - bind to localhost for security
ports:
- "127.0.0.1:8082:3923" # Web UI (reverse proxy only)
# Volume mounts
volumes:
# Password database
- /home/hoborg/.config/copyparty/passwords.json:/home/hoborg/.config/copyparty/passwords.json
# NAS storage volumes (mapped to container paths)
- /mnt/nas/shared:/w/shared:rw
- /mnt/nas/documents:/w/documents:rw
- /mnt/nas/music:/w/music:rw
- /mnt/nas/videos:/w/videos:rw
- /mnt/nas/private:/w/private:rw
- /mnt/nas/pictures:/w/pictures:rw
- /mnt/nas/installers:/w/installers:rw
- /mnt/nas/torrent:/w/torrent:rw
# Command with inline volume configuration using -v flag
command:
- --rp-loc=/files
- --name=homelab-files
- --usernames
- --chpw
- --chpw-db=/home/hoborg/.config/copyparty/passwords.json
- --xff-src=172.0.0.0/8
- --rproxy=-1
- -e2dsa
- --dedup
- --dotpart
- -a
- hoborg:AdminPass2024!
- -a
- guest:SecurePass2024!
- -v
- /w/shared:shared:r:rw,guest:rwmd,hoborg
- -v
- /w/documents:documents:rwmd,hoborg
- -v
- /w/music:music:r:rw,guest:rwmd,hoborg
- -v
- /w/videos:videos:r:rw,guest:rwmd,hoborg
- -v
- /w/private:private:rwmd,hoborg
- -v
- /w/pictures:pictures:r:rw,guest:rwmd,hoborg
- -v
- /w/installers:installers:r:rw,guest:rwmd,hoborg
- -v
- /w/torrent:torrent:r:rw,guest:rwmd,hoborg
# Resource limits
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
# Health check
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3923/files/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

View File

@@ -1,17 +1,7 @@
# Docker Daemon Configuration (Minimal Working Version)
# Deploy to: /etc/docker/daemon.json
#
# Setup commands:
# sudo cp config/docker/daemon.json /etc/docker/
# sudo systemctl restart docker
#
# Note: Removed problematic security settings that caused read-only filesystem issues.
# Only keeping essential logging configuration.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
}

View File

@@ -0,0 +1,50 @@
# Portainer Docker Compose Configuration
# Deploy with: sudo mkdir -p /opt/docker/portainer && sudo cp config/docker/portainer/docker-compose.yml /opt/docker/portainer/
# Start with: cd /opt/docker/portainer && sudo docker-compose up -d
# PORTAINER CONFIGURATION
# - Web UI for Docker container management
# - Bound to localhost only (reverse proxy recommended)
# - Persistent data storage in named volume
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
# Environment
environment:
- TZ=Europe/Budapest
# Network - bind to localhost for security
ports:
- "127.0.0.1:9000:9000" # Web UI (reverse proxy only)
# Volume mounts
volumes:
# Docker socket for container management
- /var/run/docker.sock:/var/run/docker.sock
# Portainer data
- portainer_data:/data
# Resource limits
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.1'
memory: 128M
# Health check
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/api/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
portainer_data:

View File

@@ -4,9 +4,16 @@
server {
listen 80 default_server;
server_name ak-homelab.duckdns.org _;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
# Allow Let's Encrypt ACME challenges
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all other HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
@@ -176,6 +183,32 @@ server {
proxy_set_header Connection "upgrade";
}
# Nextcloud cloud storage
location /cloud/ {
proxy_pass http://127.0.0.1:8083/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Nextcloud specific headers
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
# Large file upload support
client_max_body_size 10G;
client_body_buffer_size 128k;
# Upload timeout settings
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Buffering settings for uploads
proxy_buffering off;
proxy_request_buffering off;
}
ssl_certificate /etc/letsencrypt/live/ak-homelab.duckdns.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/ak-homelab.duckdns.org/privkey.pem; # managed by Certbot

View File

@@ -140,10 +140,10 @@
<h3>Copyparty</h3>
<p>File Server & WebDAV</p>
</a>
<a href="#" class="service disabled" onclick="return false;">
<a href="/cloud/" class="service">
<i class="fas fa-cloud"></i>
<h3>Nextcloud</h3>
<p>Temporarily Disabled</p>
<p>Cloud Storage & Collaboration</p>
</a>
<a href="/media/" class="service">
<i class="fas fa-play-circle"></i>

View File

@@ -386,8 +386,11 @@ sudo fail2ban-client get sshd banned
- **Last Update:** September 17, 2025 at 00:57:41
### Whitelisted Countries
**Configuration:** All EU member states plus European territories (full list accepted by geoip-shell)
```
AL (Albania), AD (Andorra), AM (Armenia), AT (Austria), AZ (Azerbaijan)
AL (Albania), AD (Andorra), AM (Armenia), AT (Austria), AX (Åland Islands), AZ (Azerbaijan)
BY (Belarus), BE (Belgium), BA (Bosnia and Herzegovina), BG (Bulgaria)
HR (Croatia), CY (Cyprus), CZ (Czech Republic), DK (Denmark)
EE (Estonia), FO (Faroe Islands), FI (Finland), FR (France)
@@ -440,14 +443,36 @@ journalctl -u geoip-shell
tail -f /var/log/geoip-shell.log
```
### Manual Setup Process
The tool was installed manually with interactive prompts rather than scripted installation due to security considerations. Key decisions made during setup:
### Installation and Setup
**Installation Method:** Interactive CLI setup using geoip-shell installer
The tool was configured using its interactive setup wizard, which prompted for configuration choices and automatically applied all necessary changes to the firewall. This approach ensures proper integration with the existing firewall backend.
**Key Configuration Decisions:**
1. **Whitelist Mode:** Chosen over blacklist for better control
2. **European Focus:** Primary whitelist consists of European countries
3. **Network Exceptions:** Docker and LAN networks automatically detected
4. **Dual Stack:** Both IPv4 and IPv6 protection enabled
5. **Full Protocol Coverage:** TCP and UDP both protected
2. **European Focus:** All EU member states plus European territories
3. **Country List:** Complete list of countries accepted by geoip-shell
4. **Network Exceptions:** Docker and LAN networks automatically detected
5. **Dual Stack:** Both IPv4 and IPv6 protection enabled
6. **Full Protocol Coverage:** TCP and UDP both protected
**Setup Command:**
```bash
# Download and run installer
curl -fsSL https://raw.githubusercontent.com/friendly-bits/geoip-shell/main/install.sh | bash
# Run configuration wizard (prompted automatically after install)
geoip-shell configure
```
During the interactive setup, the wizard prompted for:
- Firewall backend selection (iptables chosen)
- IP list source (RIPE selected)
- Mode selection (whitelist chosen)
- Country selection (all EU countries entered)
- Network interface configuration (all interfaces selected)
### Detailed Documentation
For complete setup details, see **[docs/geoip-blocking.md](docs/geoip-blocking.md)**

233
docs/system-backups.md Normal file
View File

@@ -0,0 +1,233 @@
# System Backups
Complete backup strategy for the homelab using Restic for encrypted, deduplicated backups to NAS.
## Overview
**Backup Tool**: Restic (modern, encrypted, deduplicated)
**Backup Destination**: NAS at `/mnt/nas/backups/homelab-restic`
**Backup Frequency**: Daily (can be automated with systemd timer)
**Encryption**: AES-256, password stored in `/home/hoborg/creds/restic-password.txt`
## What Gets Backed Up
### Full System Backup Includes:
1. **Package Lists**
- All explicitly installed packages (pacman)
- All installed packages (including dependencies)
- AUR/foreign packages list
- Allows exact system recreation after reinstall
2. **System Configuration** (`/etc/`)
- Nginx configuration
- Systemd services
- Network configuration
- All system-wide configs
- Excludes: shadow/gshadow password files
3. **Docker Volumes**
- Gitea (Git repositories and database)
- Jellyfin (media library configuration)
- Nextcloud (cloud storage data and database)
- Portainer (container management data)
### What's NOT Backed Up (Intentionally):
- **User data in `/home`**: Separate partition, safe from system reinstalls
- **Media files**: Already stored on NAS (Music, Videos, Pictures)
- **User documents**: Synced via Nextcloud/Copyparty or on separate partition
## Setup
### Initial Setup (One-Time)
1. **Initialize the backup repository:**
```bash
sudo /home/hoborg/homelab/scripts/backup-init.sh
```
This will:
- Install Restic if needed
- Create backup directory on NAS (`/mnt/nas/backups/homelab-restic`)
- Generate encryption password (saved to `/home/hoborg/creds/restic-password.txt`)
- Initialize the Restic repository
⚠️ **IMPORTANT**: Back up the password file! Without it, backups cannot be restored.
### Running Backups
**Manual backup:**
```bash
sudo /home/hoborg/homelab/scripts/backup-system-full.sh
```
**Automated backups** (optional - setup systemd timer later):
- Create systemd service and timer
- Schedule daily backups (e.g., 3 AM)
- Logs written to `/var/log/homelab-backup.log`
## Backup Scripts
### `backup-init.sh`
Initializes the Restic repository. Run once during setup.
Location: `/home/hoborg/homelab/scripts/backup-init.sh`
### `backup-system-full.sh`
Performs full system backup including packages, configs, and Docker volumes.
Location: `/home/hoborg/homelab/scripts/backup-system-full.sh`
What it does:
1. Exports package lists (pacman explicit, all packages, AUR packages)
2. Exports Docker volumes to compressed tarballs
3. Backs up entire `/etc/` directory
4. Prunes old backups (keeps: 7 daily, 4 weekly, 6 monthly, 2 yearly)
5. Shows backup statistics
## Restore Procedures
### List Available Backups
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
restic -r /mnt/nas/backups/homelab-restic snapshots
```
### Restore After System Reinstall
1. **Restore backup to temporary location:**
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
restic -r /mnt/nas/backups/homelab-restic restore latest --target /tmp/restore
```
2. **Reinstall packages:**
```bash
# Reinstall all explicitly installed packages
sudo pacman -S --needed $(cat /tmp/restore/tmp/system-backup-*/pacman-explicit.txt)
# Reinstall AUR packages
yay -S --needed $(cat /tmp/restore/tmp/system-backup-*/aur-packages.txt)
```
3. **Restore system configs:**
```bash
sudo cp -r /tmp/restore/etc/* /etc/
sudo systemctl daemon-reload
```
4. **Restore Docker volumes:**
```bash
# Example: Restore Gitea
docker-compose -f /opt/docker/gitea/docker-compose.yml down
sudo tar xzf /tmp/restore/tmp/docker-backup-*/gitea-data.tar.gz -C /tmp/gitea-restore
docker run --rm -v gitea_gitea:/data -v /tmp/gitea-restore:/backup alpine \
sh -c "rm -rf /data/* && cp -a /backup/* /data/"
docker-compose -f /opt/docker/gitea/docker-compose.yml up -d
```
### Rollback After Broken Update
If a system update breaks something:
1. **Restore specific config:**
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
# Restore nginx config
restic -r /mnt/nas/backups/homelab-restic restore latest \
--target /tmp/restore --include /etc/nginx/
sudo cp -r /tmp/restore/etc/nginx/* /etc/nginx/
sudo systemctl reload nginx
```
2. **Downgrade packages:**
```bash
# Check which packages were updated
grep -A5 "upgraded" /var/log/pacman.log | tail -20
# Downgrade specific package to cached version
sudo pacman -U /var/cache/pacman/pkg/package-old-version.pkg.tar.zst
```
### Restore Specific Docker Volume
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
# List files in backup
restic -r /mnt/nas/backups/homelab-restic ls latest | grep docker-backup
# Restore specific volume tarball
restic -r /mnt/nas/backups/homelab-restic restore latest \
--target /tmp/restore \
--include /tmp/docker-backup-*/nextcloud-data.tar.gz
# Extract and restore to Docker volume
mkdir /tmp/nextcloud-data
tar xzf /tmp/restore/tmp/docker-backup-*/nextcloud-data.tar.gz -C /tmp/nextcloud-data
docker run --rm -v nextcloud_nextcloud_data:/data -v /tmp/nextcloud-data:/backup alpine \
sh -c "rm -rf /data/* && cp -a /backup/* /data/"
```
## Backup Retention Policy
The backup script automatically prunes old backups:
- **Daily**: Keep last 7 days
- **Weekly**: Keep last 4 weeks
- **Monthly**: Keep last 6 months
- **Yearly**: Keep last 2 years
This balances storage space with recovery options.
## Troubleshooting
### "Repository not initialized" Error
Run the initialization script:
```bash
sudo /home/hoborg/homelab/scripts/backup-init.sh
```
### "Password incorrect" Error
Check that password file exists and is readable:
```bash
ls -l /home/hoborg/creds/restic-password.txt
```
### Check Backup Size
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
restic -r /mnt/nas/backups/homelab-restic stats --mode restore-size
```
### Verify Backup Integrity
```bash
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
restic -r /mnt/nas/backups/homelab-restic check
```
## Security Notes
- Backups are encrypted with AES-256
- Password file has `600` permissions (owner read/write only)
- Shadow/gshadow files are excluded from backups
- NAS should have access restrictions
- Consider offsite backup copy of password file
## Future Enhancements
- [ ] Automated systemd timer setup
- [ ] Pre-backup notifications
- [ ] Post-backup success/failure notifications
- [ ] Backup verification script
- [ ] Web dashboard for backup status
- [ ] Offsite backup replication (optional)

View File

@@ -1,5 +1,7 @@
# Voice Assistant Setup
⚠️ **STATUS: DISABLED** - onnxruntime package was removed to free disk space (1.2GB). Voice functionality is currently unavailable.
This document describes how to set up AI voice capabilities for Claude Code using local TTS (Text-to-Speech) services.
## Overview
@@ -8,6 +10,7 @@ The voice assistant setup uses:
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
- **Ryan voice model**: Professional male US English voice for AI assistant personality
- **onnxruntime**: ML inference library (removed - required for TTS)
## Prerequisites

95
scripts/backup-homelab.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/bin/bash
# Backup Docker volumes and system configs to NAS using Restic
# Can be run manually or via systemd timer
set -e
# Configuration
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
LOG_FILE="/var/log/homelab-backup.log"
DOCKER_BACKUP_DIR="/tmp/docker-backup-$$"
# Export password
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
# Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "========================================"
log "Starting Homelab Backup"
log "========================================"
# Check if repository exists
if ! restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
log "ERROR: Backup repository not initialized. Run backup-init.sh first."
exit 1
fi
# Create temporary directory for Docker volume exports
mkdir -p "$DOCKER_BACKUP_DIR"
log "Created temporary backup directory: $DOCKER_BACKUP_DIR"
# Export Docker volumes
log "Exporting Docker volumes..."
# Gitea
log " - Exporting Gitea data..."
docker run --rm -v gitea_gitea:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/gitea-data.tar.gz -C /data .
# Jellyfin
log " - Exporting Jellyfin config..."
docker run --rm -v jellyfin_jellyfin_config:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/jellyfin-config.tar.gz -C /data .
# Nextcloud data
log " - Exporting Nextcloud data..."
docker run --rm -v nextcloud_nextcloud_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/nextcloud-data.tar.gz -C /data .
# Nextcloud database
log " - Exporting Nextcloud database..."
docker run --rm -v nextcloud_nextcloud_db:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/nextcloud-db.tar.gz -C /data .
# Portainer
log " - Exporting Portainer data..."
docker run --rm -v portainer_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/portainer-data.tar.gz -C /data .
log "✓ Docker volumes exported"
# Run backup
log "Running Restic backup..."
restic -r "$BACKUP_REPO" backup \
--verbose \
--tag homelab-docker \
--tag homelab-configs \
/etc/nginx/sites-available/homelab \
/etc/systemd/system/copyparty.service \
/home/hoborg/.config/copyparty/ \
"$DOCKER_BACKUP_DIR"
# Cleanup temporary files
log "Cleaning up temporary files..."
rm -rf "$DOCKER_BACKUP_DIR"
# Prune old backups (keep: 7 daily, 4 weekly, 6 monthly, 2 yearly)
log "Pruning old backups..."
restic -r "$BACKUP_REPO" forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--keep-yearly 2 \
--prune
# Show backup stats
log "Backup statistics:"
restic -r "$BACKUP_REPO" stats --mode restore-size
log "========================================"
log "Backup completed successfully!"
log "========================================"

74
scripts/backup-init.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Initialize Restic backup repository on NAS
# Run once to set up the backup system
set -e
# Configuration
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
echo "========================================"
echo "Restic Backup Repository Initialization"
echo "========================================"
echo ""
echo "This will:"
echo " 1. Install restic if needed"
echo " 2. Create backup repository at: $BACKUP_REPO"
echo " 3. Generate and save encryption password"
echo ""
# Check if restic is installed
if ! command -v restic &> /dev/null; then
echo "Installing restic..."
pacman -S --needed --noconfirm restic
echo "✓ Restic installed"
else
echo "✓ Restic already installed ($(restic version))"
fi
# Create backup directory on NAS
echo ""
echo "Creating backup directory on NAS..."
mkdir -p "$BACKUP_REPO"
echo "✓ Directory created: $BACKUP_REPO"
# Generate random password if doesn't exist
echo ""
if [ -f "$PASSWORD_FILE" ]; then
echo "✓ Password file already exists: $PASSWORD_FILE"
else
echo "Generating encryption password..."
mkdir -p /home/hoborg/creds
openssl rand -base64 32 > "$PASSWORD_FILE"
chmod 600 "$PASSWORD_FILE"
echo "✓ Password saved to: $PASSWORD_FILE"
echo "⚠️ IMPORTANT: Back up this password file! Without it, backups cannot be restored."
fi
# Export password for restic
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
# Initialize repository
echo ""
echo "Initializing Restic repository..."
if restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
echo "✓ Repository already initialized"
else
restic -r "$BACKUP_REPO" init
echo "✓ Repository initialized"
fi
echo ""
echo "========================================"
echo "Backup Repository Ready!"
echo "========================================"
echo ""
echo "Repository location: $BACKUP_REPO"
echo "Password file: $PASSWORD_FILE"
echo ""
echo "Next steps:"
echo " 1. Run backup-homelab.sh to create first backup"
echo " 2. Test restore with backup-restore.sh"
echo " 3. Enable automated backups with systemd timer"
echo ""

128
scripts/backup-system-full.sh Executable file
View File

@@ -0,0 +1,128 @@
#!/bin/bash
# Full system backup - packages, configs, and Docker volumes
# Can be run manually or via systemd timer
set -e
# Configuration
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
LOG_FILE="/var/log/homelab-backup.log"
DOCKER_BACKUP_DIR="/tmp/docker-backup-$$"
SYSTEM_BACKUP_DIR="/tmp/system-backup-$$"
# Export password
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
# Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "========================================"
log "Starting Full System Backup"
log "========================================"
# Check if repository exists
if ! restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
log "ERROR: Backup repository not initialized. Run backup-init.sh first."
exit 1
fi
# Create temporary directories
mkdir -p "$DOCKER_BACKUP_DIR"
mkdir -p "$SYSTEM_BACKUP_DIR"
log "Created temporary directories"
# ============================================
# 1. Export Package Lists
# ============================================
log "Exporting package lists..."
pacman -Qqe > "$SYSTEM_BACKUP_DIR/pacman-explicit.txt" # Explicitly installed packages
pacman -Qq > "$SYSTEM_BACKUP_DIR/pacman-all.txt" # All installed packages
pacman -Qqm > "$SYSTEM_BACKUP_DIR/aur-packages.txt" # AUR/foreign packages only
log "✓ Package lists exported ($(wc -l < $SYSTEM_BACKUP_DIR/pacman-explicit.txt) explicit, $(wc -l < $SYSTEM_BACKUP_DIR/aur-packages.txt) AUR)"
# ============================================
# 2. Export Docker Volumes
# ============================================
log "Exporting Docker volumes..."
# Gitea
log " - Exporting Gitea data..."
docker run --rm -v gitea_gitea:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/gitea-data.tar.gz -C /data .
# Jellyfin
log " - Exporting Jellyfin config..."
docker run --rm -v jellyfin_jellyfin_config:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/jellyfin-config.tar.gz -C /data .
# Nextcloud data
log " - Exporting Nextcloud data..."
docker run --rm -v nextcloud_nextcloud_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/nextcloud-data.tar.gz -C /data .
# Nextcloud database
log " - Exporting Nextcloud database..."
docker run --rm -v nextcloud_nextcloud_db:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/nextcloud-db.tar.gz -C /data .
# Portainer
log " - Exporting Portainer data..."
docker run --rm -v portainer_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
tar czf /backup/portainer-data.tar.gz -C /data .
log "✓ Docker volumes exported"
# ============================================
# 3. Run Backup
# ============================================
log "Running Restic backup..."
restic -r "$BACKUP_REPO" backup \
--verbose \
--tag full-system \
--exclude '/etc/shadow*' \
--exclude '/etc/gshadow*' \
--exclude '/etc/passwd-' \
--exclude '/etc/group-' \
/etc/ \
"$DOCKER_BACKUP_DIR" \
"$SYSTEM_BACKUP_DIR"
# ============================================
# 4. Cleanup
# ============================================
log "Cleaning up temporary files..."
rm -rf "$DOCKER_BACKUP_DIR"
rm -rf "$SYSTEM_BACKUP_DIR"
# ============================================
# 5. Prune Old Backups
# ============================================
log "Pruning old backups..."
restic -r "$BACKUP_REPO" forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 3 \
--keep-yearly 1 \
--prune
# ============================================
# 6. Show Stats
# ============================================
log "Backup statistics:"
restic -r "$BACKUP_REPO" stats --mode restore-size
log "========================================"
log "Full system backup completed!"
log "========================================"
log ""
log "Backed up:"
log " - /etc/ (all system configs)"
log " - Docker volumes (Gitea, Jellyfin, Nextcloud, Portainer)"
log " - Package lists (pacman + AUR)"
log ""
log "To restore packages after reinstall:"
log " pacman -S --needed \$(cat pacman-explicit.txt)"
log " yay -S --needed \$(cat aur-packages.txt)"

130
scripts/setup-docker-symlinks.sh Executable file
View File

@@ -0,0 +1,130 @@
#!/bin/bash
# Set up symlinks for Docker configurations to avoid manual copying
set -e # Exit on error
echo "==================================================================="
echo "Docker Configuration Symlink Setup"
echo "==================================================================="
echo ""
REPO_ROOT="/home/hoborg/homelab"
# Function to create symlink with backup
create_symlink() {
local service=$1
local target=$2
local link=$3
echo "-------------------------------------------------------------------"
echo "Setting up: $service"
echo "-------------------------------------------------------------------"
echo "Target: $target"
echo "Link: $link"
echo ""
# Check if target exists
if [ ! -f "$target" ]; then
echo "ERROR: Target file does not exist: $target"
return 1
fi
# Create parent directory if it doesn't exist
local link_dir=$(dirname "$link")
if [ ! -d "$link_dir" ]; then
echo "Creating directory: $link_dir"
mkdir -p "$link_dir"
fi
# Backup existing file if it's not already a symlink
if [ -f "$link" ] && [ ! -L "$link" ]; then
backup="${link}.backup.$(date +%Y%m%d_%H%M%S)"
echo "Backing up existing file to: $backup"
cp "$link" "$backup"
rm "$link"
elif [ -L "$link" ]; then
echo "Removing existing symlink"
rm "$link"
fi
# Create symlink
ln -s "$target" "$link"
if [ -L "$link" ]; then
echo "✓ Symlink created successfully"
else
echo "✗ Failed to create symlink"
return 1
fi
echo ""
}
# Gitea docker-compose.yml
create_symlink "Gitea docker-compose" \
"$REPO_ROOT/config/docker/gitea/docker-compose.yml" \
"/opt/docker/gitea/docker-compose.yml"
# Jellyfin docker-compose.yml
create_symlink "Jellyfin docker-compose" \
"$REPO_ROOT/config/docker/jellyfin/docker-compose.yml" \
"/opt/docker/jellyfin/docker-compose.yml"
# qBittorrent docker-compose.yml
create_symlink "qBittorrent docker-compose" \
"$REPO_ROOT/config/docker/qbittorrent/docker-compose.yml" \
"/opt/docker/qbittorrent/docker-compose.yml"
# Portainer docker-compose.yml
create_symlink "Portainer docker-compose" \
"$REPO_ROOT/config/docker/portainer/docker-compose.yml" \
"/opt/docker/portainer/docker-compose.yml"
# Copyparty docker-compose.yml
create_symlink "Copyparty docker-compose" \
"$REPO_ROOT/config/docker/copyparty/docker-compose.yml" \
"/opt/docker/copyparty/docker-compose.yml"
# Glances docker-compose.yml
create_symlink "Glances docker-compose" \
"$REPO_ROOT/config/docker/glances/docker-compose.yml" \
"/opt/docker/glances/docker-compose.yml"
# Syncthing docker-compose.yml
create_symlink "Syncthing docker-compose" \
"$REPO_ROOT/config/docker/syncthing/docker-compose.yml" \
"/opt/docker/syncthing/docker-compose.yml"
# Docker daemon config
create_symlink "Docker daemon config" \
"$REPO_ROOT/config/docker/daemon.json" \
"/etc/docker/daemon.json"
# Landing page
create_symlink "Landing page" \
"$REPO_ROOT/config/www/index.html" \
"/var/www/homelab/index.html"
echo "==================================================================="
echo "Summary"
echo "==================================================================="
echo ""
echo "✓ All symlinks created successfully!"
echo ""
echo "Notes:"
echo " - Original files backed up with timestamp"
echo " - Edit files in $REPO_ROOT/config/docker/"
echo " - Changes take effect immediately (no copying needed)"
echo " - For daemon.json: restart docker after changes"
echo " - For docker-compose: restart containers after changes"
echo ""
echo "Verify symlinks:"
echo " ls -l /opt/docker/gitea/docker-compose.yml"
echo " ls -l /opt/docker/jellyfin/docker-compose.yml"
echo " ls -l /opt/docker/qbittorrent/docker-compose.yml"
echo " ls -l /opt/docker/portainer/docker-compose.yml"
echo " ls -l /opt/docker/copyparty/docker-compose.yml"
echo " ls -l /opt/docker/glances/docker-compose.yml"
echo " ls -l /opt/docker/syncthing/docker-compose.yml"
echo " ls -l /etc/docker/daemon.json"
echo ""