- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .opencode/skills | ||
| asleeponthejob.info | ||
| emocows.com | ||
| scripts | ||
| system/docker | ||
| .gitignore | ||
| AGENTS.md | ||
| opencode.json.example | ||
| README.md | ||
Livia Server Configuration
Created: 2026-07-06 Updated: 2026-08-30 Server: livia (192.168.50.4) OS: Ubuntu 24.04.4 LTS
Directory Layout
livia/
├── asleeponthejob.info/
│ ├── forgejo/ # Forgejo git server
│ │ ├── compose.yml
│ │ ├── backup.sh
│ │ └── README.md
│ ├── portainer/ # Portainer Docker management
│ │ └── compose.yml
│ ├── traefik/ # Traefik reverse proxy
│ │ ├── compose.yml
│ │ ├── traefik.env.example
│ │ ├── traefik.yml
│ │ ├── dynamic.yml
│ │ ├── backup.sh
│ │ └── README.md
│ └── wud/ # WUD (What's Up Docker?)
│ ├── compose.yml
│ ├── wud.env.example
│ ├── backup.sh
│ └── README.md
├── emocows.com/
│ ├── audiobookshelf/ # Audiobookshelf audiobook server
│ │ └── compose.yml
│ ├── authentik/ # Authentik OIDC provider
│ │ ├── docker-compose.yml
│ │ ├── .env.example
│ │ └── README.md
│ ├── immich/ # Immich photo management
│ │ ├── docker-compose.yml
│ │ ├── .env.example
│ │ └── README.md
│ ├── jellyfin/ # Jellyfin media server
│ │ ├── compose.yml
│ │ ├── encoding.xml
│ │ ├── system.xml
│ │ ├── backup.sh
│ │ └── README.md
│ ├── mealie/ # Mealie recipe manager
│ │ ├── compose.yml
│ │ ├── mealie.env.example
│ │ ├── backup.sh
│ │ └── README.md
│ ├── media-stack/ # Media management stack
│ │ ├── docker-compose.yml
│ │ └── README.md
│ └── navidrome/ # Navidrome music server
│ └── compose.yml
├── scripts/ # Maintenance and deployment scripts
│ ├── enable-maintenance
│ ├── disable-maintenance
│ ├── deploy-updates.sh
│ ├── livia-update.sh
│ └── backup-databases.sh
├── ssh/ # SSH configuration
│ ├── config
│ └── livia.pub
├── system/ # System configuration
│ ├── docker/daemon.json
│ ├── journald.conf
│ ├── fstab
│ └── swappiness.conf
├── .gitignore
├── AGENTS.md
├── README.md
└── opencode.json.example
Services Overview
Public Services (Let's Encrypt TLS)
| Service | Domain | Internal Port |
|---|---|---|
| Traefik | traefik.asleeponthejob.info | 443 |
| Authentik | auth.emocows.com | 9000 |
| Forgejo | git.asleeponthejob.info | 3000 |
| Immich | pics.emocows.com | 2283 |
| Jellyfin | media.emocows.com | 8096 |
| Mealie | meals.emocows.com | 9925 |
| Navidrome | music.emocows.com | 4533 |
| Portainer | portainer.asleeponthejob.info | 9443 |
| WUD | wud.asleeponthejob.info | 3000 |
| Audiobookshelf | books.emocows.com | 80 |
Internal Services (Self-Signed CA TLS)
| Service | Domain | Internal Port |
|---|---|---|
| Prowlarr | prowlarr.emocows.com | 9696 |
| Sonarr | sonarr.emocows.com | 8989 |
| Radarr | radarr.emocows.com | 7878 |
| Lidarr | lidarr.emocows.com | 8686 |
| qBittorrent | qbittorrent.emocows.com | 1330 |
Internal-Only Services
| Service | Purpose | Internal Port |
|---|---|---|
| Forgejo Postgres | Forgejo database | 5432 |
| Mealie Postgres | Mealie database | 5432 |
| Authentik Postgres | Authentik database | 5432 |
| Immich Postgres | Immich database | 5432 |
Hardware
- ASUS desktop, NVIDIA GeForce GTX 1080
- 31 GiB RAM, 238.5G NVMe (root) + 894.3G SATA (/home)
- Media: /mnt/Entertainment/{Movies,Music,TvSeries,Audiobooks,Podcasts}
- Downloads: /mnt/Downloads
- Photos: /mnt/Pics
Secrets Management
All secrets are stored in 1Password "Home Lab" vault. No secrets are committed to this repository.
1Password Items
| 1Password Item | Type | Purpose |
|---|---|---|
| Traefik (Dashboard) | Login | Dashboard password (hash in traefik.env) |
| Arista (Authentik) | Login | Web UI login (akadmin) |
| Jellyfin (Livia) | Login | Web UI login |
| Mealie (Homelab) | Login | Web UI login |
| Mealie Env | Document | .env file with DB + OIDC secrets |
| Livia (Portainer) | Login | Web UI login |
| Arista (Immich) | Login | Web UI login |
| WUD | Login | OIDC + SMTP secrets (custom fields) |
| Livia Backup | Document | Backup archive |
On-Server Environment Files
| File | 1Password Source | Purpose |
|---|---|---|
/srv/traefik/traefik.env |
Traefik (Dashboard) | Dashboard basic auth hash |
/srv/authentik/.env |
Manual creation | DB password, secret key |
/srv/mealie/mealie.env |
Mealie Env (document) | DB passwords, OIDC credentials |
/srv/immich/.env |
Manual creation | DB password |
/srv/wud/wud.env |
WUD (custom fields) | OIDC client ID + secret, SMTP password |
/srv/forgejo/forgejo.env |
Manual creation | DB password, Forgejo config |
Environment File Templates
Template files (.env.example) are provided in each service directory. To create an environment file:
# Example for Traefik (generate hash from password)
op item get "Traefik (Dashboard)" --vault "Home Lab" --fields password --reveal | \
jq -r '.password' | openssl passwd -apr1 -stdin
# Create traefik.env with: TRAEFIK_DASHBOARD_USERS=admin:<hash>
# Example for Mealie (download document)
op document get "Mealie Env" --vault "Home Lab" > /srv/mealie/mealie.env
chmod 600 /srv/mealie/mealie.env
Backup Scripts
Per-Service Backups
Each service has a backup.sh script in its directory:
# Jellyfin
cd emocows.com/jellyfin && ./backup.sh --local
# Mealie
cd emocows.com/mealie && ./backup.sh --local
# Traefik
cd asleeponthejob.info/traefik && ./backup.sh --local
# WUD
cd asleeponthejob.info/wud && ./backup.sh --local
# Forgejo
cd asleeponthejob.info/forgejo && ./backup.sh --local
# Restore (Jellyfin and Mealie only)
cd emocows.com/jellyfin && ./backup.sh --restore
cd emocows.com/mealie && ./backup.sh --restore
Database Backups (scripts/backup-databases.sh)
Runs on livia (not via SSH from client). Backs up all PostgreSQL databases:
| Container | Database | User |
|---|---|---|
mealie-postgres |
mealie | mealie |
immich_postgres |
immich | postgres |
authentik_postgres |
authentik | authentik |
forgejo-postgres |
forgejo | forgejo |
Always run a database backup before destructive actions (container upgrades, compose changes, migrations):
# Backup all databases (runs on livia)
cat scripts/backup-databases.sh | ssh livia "sudo bash -s"
# Backup a single database directly (e.g., Mealie)
ssh livia "sudo docker exec mealie-postgres pg_dump -U mealie -d mealie --no-owner --no-acl | gzip" > mealie-backup.sql.gz
# Restore from latest backups
cat scripts/backup-databases.sh | ssh livia "sudo bash -s -- --restore"
- Backups are stored at
/opt/backups/{database}/on livia - Retention: 7 days (older backups are auto-rotated)
- Format:
.sql.gz(compressed PostgreSQL dump, restorable withzcat ... | psql) - Local backup copies saved to this machine when using
>redirection
Quick Restore
# 1. Fresh Ubuntu 24.04 install
# 2. Install Docker + nvidia-container-toolkit
# 3. Install cron: sudo apt install cron
# 4. Grab "Livia Backup" document from 1Password and extract
# 5. Clone this repository
# 6. Create environment files from 1Password:
# - /srv/mealie/mealie.env (from "Mealie Env" document)
# - /srv/traefik/traefik.env (generate hash from "Traefik (Dashboard)")
# - /srv/wud/wud.env (from "WUD" custom fields)
# - /srv/authentik/.env (manual creation)
# - /srv/immich/.env (manual creation)
# - /srv/forgejo/forgejo.env (manual creation)
# 7. Create required directories:
# sudo mkdir -p /srv/{traefik,mealie,authentik,immich,wud,forgejo,navidrome,audiobookshelf}
# 8. Deploy services (in order):
# cd asleeponthejob.info/traefik && sudo docker compose up -d
# cd ../portainer && sudo docker compose up -d
# cd ../../emocows.com/authentik && sudo docker compose up -d
# cd ../mealie && sudo docker compose up -d
# cd ../immich && sudo docker compose up -d
# cd ../jellyfin && sudo docker compose up -d
# cd ../media-stack && sudo docker compose up -d
# cd ../navidrome && sudo docker compose up -d
# cd ../audiobookshelf && sudo docker compose up -d
# cd ../../asleeponthejob.info/wud && sudo docker compose up -d
# cd ../forgejo && sudo docker compose up -d
# 9. Verify: sudo docker ps
Network Architecture
Internet → Router (port 80/443) → Synology NAS (192.168.50.2) → Livia (192.168.50.4)
- Synology forwards ports 80/443 to livia
- Traefik handles HTTP-01 challenge on port 80
- Let's Encrypt issues certificates for public domains
- HTTP→HTTPS redirect via Traefik middleware
Certificate Architecture
Traefik uses a hybrid TLS approach: Let's Encrypt for public sites and self-signed CA-signed certs for internal sites.
How It Works
- Public sites (have DNS A records) → Let's Encrypt via HTTP-01 challenge
- Internal sites (LAN only) → Individual per-service certs signed by Livia Home Lab CA, each with a SAN (Subject Alternative Name)
- Why not wildcards? — Wildcard certs (
*.emocows.com) block Let's Encrypt ACME challenges; Traefik sees them as "provided" and skips HTTP-01 - SAN matching — Each cert has a SAN matching its domain (e.g.,
DNS:radarr.emocows.com) - Fallback — If no matching cert, Traefik uses the default certificate
Certificate Hierarchy
Livia Home Lab CA (self-signed root, 10-year validity)
├── media.emocows.com
├── prowlarr.emocows.com
├── sonarr.emocows.com
├── radarr.emocows.com
├── lidarr.emocows.com
├── qbittorrent.emocows.com
├── portainer.asleeponthejob.info
├── traefik.asleeponthejob.info
└── wud.asleeponthejob.info
Certificate Files (on-server at /srv/traefik/certs/)
| File | Purpose |
|---|---|
ca.crt / ca.key |
CA root certificate and private key (install ca.crt on clients) |
{service}.crt |
Leaf certificate for each service |
{service}.key |
Private key for each service |
{service}-chain.crt |
Full chain (leaf + CA) — what Traefik serves |
acme.json |
Let's Encrypt certificates (auto-managed, in /srv/traefik/) |
Installation on Client Devices
To avoid browser warnings, install the Livia Home Lab CA certificate in your device's trust store:
macOS:
scp livia:/srv/traefik/certs/ca.crt /tmp/livia-ca.crt
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/livia-ca.crt
Windows:
Chrome, Edge, and other Chromium-based browsers (Vivaldi, Brave) use the Windows certificate store, so a single system-wide install covers them all. Firefox has its own store and needs a separate import.
System-wide install (covers Chrome, Edge, Vivaldi, Brave):
- Copy
ca.crtfrom livia to the Windows machine - Double-click
ca.crt→ Install Certificate - Select Local Machine (requires admin) → Next
- Select Place all certificates in the following store → Browse → Trusted Root Certification Authorities → OK → Next → Finish
- Accept the security warning → Done
PowerShell (alternative):
# Import to the Local Machine trust store (run as Administrator)
Import-Certificate -FilePath "C:\path\to\ca.crt" -CertStoreLocation Cert:\LocalMachine\Root
Firefox: Firefox uses its own certificate store and does not trust the Windows store by default.
- Open Firefox → Settings → Privacy & Security → Certificates → View Certificates
- Authorities → Import → select
ca.crt - Check Trust this CA to identify websites → OK
- Restart Firefox
Note: After installing, restart all browsers for changes to take effect.
Linux (system-wide):
scp livia:/srv/traefik/certs/ca.crt /usr/local/share/ca-certificates/livia-ca.crt
sudo update-ca-certificates
Linux (Chromium/Vivaldi/Chrome/Edge): Chromium-based browsers use their own NSS certificate database, separate from the system trust store.
# Install certutil
sudo apt-get install -y libnss3-tools
# Add the CA to the Chromium NSS trust store
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "Livia Home Lab CA" -i /path/to/ca.crt
# Restart the browser for changes to take effect
Linux (Firefox):
- Settings → Privacy & Security → Certificates → View Certificates
- Authorities → Import → select
ca.crt - Check "Trust to identify websites"
iOS/Android:
- Download
ca.crtfromhttps://traefik.asleeponthejob.info(accept warning first) - Install as trusted certificate in device settings
Regenerating Certificates
The server uses individual per-service certificates (not wildcards) because wildcards block Let's Encrypt ACME challenges. Each cert must include a Subject Alternative Name (SAN) — modern browsers ignore the CN field entirely.
Services and their domains:
| Service | Domain |
|---|---|
| media | media.emocows.com |
| prowlarr | prowlarr.emocows.com |
| sonarr | sonarr.emocows.com |
| radarr | radarr.emocows.com |
| lidarr | lidarr.emocows.com |
| qbittorrent | qbittorrent.emocows.com |
| traefik-dash | traefik.asleeponthejob.info |
| wud | wud.asleeponthejob.info |
| portainer | portainer.asleeponthejob.info |
To regenerate a single cert:
ssh livia
# Example: regenerate sonarr cert (repeat for each service)
DOMAIN="sonarr.emocows.com"
NAME="sonarr"
CERT_DIR="/srv/traefik/certs"
# Generate CSR with SAN
sudo openssl req -new -key "$CERT_DIR/$NAME.key" -out "/tmp/$NAME.csr" \
-subj "/CN=$DOMAIN" -addext "subjectAltName=DNS:$DOMAIN"
# Sign with CA
sudo openssl x509 -req -in "/tmp/$NAME.csr" \
-CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" \
-CAcreateserial -out "$CERT_DIR/$NAME.crt" -days 3650 \
-copy_extensions copyall
# Build full chain (leaf + CA)
sudo cat "$CERT_DIR/$NAME.crt" "$CERT_DIR/ca.crt" > "$CERT_DIR/$NAME-chain.crt"
# Verify SANs are present
sudo openssl x509 -in "$CERT_DIR/$NAME.crt" -noout -ext subjectAltName
# Restart Traefik
sudo docker restart traefik
To regenerate the CA (only if compromised or expired):
ssh livia
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /srv/traefik/certs/ca.key -out /srv/traefik/certs/ca.crt \
-subj '/CN=Livia Home Lab CA'
# Then regenerate ALL per-service certs (they're signed by the CA)
Configuration Files
| File | Purpose |
|---|---|
traefik/traefik.yml |
Static config: entrypoints, ACME, file provider |
traefik/dynamic.yml |
Dynamic config: self-signed certs, TLS store |
traefik/compose.yml |
Container labels for routing |
WUD (What's Up Docker?)
- Monitors all containers for updates
- Notifications: Gmail SMTP, batch mode, Friday mornings
- OIDC: Authentik at auth.emocows.com
- CORS: disabled
- Delete: through UI only