Compare commits
11 Commits
6894c5f175
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 059daa77aa | |||
| 9aa881d895 | |||
| ab4a4cfc9c | |||
| 646d3e59bc | |||
| ad7270aa74 | |||
| 7e64557f0b | |||
| acc30e7c3d | |||
| 3d57327146 | |||
| d0727bd8a7 | |||
| 7eaf16f53e | |||
| bab97f7166 |
19
TODO.md
19
TODO.md
@@ -23,7 +23,7 @@
|
|||||||
- [ ] WireGuard VPN server configuration
|
- [ ] WireGuard VPN server configuration
|
||||||
- [ ] UFW firewall setup and rules
|
- [ ] UFW firewall setup and rules
|
||||||
- [ ] fail2ban for intrusion prevention
|
- [ ] 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
|
## Git & Development
|
||||||
- [x] Gitea Docker container setup *(completed - running on port 3000)*
|
- [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)
|
- [x] Shared media folders with Copyparty (Music, Videos, shared)
|
||||||
- [ ] Set up self-hosted chat server (Matrix or Mattermost)
|
- [ ] Set up self-hosted chat server (Matrix or Mattermost)
|
||||||
- [ ] Install monitoring and management tools *(in progress)*
|
- [ ] Install monitoring and management tools *(in progress)*
|
||||||
- [ ] Portainer (Docker management with built-in auth)
|
- [x] Portainer (Docker management with built-in auth)
|
||||||
- [ ] Glances (system monitoring with nginx basic auth)
|
- [x] Glances (system monitoring with nginx basic auth)
|
||||||
- [ ] Cockpit (system administration with PAM auth)
|
|
||||||
- [ ] lazydocker (terminal Docker management)
|
- [ ] lazydocker (terminal Docker management)
|
||||||
- [ ] Configure nginx basic auth for Glances endpoint
|
- [ ] Configure nginx basic auth for Glances endpoint
|
||||||
- [ ] Update nginx reverse proxy config for new admin services
|
- [ ] 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
|
- [ ] Set up Nextcloud for advanced file synchronization features
|
||||||
- Copyparty covers basic file sharing needs
|
- Copyparty covers basic file sharing needs
|
||||||
- [x] Set up reverse proxy with SSL certificates *(completed - HTTPS working with auto-renewal)*
|
- [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
|
- Gitea: ✅ Docker
|
||||||
- Jellyfin: ✅ Docker
|
- Jellyfin: ✅ Docker
|
||||||
- Copyparty: ❌ systemd service (consider dockerizing)
|
- qBittorrent: ✅ Docker
|
||||||
- Nginx: ❌ system package (fine as-is for reverse proxy)
|
|
||||||
- Portainer: ✅ Docker
|
- Portainer: ✅ Docker
|
||||||
- Glances: ❌ system package (web server mode)
|
- Copyparty: ✅ Docker
|
||||||
- Cockpit: ❌ system package (system integration required)
|
- 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
|
## Hardware & Troubleshooting
|
||||||
- [ ] Fix bluetooth audio connectivity issues
|
- [ ] Fix bluetooth audio connectivity issues
|
||||||
|
|||||||
@@ -1,77 +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
|
|
||||||
|
|
||||||
[/installers]
|
|
||||||
/mnt/nas/installers
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/torrent]
|
|
||||||
/mnt/nas/torrent
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
90
config/docker/copyparty/docker-compose.yml
Normal file
90
config/docker/copyparty/docker-compose.yml
Normal 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
|
||||||
@@ -4,9 +4,16 @@
|
|||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
server_name ak-homelab.duckdns.org _;
|
server_name ak-homelab.duckdns.org _;
|
||||||
|
|
||||||
# Redirect HTTP to HTTPS
|
# Allow Let's Encrypt ACME challenges
|
||||||
return 301 https://$host$request_uri;
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect all other HTTP to HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -176,6 +183,32 @@ server {
|
|||||||
proxy_set_header Connection "upgrade";
|
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 /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
|
ssl_certificate_key /etc/letsencrypt/live/ak-homelab.duckdns.org/privkey.pem; # managed by Certbot
|
||||||
|
|||||||
@@ -140,10 +140,10 @@
|
|||||||
<h3>Copyparty</h3>
|
<h3>Copyparty</h3>
|
||||||
<p>File Server & WebDAV</p>
|
<p>File Server & WebDAV</p>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="service disabled" onclick="return false;">
|
<a href="/cloud/" class="service">
|
||||||
<i class="fas fa-cloud"></i>
|
<i class="fas fa-cloud"></i>
|
||||||
<h3>Nextcloud</h3>
|
<h3>Nextcloud</h3>
|
||||||
<p>Temporarily Disabled</p>
|
<p>Cloud Storage & Collaboration</p>
|
||||||
</a>
|
</a>
|
||||||
<a href="/media/" class="service">
|
<a href="/media/" class="service">
|
||||||
<i class="fas fa-play-circle"></i>
|
<i class="fas fa-play-circle"></i>
|
||||||
|
|||||||
233
docs/system-backups.md
Normal file
233
docs/system-backups.md
Normal 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)
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# Voice Assistant Setup
|
# 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.
|
This document describes how to set up AI voice capabilities for Claude Code using local TTS (Text-to-Speech) services.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -8,6 +10,7 @@ The voice assistant setup uses:
|
|||||||
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
|
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
|
||||||
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
|
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
|
||||||
- **Ryan voice model**: Professional male US English voice for AI assistant personality
|
- **Ryan voice model**: Professional male US English voice for AI assistant personality
|
||||||
|
- **onnxruntime**: ML inference library (removed - required for TTS)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|||||||
95
scripts/backup-homelab.sh
Executable file
95
scripts/backup-homelab.sh
Executable 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
74
scripts/backup-init.sh
Executable 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
128
scripts/backup-system-full.sh
Executable 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)"
|
||||||
@@ -29,6 +29,13 @@ create_symlink() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
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
|
# Backup existing file if it's not already a symlink
|
||||||
if [ -f "$link" ] && [ ! -L "$link" ]; then
|
if [ -f "$link" ] && [ ! -L "$link" ]; then
|
||||||
backup="${link}.backup.$(date +%Y%m%d_%H%M%S)"
|
backup="${link}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
@@ -73,11 +80,31 @@ create_symlink "Portainer docker-compose" \
|
|||||||
"$REPO_ROOT/config/docker/portainer/docker-compose.yml" \
|
"$REPO_ROOT/config/docker/portainer/docker-compose.yml" \
|
||||||
"/opt/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
|
# Docker daemon config
|
||||||
create_symlink "Docker daemon config" \
|
create_symlink "Docker daemon config" \
|
||||||
"$REPO_ROOT/config/docker/daemon.json" \
|
"$REPO_ROOT/config/docker/daemon.json" \
|
||||||
"/etc/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 "==================================================================="
|
||||||
echo "Summary"
|
echo "Summary"
|
||||||
echo "==================================================================="
|
echo "==================================================================="
|
||||||
@@ -96,5 +123,8 @@ echo " ls -l /opt/docker/gitea/docker-compose.yml"
|
|||||||
echo " ls -l /opt/docker/jellyfin/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/qbittorrent/docker-compose.yml"
|
||||||
echo " ls -l /opt/docker/portainer/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 " ls -l /etc/docker/daemon.json"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user