Complete installation, setup, and configuration guide for loose-seal as the centralized monitoring and dashboards server for the homelab infrastructure.
## Update system packages
sudo apt update && sudo apt upgrade -y
## Install essential packages
sudo apt install -y \
curl \
wget \
git \
htop \
unzip \
nano \
software-properties-common \
apt-transport-https \
ca-certificates \
gnupg \
lsb-release \
ufw \
fail2ban
## Set timezone
sudo timedatectl set-timezone America/New_York
## Create 4GB swap file
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
## Make swap permanent
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
## Verify swap
sudo swapon --show
free -h
## Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
## Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
## Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
## Add user to docker group
sudo usermod -aG docker $USER
## Enable Docker service
sudo systemctl enable docker
sudo systemctl start docker
## Verify installation
docker --version
docker compose version
## Create Docker daemon configuration
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<EOF
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"default-address-pools": [
{
"base": "172.16.0.0/12",
"size": 24
}
]
}
EOF
## Restart Docker with new configuration
sudo systemctl restart docker
## Reset and configure UFW
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
## Allow SSH from anywhere (will be restricted to VPN later)
sudo ufw allow ssh
## Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
## Enable firewall
sudo ufw --force enable
sudo ufw status
## Backup original SSH config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
## Create hardened SSH config
sudo tee /etc/ssh/sshd_config.d/99-security.conf <<EOF
## Disable root login
PermitRootLogin no
## Key-based authentication only
PasswordAuthentication no
PubkeyAuthentication yes
## Disable empty passwords
PermitEmptyPasswords no
## Limit login attempts
MaxAuthTries 3
MaxSessions 3
## Disable unused features
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
## Use secure protocols only
Protocol 2
EOF
## Restart SSH service
sudo systemctl restart sshd
## Install fail2ban (already installed above)
## Create custom jail for SSH
sudo tee /etc/fail2ban/jail.d/ssh.conf <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
## Start and enable fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
## Check status
sudo fail2ban-client status
## Add Tailscale repository
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
## Install Tailscale
sudo apt update
sudo apt install tailscale
## Connect to Tailscale network
sudo tailscale up --hostname=loose-seal
## Verify connection
tailscale status
tailscale ip -4
## Get Tailscale IP
TAILSCALE_IP=$(tailscale ip -4)
TAILSCALE_SUBNET="100.64.0.0/10"
## Update UFW to only allow SSH from Tailscale network
sudo ufw delete allow ssh
sudo ufw allow from $TAILSCALE_SUBNET to any port ssh
sudo ufw reload
## Create homelab directory
mkdir -p /home/$USER/homelab
cd /home/$USER/homelab
## Clone loose-seal repository
git clone https://github.com/dratspiker/homelab-loose-seal.git
cd homelab-loose-seal
## Set proper permissions
sudo chown -R $USER:$USER /home/$USER/homelab
## Copy environment template
cp .env.example .env
## Edit environment file
nano .env
## Domain Configuration
DOMAIN=speicher.family
SUBDOMAIN_BESZEL=beszel
SUBDOMAIN_SEQ=seq
SUBDOMAIN_GRAFANA=grafana
## Beszel Configuration
BESZEL_HUB_USER=admin
BESZEL_HUB_PASSWORD=<secure-password>
## Seq Configuration
SEQ_FIRST_RUN_ADMIN_USERNAME=admin
SEQ_FIRST_RUN_ADMIN_PASSWORD=<secure-password>
## Grafana Configuration
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=<secure-password>
## Database Configuration
POSTGRES_USER=grafana
POSTGRES_PASSWORD=<secure-password>
POSTGRES_DB=grafana
## SMTP Configuration (for alerts)
SMTP_HOST=smtp.fastmail.com
SMTP_PORT=587
SMTP_USER=<email>
SMTP_PASSWORD=<password>
## Cloudflare (for SSL)
CLOUDFLARE_API_TOKEN=<token>
CLOUDFLARE_ZONE_ID=<zone-id>
## Generate random passwords
echo "BESZEL_HUB_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "SEQ_FIRST_RUN_ADMIN_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "GF_SECURITY_ADMIN_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)" >> .env
## Create monitoring network
docker network create monitoring
## Verify network
docker network ls
## Create volumes for persistent data
docker volume create beszel_data
docker volume create seq_data
docker volume create grafana_data
docker volume create postgres_data
docker volume create caddy_data
docker volume create caddy_config
## Verify volumes
docker volume ls
## Navigate to project directory
cd /home/$USER/homelab/homelab-loose-seal
## Pull all images
docker compose pull
## Deploy services in stages
## Stage 1: Database and reverse proxy
docker compose up -d postgres caddy
## Wait for database initialization
sleep 30
## Stage 2: Core monitoring services
docker compose up -d beszel seq grafana
## Stage 3: Additional monitoring tools
docker compose up -d glance osquery
## Verify deployment
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
## Check all containers
docker ps -a
## Check specific service logs
docker logs beszel
docker logs seq
docker logs grafana
## Check network connectivity
docker exec caddy curl -I http://beszel:45876/api/health
Access https://beszel.speicher.family
Login with admin credentials from .env file
Configure hub settings:
## Generate agent installation commands for other servers
## Commands will be provided in Beszel web interface
## Deploy to: lucille4, nas02, lucille5, family-macbook
Access https://seq.speicher.family
Complete initial setup with admin credentials
Configure:
## Example rsyslog configuration for remote servers
## Add to /etc/rsyslog.d/50-seq.conf on each server:
cat > /tmp/seq-rsyslog.conf <<EOF
## Forward logs to Seq
*.* @@seq.speicher.family:12201;RSYSLOG_SyslogProtocol23Format
EOF
## Deploy to other servers via Ansible or manual configuration
Access https://grafana.speicher.family
Login with admin credentials
Configure data sources:
## Import pre-built dashboards
## - Homelab overview dashboard
## - Server-specific dashboards
## - Service health dashboard
## - Resource utilization dashboard
## Create osquery configuration
sudo mkdir -p /etc/osquery
sudo tee /etc/osquery/osquery.conf <<EOF
{
"options": {
"host_identifier": "hostname",
"schedule_splay_percent": 10
},
"schedule": {
"system_info": {
"query": "SELECT hostname, cpu_brand, physical_memory FROM system_info;",
"interval": 3600
},
"logged_in_users": {
"query": "SELECT user, host FROM logged_in_users;",
"interval": 600
},
"processes": {
"query": "SELECT pid, name, path FROM processes;",
"interval": 600
}
}
}
EOF
## Start osquery service
sudo systemctl enable osqueryd
sudo systemctl start osqueryd
Configure notification channels (email, webhooks)
Set up alert rules for:
Configure signal rules for:
## Create backup script
sudo mkdir -p /opt/monitoring/scripts
sudo tee /opt/monitoring/scripts/backup.sh <<EOF
#!/bin/bash
set -e
BACKUP_DIR="/opt/monitoring/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"
mkdir -p "$BACKUP_PATH"
## Backup Docker volumes
docker run --rm \
-v beszel_data:/source/beszel_data:ro \
-v seq_data:/source/seq_data:ro \
-v grafana_data:/source/grafana_data:ro \
-v postgres_data:/source/postgres_data:ro \
-v "$BACKUP_PATH":/backup \
alpine tar -czf /backup/volumes.tar.gz -C /source .
## Backup configuration
cp -r /home/$USER/homelab/homelab-loose-seal "$BACKUP_PATH/config"
## Cleanup old backups (keep 14 days)
find "$BACKUP_DIR" -type d -mtime +14 -exec rm -rf {} +
echo "Backup completed: $BACKUP_PATH"
EOF
sudo chmod +x /opt/monitoring/scripts/backup.sh
## Add backup cron job
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/monitoring/scripts/backup.sh") | crontab -
## Verify cron job
crontab -l
## Create performance optimization script
sudo tee /opt/monitoring/scripts/optimize.sh <<EOF
#!/bin/bash
## Clean Docker system
docker system prune -f
## Clean package cache
sudo apt autoremove -y
sudo apt autoclean
## Optimize memory
sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
echo "System optimization completed"
EOF
sudo chmod +x /opt/monitoring/scripts/optimize.sh
## Run weekly via cron
(crontab -l 2>/dev/null; echo "0 4 * * 0 /opt/monitoring/scripts/optimize.sh") | crontab -
## Create health check script
tee /opt/monitoring/scripts/health-check.sh <<EOF
#!/bin/bash
echo "=== Loose-seal Health Check ==="
echo
## Docker status
echo "Docker Services:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo
## Service health checks
echo "Service Health:"
services=(
"https://beszel.speicher.family"
"https://seq.speicher.family"
"https://grafana.speicher.family"
)
for url in "${services[@]}"; do
if curl -s -o /dev/null -w "%{http_code}" "$url" | grep -q "200\|301\|302"; then
echo "✅ $url"
else
echo "❌ $url"
fi
done
echo
echo "System Resources:"
echo "CPU Load: $(uptime | awk -F'load average:' '{print $2}')"
echo "Memory: $(free -h | grep Mem | awk '{print $3 "/" $2}')"
echo "Disk: $(df -h / | tail -1 | awk '{print $5 " used"}')"
echo "Swap: $(free -h | grep Swap | awk '{print $3 "/" $2}')"
echo
echo "Health check completed"
EOF
chmod +x /opt/monitoring/scripts/health-check.sh
## Run health check
/opt/monitoring/scripts/health-check.sh
For daily operations and maintenance, see the Maintenance Guide.