Homelab infrastructure version control
Find a file
2026-09-10 21:34:56 +00:00
appdata stacks: add n8n, excalidraw, navidrome (compose + appdata markers) 2026-08-23 00:01:31 +00:00
docs Rebrand Authentik issuer to auth.aibora.org (OIDC clients: Linkwarden, FreshRSS, Komodo, Actual Budget); FreshRSS base_url -> rss.aibora.org; pgadmin blueprint -> pgadmin.local.aibora.org; docs updated 2026-08-23 22:52:49 +00:00
scripts infra: capture deployed stacks + harden gitignore 2026-07-05 23:41:05 +00:00
stacks komodo: token URL doc -> git.aibora.org 2026-09-10 21:34:56 +00:00
.gitignore infra: capture deployed stacks + harden gitignore 2026-07-05 23:41:05 +00:00
HOMELAB-OVERVIEW.md Rebrand Authentik issuer to auth.aibora.org (OIDC clients: Linkwarden, FreshRSS, Komodo, Actual Budget); FreshRSS base_url -> rss.aibora.org; pgadmin blueprint -> pgadmin.local.aibora.org; docs updated 2026-08-23 22:52:49 +00:00
README.md feat: Add memory limits and monitoring dashboard 2025-12-30 11:48:16 -05:00

Hermes Homelab Infrastructure

Professional Docker-based homelab infrastructure for media management, productivity services, monitoring, and gaming.

Table of Contents

Overview

This homelab runs 34+ containerized services across 7 Docker Compose stacks, providing:

  • Media Management: Jellyfin, Sonarr, Radarr, Lidarr, Bazarr, Prowlarr, qBittorrent
  • Productivity: Joplin, FreshRSS, Grocy, Linkwarden
  • Communication: Synapse (Matrix), Element
  • Gaming: Foundry VTT, RomM (ROM library manager)
  • Monitoring: Grafana, Prometheus, Uptime Kuma, Netbox
  • Infrastructure: Watchtower, Proton Bridge, Homepage dashboard

System Specifications

Hardware:

  • CPU: Intel(R) Core(TM) i5-1235U (12th Gen) - 12 cores
  • RAM: 31 GB
  • Storage: 468 GB system + NAS mounts
  • OS: Linux 6.8.0-88-generic

Current Resource Usage:

  • Docker Containers: 34+ running
  • Networks: 8 Docker networks (including shared-services)
  • Volumes: 50+ persistent volumes

Quick Start

Prerequisites

  1. Docker and Docker Compose installed
  2. Git repository cloned to /srv/
  3. External storage mounted at /mnt/media-das/
  4. Network access on local subnet

Initial Setup

# 1. Navigate to services stack (must be started FIRST)
cd /srv/stacks/services

# 2. Create .env file from template
cp .env.template .env
nano .env  # Fill in credentials

# 3. Start PostgreSQL and core services
docker compose up -d

# 4. Create databases for other stacks
docker exec -it postgres_hermes psql -U postgres
# Run database creation commands (see Database Management section)

# 5. Start other stacks in order
cd /srv/stacks/monitoring && docker compose up -d
cd /srv/stacks/apps && docker compose up -d
cd /srv/stacks/media && docker compose up -d
cd /srv/stacks/games-foundry && docker compose up -d
cd /srv/stacks/infra-updates && docker compose up -d

# 6. Verify all services are healthy
docker ps --format "table {{.Names}}\t{{.Status}}"

Service Stacks

1. Services Stack (Core Infrastructure)

Location: /srv/stacks/services/

Services:

  • PostgreSQL (postgres_hermes) - Central database server
  • Homepage (port 3333) - Dashboard
  • FreshRSS (port 8070) - RSS reader
  • Joplin Server (port 22300) - Note synchronization
  • Grocy (port 9283) - Grocery management
  • Linkwarden (port 3334) - Bookmark manager
  • MeiliSearch (port 7700) - Search engine

Key Details:

  • PostgreSQL password is shared across ALL stacks
  • Must be started FIRST before other stacks
  • Connected to shared-services network for cross-stack access

2. Monitoring Stack

Location: /srv/stacks/monitoring/

