Complete installation, setup, and configuration guide for Lucille4 as the primary homelab application server running 40+ containerized services.
## Update system packages
sudo apt update && sudo apt upgrade -y
## Install essential packages
sudo apt install -y \
curl \
wget \
git \
htop \
unzip \
software-properties-common \
apt-transport-https \
ca-certificates \
gnupg \
lsb-release
## Configure automatic security updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
## Increase file limits
echo '* soft nofile 65536' | sudo tee -a /etc/security/limits.conf
echo '* hard nofile 65536' | sudo tee -a /etc/security/limits.conf
## Configure swap (if needed)
sudo swapon --show
## Add swap if less than 16GB RAM available
## Set timezone
sudo timedatectl set-timezone America/New_York
## 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
newgrp docker
## 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
## 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=lucille4
## Verify connection
tailscale status
## Edit netplan configuration
sudo nano /etc/netplan/01-network-manager-all.yaml
## Example configuration:
network:
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses:
- 192.168.1.101/24
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
routes:
- to: default
via: 192.168.1.1
## Apply configuration
sudo netplan apply
## Create homelab directory
mkdir -p /home/$USER/homelab
cd /home/$USER/homelab
## Clone lucille4 repository
git clone https://github.com/dratspiker/homelab-lucille4.git
cd homelab-lucille4
## Set proper permissions
sudo chown -R $USER:$USER /home/$USER/homelab
## Create external networks
docker network create caddy
docker network create internal
## Verify networks
docker network ls
## Create all required volumes
docker volume create caddy_data
docker volume create caddy_config
docker volume create auth-db
docker volume create auth-redis
docker volume create grafana_data
docker volume create influxdb_data
docker volume create paperless-db
docker volume create paperless-broker
docker volume create paperless-media
docker volume create n8n-data
docker volume create mealie_data
docker volume create ollama_data
docker volume create code-server_data
docker volume create jupyter_data
## Verify volumes
docker volume ls
## Copy environment template
cp .env.example .env
## Edit environment file
nano .env
## Domain Configuration
DOMAIN=speicher.family
SUBDOMAIN_AUTH=auth
SUBDOMAIN_PAPERLESS=paperless
SUBDOMAIN_N8N=n8n
SUBDOMAIN_GRAFANA=grafana
## Database Passwords
POSTGRES_PASSWORD=<secure-password>
REDIS_PASSWORD=<secure-password>
MONGODB_PASSWORD=<secure-password>
## Application Secrets
AUTHENTIK_SECRET_KEY=<generated-secret>
N8N_ENCRYPTION_KEY=<generated-secret>
PAPERLESS_SECRET_KEY=<generated-secret>
## External API Keys
OPENAI_API_KEY=<api-key>
CLOUDFLARE_API_TOKEN=<token>
## SMTP Configuration
SMTP_HOST=smtp.fastmail.com
SMTP_PORT=587
SMTP_USER=<email>
SMTP_PASSWORD=<password>
## Backup Configuration
BACKUP_LOCATION=/mnt/nas02/backups/lucille4
BACKUP_RETENTION_DAYS=30
## Generate random secrets for applications
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -hex 32)" >> .env
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
echo "PAPERLESS_SECRET_KEY=$(openssl rand -hex 32)" >> .env
## Create mount point for NAS backup
sudo mkdir -p /mnt/nas02
## Install CIFS utilities
sudo apt install cifs-utils
## Create credentials file
sudo nano /etc/cifs-credentials
## Add:
username=<nas-username>
password=<nas-password>
domain=<domain>
## Set secure permissions
sudo chmod 600 /etc/cifs-credentials
## Add to fstab for persistent mount
echo '//nas02.local/backups /mnt/nas02 cifs credentials=/etc/cifs-credentials,uid=1000,gid=1000,iocharset=utf8 0 0' | sudo tee -a /etc/fstab
## Test mount
sudo mount -a
## Create application data directories
sudo mkdir -p /opt/homelab/{backups,logs,config,data}
sudo chown -R $USER:$USER /opt/homelab
## Create symbolic links for easy access
ln -s /opt/homelab ~/homelab-data
## Create docker-socket-proxy service
cat > docker-socket-proxy.yml <<EOF
version: '3.8'
services:
dockerproxy:
image: tecnativa/docker-socket-proxy
container_name: dockerproxy
environment:
- CONTAINERS=1
- IMAGES=1
- AUTH=1
- SECRETS=1
- POST=1
- BUILD=1
- COMMIT=1
- CONFIGS=1
- DISTRIBUTION=1
- EXEC=1
- GRPC=1
- INFO=1
- NETWORKS=1
- NODES=1
- PLUGINS=1
- SERVICES=1
- SESSION=1
- SWARM=1
- SYSTEM=1
- TASKS=1
- VERSION=1
- VOLUMES=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- internal
restart: unless-stopped
networks:
internal:
external: true
EOF
## Deploy socket proxy
docker compose -f docker-socket-proxy.yml up -d
## Install and configure UFW
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
## Allow SSH
sudo ufw allow ssh
## Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
## Allow Tailscale
sudo ufw allow in on tailscale0
## Enable firewall
sudo ufw --force enable
sudo ufw status
## Navigate to project directory
cd /home/$USER/homelab/homelab-lucille4
## Pull all images
docker compose pull
## Deploy services in stages
## Stage 1: Infrastructure services
docker compose up -d caddy postgres redis
## Wait for databases to initialize
sleep 30
## Stage 2: Authentication
docker compose up -d authentik-server authentik-worker
## Wait for auth setup
sleep 60
## Stage 3: All remaining services
docker compose up -d
## Verify deployment
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
## Check all containers
docker ps -a
## Check specific service logs
docker logs authentik-server
docker logs caddy
docker logs paperless-webserver
## Check network connectivity
docker exec caddy curl -I http://authentik-server:9000/if/flow/initial-setup/
https://auth.speicher.family/if/flow/initial-setup/
## Create superuser account
docker exec -it paperless-webserver python manage.py createsuperuser
## Configure OCR and AI features
docker exec -it paperless-webserver python manage.py configure_ocr
https://n8n.speicher.familyhttps://grafana.speicher.family
## Configure Seq for centralized logging
## Access via https://seq.speicher.family
## Configure API keys for services
## Set up log retention policies
## Verify Prometheus targets
curl http://localhost:9090/targets
## Configure scrape configs for all services
## Set up recording rules
## Configure alerting rules
## Create backup script
cat > /opt/homelab/scripts/backup.sh <<EOF
#!/bin/bash
set -e
BACKUP_DIR="/mnt/nas02/backups/lucille4"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"
mkdir -p "$BACKUP_PATH"
## Backup Docker volumes
docker run --rm \
-v caddy_data:/source/caddy_data:ro \
-v auth-db:/source/auth-db:ro \
-v paperless-db:/source/paperless-db:ro \
-v n8n-data:/source/n8n-data:ro \
-v grafana_data:/source/grafana_data:ro \
-v "$BACKUP_PATH":/backup \
alpine tar -czf /backup/volumes.tar.gz -C /source .
## Backup configuration
cp -r /home/$USER/homelab/homelab-lucille4 "$BACKUP_PATH/config"
## Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -type d -mtime +30 -exec rm -rf {} +
echo "Backup completed: $BACKUP_PATH"
EOF
chmod +x /opt/homelab/scripts/backup.sh
## Add backup cron job
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/homelab/scripts/backup.sh") | crontab -
## Verify cron job
crontab -l
## Create health check script
cat > /opt/homelab/scripts/health-check.sh <<EOF
#!/bin/bash
echo "=== Lucille4 Health Check ==="
echo
## Docker status
echo "Docker Status:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | head -20
echo
## Service URLs
echo "Service Health:"
services=(
"https://auth.speicher.family"
"https://paperless.speicher.family"
"https://n8n.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:"
df -h / | tail -1
echo "Memory: $(free -h | grep Mem | awk '{print $3 "/" $2}')"
echo
echo "Health check completed"
EOF
chmod +x /opt/homelab/scripts/health-check.sh
## Run health check
/opt/homelab/scripts/health-check.sh
For daily operations and maintenance, see the Maintenance Guide.