Services:

  • Grafana (port 3000) - Metrics visualization
  • Prometheus (port 9090) - Metrics collection
  • node_exporter (port 9100) - System metrics
  • Uptime Kuma (port 3001) - Uptime monitoring
  • Netbox (port 8000) - IPAM/DCIM
  • Redis (port 6379) - Cache for Netbox

Default Credentials:

  • Grafana: admin / admin (change on first login)
  • Netbox: admin / [NETBOX_SUPERUSER_PASSWORD from .env]

3. Apps Stack

Location: /srv/stacks/apps/

Services:

  • Vikunja (port 3456) - Task management
  • Synapse (port 8008) - Matrix homeserver
  • Element (port 8009) - Matrix web client
  • RomM (port 8080) - ROM library manager
  • MariaDB (romm-db) - RomM database

Key Details:

  • RomM uses MariaDB (not PostgreSQL)
  • ROM library mounted at /mnt/media-das/media/roms/
  • Element configured to connect to Synapse

4. Media Stack

Location: /srv/stacks/media/

Services:

  • Jellyfin (port 8096) - Media server
  • Sonarr (port 8989) - TV show management
  • Radarr (port 7878) - Movie management
  • Lidarr (port 8686) - Music management
  • Bazarr (port 6767) - Subtitle management
  • Prowlarr (port 9696) - Indexer manager
  • qBittorrent (port 8090) - Torrent client (VPN-enabled)
  • NZBGet (port 6789) - Usenet client
  • Jellyseerr (port 5055) - Media request management
  • Kavita (port 5000) - Comic/manga reader
  • AudioBookshelf (port 13378) - Audiobook server

Key Details:

  • qBittorrent runs through VPN container
  • Media stored on /mnt/media-das/media/
  • API keys required for interconnectivity

5. Games - Foundry VTT Stack

Location: /srv/stacks/games-foundry/

Services:

  • Foundry VTT Main (port 30000)
  • Foundry VTT Alt (port 30001)

Key Details:

  • Requires Foundry VTT license
  • Two instances for parallel campaigns
  • Data stored in /srv/appdata/foundry/

6. Infrastructure Updates Stack

Location: /srv/stacks/infra-updates/

Services:

  • Watchtower (port 8080 API) - Automatic container updates
  • Proton Bridge (port 25 SMTP, 143 IMAP) - Email bridge

Key Details:

  • Watchtower updates containers on schedule (default: monthly)
  • Excludes critical services from auto-update
  • Sends email notifications via Proton Bridge

7. Remote Stack

Location: /srv/stacks/remote/

Services:

  • Code Server (port 8443) - VS Code in browser
  • Guacamole (port 8084) - Remote desktop gateway

Network Architecture

Docker Networks

shared-services (bridge)
├── postgres_hermes (services stack)
├── vikunja (apps stack)
├── synapse (apps stack)
├── grafana (monitoring stack)
├── netbox (monitoring stack)
└── uptimekuma (monitoring stack)

Default per-stack networks:
- services_default
- monitoring_default
- apps_default
- media_default
- games-foundry_default
- infra-updates_default
- remote_default

Port Mapping

Service Port Stack Access
Homepage 3333 services Dashboard
Jellyfin 8096 media Media streaming
Grafana 3000 monitoring Metrics
Uptime Kuma 3001 monitoring Uptime
Netbox 8000 monitoring IPAM
Vikunja 3456 apps Tasks
Synapse 8008 apps Matrix
Element 8009 apps Matrix client
RomM 8080 apps ROM library
Foundry Main 30000 games D&D VTT
Sonarr 8989 media TV shows
Radarr 7878 media Movies
qBittorrent 8090 media Torrents
Jellyseerr 5055 media Requests

Directory Structure

/srv/
├── stacks/                    # Docker Compose configurations
│   ├── services/              # Core services (PostgreSQL, Homepage, etc.)
│   ├── monitoring/            # Grafana, Prometheus, Netbox
│   ├── apps/                  # Vikunja, Synapse, RomM
│   ├── media/                 # Jellyfin, Sonarr, Radarr, etc.
│   ├── games-foundry/         # Foundry VTT instances
│   ├── infra-updates/         # Watchtower, Proton Bridge
│   └── remote/                # Code Server, Guacamole
│
├── appdata/                   # Persistent application data
│   ├── services/              # Service stack data
│   ├── monitoring/            # Monitoring stack data
│   ├── apps/                  # Apps stack data
│   ├── media/                 # Media stack data
│   ├── foundry/               # Foundry VTT data
│   ├── infra-updates/         # Infrastructure data
│   └── remote/                # Remote access data
│
└── README.md                  # This file

/mnt/media-das/media/          # NAS-mounted storage
├── movies/                    # Movie library
├── tv/                        # TV show library
├── music/                     # Music library
├── audiobooks/                # Audiobook library
├── comics/                    # Comic/manga library
├── downloads/                 # Download directory
└── roms/                      # ROM library for RomM
    ├── TP/roms/              # Platform folders
    ├── nds/roms/
    ├── gameboy-color/roms/
    ├── mame/roms/
    └── snes/roms/

Configuration Management

Environment Variables

Each stack has:

  • .env file - Active configuration (gitignored, contains secrets)
  • .env.template file - Template for new deployments (tracked in Git)

Setup Process:

# Copy template
cp .env.template .env

# Edit with your values
nano .env

# Replace all CHANGE_ME values

Shared Credentials

PostgreSQL Root Password:

  • Set in /srv/stacks/services/.env as PG_ROOT_PASSWORD
  • Must be copied to monitoring and apps stacks
  • Used by: Grafana, Netbox, Uptime Kuma, Vikunja, Synapse

API Key Generation: Most services generate API keys after first start:

  1. Start the service
  2. Access the web UI
  3. Navigate to Settings → General → Security
  4. Generate API key
  5. Add to .env file
  6. Restart dependent services

Configuration Files

Some services require configuration files beyond environment variables:

Service Config File Purpose
Vikunja /srv/appdata/apps/vikunja/config.yml CORS, database settings
RomM /srv/appdata/apps/romm/config.yml Database, auth settings
Netbox /srv/appdata/monitoring/netbox/config/configuration.py Database, Redis, secret key
Prometheus /srv/appdata/monitoring/prometheus/config/prometheus.yml Scrape targets
Homepage /srv/appdata/services/homepage/config/*.yaml Dashboard configuration

Database Management

PostgreSQL (postgres_hermes)

Connection Details:

  • Host: postgres_hermes (internal) or localhost:5432 (external)
  • User: postgres
  • Password: [PG_ROOT_PASSWORD from services/.env]

Database Creation Commands:

-- Connect to PostgreSQL
docker exec -it postgres_hermes psql -U postgres

-- Services stack databases
CREATE DATABASE freshrss_db;
CREATE USER freshrss WITH PASSWORD '[FRESHRSS_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE freshrss_db TO freshrss;

CREATE DATABASE grocy_db;
CREATE USER grocy WITH PASSWORD '[GROCY_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE grocy_db TO grocy;

CREATE DATABASE joplin_db;
CREATE USER joplin WITH PASSWORD '[JOPLIN_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE joplin_db TO joplin;

CREATE DATABASE linkwarden_db;
CREATE USER linkwarden WITH PASSWORD '[LINKWARDEN_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE linkwarden_db TO linkwarden;

-- Monitoring stack databases
CREATE DATABASE grafana_db;
CREATE USER grafana WITH PASSWORD '[GRAFANA_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE grafana_db TO grafana;

CREATE DATABASE netbox_db;
CREATE USER netbox WITH PASSWORD '[NETBOX_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE netbox_db TO netbox;

CREATE DATABASE uptimekuma_db;
CREATE USER uptimekuma WITH PASSWORD '[UPTIMEKUMA_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE uptimekuma_db TO uptimekuma;

-- Apps stack databases
CREATE DATABASE vikunja_db;
CREATE USER vikunja WITH PASSWORD '[VIKUNJA_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE vikunja_db TO vikunja;

CREATE DATABASE synapse_db;
CREATE USER synapse WITH PASSWORD '[SYNAPSE_DB_PASSWORD]';
GRANT ALL PRIVILEGES ON DATABASE synapse_db TO synapse;

-- Grant schema permissions (PostgreSQL 15+ requirement)
\c vikunja_db
GRANT ALL ON SCHEMA public TO vikunja;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO vikunja;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO vikunja;

\c synapse_db
GRANT ALL ON SCHEMA public TO synapse;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO synapse;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO synapse;

\c grafana_db
GRANT ALL ON SCHEMA public TO grafana;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO grafana;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO grafana;

\c netbox_db
GRANT ALL ON SCHEMA public TO netbox;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO netbox;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO netbox;

\c uptimekuma_db
GRANT ALL ON SCHEMA public TO uptimekuma;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO uptimekuma;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO uptimekuma;

MariaDB (romm-db)

RomM v4 uses MariaDB instead of PostgreSQL. The database is automatically created by the container.

Connection Details:

  • Host: romm-db
  • Port: 3306
  • Database: romm_db
  • User: romm
  • Password: [ROMM_DB_PASSWORD from apps/.env]

Backup Strategy

Database Backups

PostgreSQL:

# Backup all databases
docker exec postgres_hermes pg_dumpall -U postgres > /srv/backups/postgres_backup_$(date +%Y%m%d).sql

# Backup specific database
docker exec postgres_hermes pg_dump -U postgres -d vikunja_db > /srv/backups/vikunja_backup_$(date +%Y%m%d).sql

# Restore database
docker exec -i postgres_hermes psql -U postgres < /srv/backups/postgres_backup_20250101.sql

MariaDB (RomM):

# Backup RomM database
docker exec romm-db mysqldump -u romm -p'[ROMM_DB_PASSWORD]' romm_db > /srv/backups/romm_backup_$(date +%Y%m%d).sql

# Restore RomM database
docker exec -i romm-db mysql -u romm -p'[ROMM_DB_PASSWORD]' romm_db < /srv/backups/romm_backup_20250101.sql

Application Data Backups

# Backup all appdata
tar -czf /srv/backups/appdata_backup_$(date +%Y%m%d).tar.gz /srv/appdata/

# Backup specific stack
tar -czf /srv/backups/monitoring_backup_$(date +%Y%m%d).tar.gz /srv/appdata/monitoring/

# Restore appdata
tar -xzf /srv/backups/appdata_backup_20250101.tar.gz -C /

Configuration Backups

# Backup all compose configurations
tar -czf /srv/backups/stacks_backup_$(date +%Y%m%d).tar.gz /srv/stacks/ --exclude='*.env'

# Backup environment templates (safe to commit to Git)
cp /srv/stacks/*/.env.template /srv/backups/env-templates/

Maintenance

Regular Tasks

Daily:

  • Monitor container health: docker ps --filter "health=unhealthy"
  • Check disk space: df -h
  • Review logs for errors: docker logs [container] --since 24h

Weekly:

  • Review Uptime Kuma for service outages
  • Check Grafana dashboards for resource usage trends
  • Review Watchtower logs for update failures

Monthly:

  • Update containers manually (critical services)
  • Backup databases
  • Review and rotate logs
  • Clean up unused Docker resources: docker system prune -a

Service Updates

Automatic Updates (via Watchtower):

  • Runs on schedule (default: monthly at 1 AM)
  • Excludes critical services: jellyfin, joplin, meili_search, postgres_hermes, foundry-main, foundry-alt

Manual Updates (for critical services):

# 1. Backup database first
# 2. Pull new image
docker compose pull [service]

# 3. Recreate container
docker compose up -d [service]

# 4. Verify functionality
docker logs -f [service]

Log Management

View logs:

# Follow live logs
docker logs -f [container]

# View last 100 lines
docker logs --tail 100 [container]

# View logs since timestamp
docker logs --since 2024-01-01T00:00:00 [container]

Rotate logs:

# Configure in /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Resource Monitoring

Check resource usage:

# Overall stats
docker stats

# Specific container
docker stats [container]

# System resources
htop

Troubleshooting

Common Issues

1. Container Unhealthy

Symptom: docker ps shows unhealthy status

Diagnosis:

# Check logs
docker logs [container]

# Inspect health check
docker inspect [container] | jq '.[0].State.Health'

# Test health endpoint manually
docker exec [container] curl -f http://localhost:[port]/health

Common Fixes:

  • Verify database connectivity
  • Check configuration file syntax
  • Ensure proper permissions on mounted volumes
  • Review healthcheck endpoint (use unauthenticated routes)

2. Database Connection Failed

Symptom: Service can't connect to PostgreSQL

Diagnosis:

# Check PostgreSQL is running
docker ps | grep postgres_hermes

# Check if container is on shared-services network
docker network inspect shared-services

# Test connection from container
docker exec [container] ping postgres_hermes

Fix:

# Connect container to shared-services network
docker network connect shared-services [container]

# Verify PostgreSQL password matches in .env files

3. Permission Denied Errors (PostgreSQL 15+)

Symptom: pq: permission denied for schema public

Fix:

docker exec -it postgres_hermes psql -U postgres -d [database]
GRANT ALL ON SCHEMA public TO [user];
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO [user];
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO [user];

4. RomM Not Scanning Library

Symptom: RomM shows "Directory not found" errors

Fix:

  • Verify ROM folder structure: /platform/roms/game/
  • Check mount permissions: ls -la /mnt/media-das/media/roms/
  • Ensure config.yml is writable (644 permissions)
  • Verify MariaDB is healthy: docker logs romm-db

5. Watchtower Not Sending Emails

Symptom: No email notifications after updates

Diagnosis:

# Check Watchtower logs
docker logs watchtower

# Test Proton Bridge connectivity
docker exec watchtower nc -zv proton-bridge 25

Fix:

  • Verify SMTP credentials in .env
  • Ensure Proton Bridge is running
  • Check both containers are on same network

Health Check Commands

# Check all container statuses
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# Check network connectivity
docker network ls
docker network inspect shared-services

# Check volume mounts
docker inspect [container] | jq '.[0].Mounts'

# Check environment variables
docker exec [container] env | grep -i db

# Check disk space
df -h /srv/appdata/
df -h /mnt/media-das/

Service-Specific Troubleshooting

Vikunja:

  • Check /srv/appdata/apps/vikunja/config.yml exists
  • Verify CORS settings include publicurl
  • Ensure PostgreSQL connection on shared-services network

RomM:

  • Verify MariaDB (not PostgreSQL) connection
  • Check folder structure: platform/roms/ required
  • Ensure config.yml permissions: 644

Netbox:

  • Verify Redis is running
  • Check configuration.py exists
  • Ensure SECRET_KEY is 50+ characters
  • Healthcheck should use /login/ not /api/

Grafana:

  • Default login: admin/admin
  • Check database connection to postgres_hermes
  • Verify network connectivity to Prometheus

Support and Documentation

Official Documentation:

  • Each stack folder contains a detailed README.md
  • Service-specific configurations in /srv/appdata/*/README.md
  • Environment variable templates: .env.template files

Useful Commands:

# Quick health check
cd /srv && find stacks/ -name compose.yml -execdir docker compose ps \;

# Restart all services
cd /srv && find stacks/ -name compose.yml -execdir docker compose restart \;

# View all logs
cd /srv && find stacks/ -name compose.yml -execdir docker compose logs --tail=50 \;

SSL Certificates

SSL certificates for all homelab services are automatically managed by Traefik running on the edge device (Raspberry Pi) using Let's Encrypt with Cloudflare DNS challenge.

Current Domains

  • claudiogoncalves.me (wildcard *.claudiogoncalves.me)
  • local.claudiogoncalves.me (wildcard *.local.claudiogoncalves.me)
  • whoami.claudiogoncalves.me
  • foundryvtt.realidados.com

Certificate Management

  • Certificate Authority: Let's Encrypt (ACME v2)
  • Validation Method: DNS-01 challenge via Cloudflare API
  • Certificate Lifetime: 90 days
  • Auto-Renewal: Traefik automatically renews certificates 30 days before expiration
  • Storage: /srv/appdata/pi-core/traefik/acme.json on edge device

Backup & Recovery

  • SSL certificates are backed up daily as part of the edge device restic backup
  • In case of certificate loss, Traefik will automatically request new certificates on startup
  • Backup location: Garage S3 at s3://10.8.29.17:3900/restic-backup

Monitoring

  • Check certificate status in Traefik dashboard: https://traefik-dashboard.local.claudiogoncalves.me
  • View Traefik logs: ssh rpi "docker logs traefik --tail 100"
  • Verify certificate expiration: Check browser certificate details or use openssl s_client

Adding New Domains

  1. Update DNS records in Cloudflare to point to your public IP
  2. Add Traefik route in /srv/stacks/pi-core/traefik/dynamic/[service].yml
  3. Traefik will automatically request and configure SSL certificate

Last Updated: December 2025 Maintained By: Hermes Homelab Team Version: 1.0