Skip to content

Instantly share code, notes, and snippets.

@punkhop
Last active July 19, 2025 00:39
Show Gist options
  • Save punkhop/d2c5159577bb3969b26e96ca77d86b5f to your computer and use it in GitHub Desktop.
Save punkhop/d2c5159577bb3969b26e96ca77d86b5f to your computer and use it in GitHub Desktop.
DietPi Pi-hole + Home Assistant Setup Script
#!/bin/bash
# DietPi Pi-hole + Home Assistant Setup Script
# Installs: Pi-hole, Home Assistant
# Sets up: Auto-updates, backups, monitoring
set -e # Exit on any error
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log() {
echo -e "${GREEN}[$(date '+%H:%M:%S')] $1${NC}"
}
warn() {
echo -e "${YELLOW}[$(date '+%H:%M:%S')] WARNING: $1${NC}"
}
error() {
echo -e "${RED}[$(date '+%H:%M:%S')] ERROR: $1${NC}"
exit 1
}
info() {
echo -e "${BLUE}[$(date '+%H:%M:%S')] INFO: $1${NC}"
}
# Check if running as root
if [[ $EUID -eq 0 ]]; then
error "Don't run this as root. Run as dietpi user."
fi
log "πŸš€ DietPi Pi-hole + Home Assistant Setup Starting..."
# Update system first
log "πŸ“¦ Updating system..."
# Check if we're actually on DietPi
if [ -f /boot/dietpi/.installed ]; then
# Use DietPi commands if available
if command -v dietpi-update >/dev/null 2>&1; then
sudo dietpi-update
else
log "DietPi tools not found, using standard apt..."
sudo apt update && sudo apt upgrade -y
fi
if command -v dietpi-software >/dev/null 2>&1; then
sudo dietpi-software install 9 # Git
else
sudo apt install -y git
fi
else
# Not DietPi, use standard commands
warn "Not running on DietPi, using standard Debian commands..."
sudo apt update && sudo apt upgrade -y
sudo apt install -y git
fi
# Install essential tools and all dependencies upfront
log "πŸ”§ Installing essential tools and dependencies..."
sudo apt install -y curl wget htop tmux unzip jq net-tools build-essential gcc \
python3 python3-dev python3-venv python3-pip \
libffi-dev libssl-dev libjpeg-dev zlib1g-dev autoconf \
libopenjp2-7 libtiff6 libturbojpeg0-dev tzdata
# Create service directories
log "πŸ“ Creating service directories..."
sudo mkdir -p /opt/homeassistant
sudo mkdir -p /opt/scripts
sudo mkdir -p /var/log/dietpi-services
#=== PI-HOLE INSTALLATION ===
log "πŸ›‘οΈ Installing Pi-hole..."
curl -sSL https://install.pi-hole.net | bash
# Note: Pi-hole installer is interactive, you'll need to configure during install
#=== HOME ASSISTANT INSTALLATION ===
log "🏠 Installing Home Assistant..."
# Create HA user and directories
sudo useradd -rm homeassistant -G dialout,gpio,i2c || true
sudo mkdir -p /srv/homeassistant
sudo mkdir -p /srv/homeassistant/.homeassistant
sudo chown homeassistant:homeassistant /srv/homeassistant
sudo chown homeassistant:homeassistant /srv/homeassistant/.homeassistant
# Install HA in virtual environment with dependency fixes
log "Installing Home Assistant with fixed dependencies..."
sudo -u homeassistant -H bash -c '
set -e
cd /srv/homeassistant
python3 -m venv .
source bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install wheel
# Fix josepy compatibility issue with specific version constraints
pip3 install "josepy>=1.1.0,<1.14.0"
pip3 install "acme>=0.40.0,<3.0.0"
pip3 install "cryptography>=3.0.0"
# Now install Home Assistant
pip3 install homeassistant
' || error "Home Assistant installation failed"
# Create HA systemd service
log "Creating Home Assistant service..."
sudo tee /etc/systemd/system/[email protected] > /dev/null <<EOF
[Unit]
Description=Home Assistant
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=%i
WorkingDirectory=/srv/homeassistant
ExecStart=/srv/homeassistant/bin/hass -c "/srv/homeassistant/.homeassistant"
Restart=on-failure
RestartSec=5
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable [email protected]
log "Starting Home Assistant (this may take a few minutes on first boot)..."
sudo systemctl start [email protected]
# Wait for HA to be ready with better error handling
log "Waiting for Home Assistant to start..."
HA_STARTED=false
for i in {1..60}; do
if sudo systemctl is-active --quiet [email protected]; then
if curl -s http://localhost:8123 >/dev/null 2>&1; then
log "βœ… Home Assistant is responding on port 8123"
HA_STARTED=true
break
fi
fi
if [ $i -eq 60 ]; then
warn "⚠️ Home Assistant startup timeout reached"
warn " Check status: sudo systemctl status [email protected]"
warn " Check logs: sudo journalctl -u [email protected] --no-pager"
warn " HA may still be starting up (first boot can take 10+ minutes)"
break
fi
echo -n "."
sleep 5
done
if [ "$HA_STARTED" = true ]; then
log "βœ… Home Assistant startup complete"
else
warn "⚠️ Home Assistant may still be starting up"
fi
#=== RCLONE INSTALLATION ===
log "πŸ“¦ Installing rclone for cloud backups..."
curl https://rclone.org/install.sh | sudo bash
echo ""
echo "Testing rclone installation..."
if command -v rclone >/dev/null 2>&1; then
log "βœ… rclone installed successfully"
else
error "rclone installation failed"
fi
#=== AUTO-UPDATE SETUP ===
log "πŸ”„ Setting up auto-updates..."
# Create update script
sudo tee /opt/scripts/auto-update.sh > /dev/null <<'EOF'
#!/bin/bash
LOG_FILE="/var/log/dietpi-services/auto-update.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] Starting auto-update..." >> $LOG_FILE
# Update DietPi if available
if command -v dietpi-update >/dev/null 2>&1; then
dietpi-update >> $LOG_FILE 2>&1
else
apt update && apt upgrade -y >> $LOG_FILE 2>&1
fi
# Update packages
apt update && apt upgrade -y >> $LOG_FILE 2>&1
# Update Pi-hole
pihole -up >> $LOG_FILE 2>&1
# Update Home Assistant with dependency fixes
sudo systemctl stop [email protected]
sudo -u homeassistant -H -s << 'HAEOF'
cd /srv/homeassistant
source bin/activate
pip3 install --upgrade pip
pip3 install "josepy>=1.1.0,<1.14.0"
pip3 install "acme>=0.40.0,<3.0.0"
pip3 install --upgrade homeassistant
HAEOF
sudo systemctl start [email protected]
echo "[$DATE] Auto-update completed." >> $LOG_FILE
# Clean up old logs (keep last 30 days)
find /var/log/dietpi-services/ -name "*.log" -mtime +30 -delete
EOF
sudo chmod +x /opt/scripts/auto-update.sh
# Create weekly cron job for updates
sudo tee /etc/cron.d/dietpi-auto-update > /dev/null <<EOF
# Auto-update all services weekly on Sunday at 3 AM
0 3 * * 0 root /opt/scripts/auto-update.sh
EOF
#=== BACKUP SETUP ===
log "πŸ’Ύ Setting up backup system..."
sudo tee /opt/scripts/ha-backup.sh > /dev/null <<'EOF'
#!/bin/bash
TEMP_DIR="/tmp/dietpi-backup"
DATE=$(date '+%Y%m%d_%H%M%S')
LOG_FILE="/var/log/dietpi-services/ha-backup.log"
mkdir -p $TEMP_DIR
# Create HA config backup
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating Home Assistant backup..." >> $LOG_FILE
tar -czf "$TEMP_DIR/ha_backup_$DATE.tar.gz" -C /srv/homeassistant/.homeassistant . 2>/dev/null || true
# Backup Pi-hole config
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating Pi-hole backup..." >> $LOG_FILE
tar -czf "$TEMP_DIR/pihole_backup_$DATE.tar.gz" -C /etc/pihole . 2>/dev/null || true
# Backup DietPi config (if DietPi directories exist)
if [ -d /boot/dietpi ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating DietPi backup..." >> $LOG_FILE
tar -czf "$TEMP_DIR/dietpi_backup_$DATE.tar.gz" /boot/dietpi.txt /boot/dietpi /var/lib/dietpi 2>/dev/null || true
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] DietPi directories not found, skipping DietPi backup..." >> $LOG_FILE
fi
# Upload to Google Drive if configured
if [ -f /root/.config/rclone/rclone.conf ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Uploading backups to Google Drive..." >> $LOG_FILE
# Upload all backup files
rclone copy $TEMP_DIR/ remote:dietpi-backups/ --include "*.tar.gz"
# Clean up Google Drive - keep only last 3 of each type
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleaning old backups from Google Drive..." >> $LOG_FILE
# Get list of files, sort by date, delete old ones
rclone ls remote:dietpi-backups/ | grep "ha_backup_" | sort -k2 | head -n -3 | awk '{print $2}' | while read file; do
rclone delete "remote:dietpi-backups/$file"
done
rclone ls remote:dietpi-backups/ | grep "pihole_backup_" | sort -k2 | head -n -3 | awk '{print $2}' | while read file; do
rclone delete "remote:dietpi-backups/$file"
done
rclone ls remote:dietpi-backups/ | grep "dietpi_backup_" | sort -k2 | head -n -3 | awk '{print $2}' | while read file; do
rclone delete "remote:dietpi-backups/$file"
done
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Google Drive upload and cleanup complete" >> $LOG_FILE
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] No Google Drive config found - local backup only" >> $LOG_FILE
fi
# Delete local temp files immediately
rm -rf $TEMP_DIR
# Log completion
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Weekly backup cycle complete" >> $LOG_FILE
EOF
sudo chmod +x /opt/scripts/ha-backup.sh
# Weekly backups at 2 AM Saturday
sudo tee /etc/cron.d/ha-backup > /dev/null <<EOF
# Home Assistant + Pi-hole + DietPi weekly backup Saturday 2 AM
0 2 * * 6 root /opt/scripts/ha-backup.sh
EOF
#=== MONITORING SETUP ===
log "πŸ“Š Setting up service monitoring..."
sudo tee /opt/scripts/service-monitor.sh > /dev/null <<'EOF'
#!/bin/bash
LOG_FILE="/var/log/dietpi-services/monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
check_service() {
SERVICE=$1
if sudo systemctl is-active --quiet $SERVICE; then
echo "[$DATE] βœ… $SERVICE is running" >> $LOG_FILE
else
echo "[$DATE] ❌ $SERVICE is down - attempting restart" >> $LOG_FILE
sudo systemctl restart $SERVICE
sleep 10
if sudo systemctl is-active --quiet $SERVICE; then
echo "[$DATE] βœ… $SERVICE restarted successfully" >> $LOG_FILE
else
echo "[$DATE] 🚨 $SERVICE failed to restart" >> $LOG_FILE
fi
fi
}
# Check critical services
check_service "pihole-FTL"
check_service "home-assistant@homeassistant"
# Check disk space
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 85 ]; then
echo "[$DATE] ⚠️ Disk usage high: $DISK_USAGE%" >> $LOG_FILE
fi
# Check memory usage
MEM_USAGE=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
if [ $MEM_USAGE -gt 85 ]; then
echo "[$DATE] ⚠️ Memory usage high: $MEM_USAGE%" >> $LOG_FILE
fi
EOF
sudo chmod +x /opt/scripts/service-monitor.sh
# Monitor every 15 minutes
sudo tee /etc/cron.d/service-monitor > /dev/null <<EOF
# Service monitoring every 15 minutes
*/15 * * * * root /opt/scripts/service-monitor.sh
EOF
#=== CLOUD BACKUP SETUP SCRIPT ===
log "☁️ Creating cloud backup setup script..."
sudo tee /opt/scripts/setup-cloud-backup.sh > /dev/null <<'EOF'
#!/bin/bash
echo "☁️ Google Drive Backup Setup"
echo "============================"
echo ""
echo "This will set up automatic weekly backups to Google Drive."
echo "You'll need to authenticate with your Google account."
echo ""
read -p "Continue? (y/n): " continue_setup
if [ "$continue_setup" != "y" ]; then
echo "Setup cancelled."
exit 0
fi
echo ""
echo "Setting up Google Drive connection..."
echo ""
echo "⚠️ IMPORTANT: You'll see a URL to visit in your browser."
echo " 1. Copy the URL and open it on your laptop/phone"
echo " 2. Log into your Google account"
echo " 3. Grant access to rclone"
echo " 4. Copy the verification code"
echo " 5. Paste it back here"
echo ""
read -p "Ready? Press Enter to continue..."
# Set up Google Drive
echo "Setting up Google Drive access..."
echo "We'll generate a URL you can open on your laptop/phone."
echo ""
# Create basic config first
sudo mkdir -p /root/.config/rclone
sudo tee /root/.config/rclone/rclone.conf > /dev/null <<RCLONEEOF
[remote]
type = drive
scope = drive
RCLONEEOF
echo "Getting authorization URL..."
# Use rclone authorize to get a working external URL
sudo rclone authorize drive --config /root/.config/rclone/rclone.conf > /tmp/rclone_auth.txt 2>&1 &
RCLONE_PID=$!
# Wait a moment for rclone to start
sleep 3
# Extract the URL from rclone output
AUTH_URL=$(grep -o 'https://accounts.google.com[^[:space:]]*' /tmp/rclone_auth.txt | head -1)
if [ -n "$AUTH_URL" ]; then
echo ""
echo "πŸ”— Copy and open this URL in your browser:"
echo "$AUTH_URL"
echo ""
echo "1. Log into your Google account"
echo "2. Grant access to rclone"
echo "3. Copy the authorization code from the success page"
echo "4. Paste it here"
echo ""
read -p "Authorization code: " auth_code
# Kill the background rclone process
kill $RCLONE_PID 2>/dev/null
# Complete the authorization
echo "Completing authorization..."
echo "$auth_code" | sudo rclone config reconnect remote drive --config /root/.config/rclone/rclone.conf
else
echo "❌ Failed to generate authorization URL"
echo "Please run manual setup: sudo rclone config"
exit 1
fi
echo ""
echo "Testing connection..."
if sudo rclone ls remote: > /dev/null 2>&1; then
echo "βœ… Google Drive connection successful!"
# Create backup folder
sudo rclone mkdir remote:dietpi-backups
echo "βœ… Created dietpi-backups folder"
echo ""
echo "πŸŽ‰ Setup complete!"
echo "πŸ“ Backups will be stored in: Google Drive > dietpi-backups/"
echo "πŸ”„ Automatic backups: Every Saturday at 2 AM"
echo "πŸ’Ύ Retention: Last 3 backups of each type"
echo ""
echo "Test backup now: sudo /opt/scripts/ha-backup.sh"
else
echo "❌ Connection failed. Please try again."
exit 1
fi
EOF
sudo chmod +x /opt/scripts/setup-cloud-backup.sh
#=== RESTORE SCRIPT ===
log "πŸ”„ Creating restore script..."
sudo tee /opt/scripts/restore-from-backup.sh > /dev/null <<'EOF'
#!/bin/bash
echo "πŸ”„ DietPi Restore from Google Drive Backup"
echo "=========================================="
echo ""
# Check for Google Drive config
if [ ! -f /root/.config/rclone/rclone.conf ]; then
echo "❌ No Google Drive config found!"
echo "Run setup first: /opt/scripts/setup-cloud-backup.sh"
exit 1
fi
echo "☁️ Downloading available backups from Google Drive..."
mkdir -p /tmp/restore
rclone copy remote:dietpi-backups/ /tmp/restore/ --include "*.tar.gz"
if [ ! "$(ls -A /tmp/restore/*.tar.gz 2>/dev/null)" ]; then
echo "❌ No backups found in Google Drive!"
exit 1
fi
echo ""
echo "πŸ“ Available backups:"
echo ""
ls -la /tmp/restore/*backup*.tar.gz | awk '{print $9, $5, $6, $7, $8}' | sort
echo ""
echo "Enter backup date to restore (format: YYYYMMDD)"
read -p "Or type 'latest' for most recent backup: " backup_date
if [ "$backup_date" = "latest" ]; then
HA_BACKUP=$(ls -t /tmp/restore/ha_backup_*.tar.gz 2>/dev/null | head -1)
PIHOLE_BACKUP=$(ls -t /tmp/restore/pihole_backup_*.tar.gz 2>/dev/null | head -1)
DIETPI_BACKUP=$(ls -t /tmp/restore/dietpi_backup_*.tar.gz 2>/dev/null | head -1)
else
HA_BACKUP=$(ls /tmp/restore/ha_backup_${backup_date}_*.tar.gz 2>/dev/null | head -1)
PIHOLE_BACKUP=$(ls /tmp/restore/pihole_backup_${backup_date}_*.tar.gz 2>/dev/null | head -1)
DIETPI_BACKUP=$(ls /tmp/restore/dietpi_backup_${backup_date}_*.tar.gz 2>/dev/null | head -1)
fi
echo ""
echo "🏠 Restoring Home Assistant..."
if [ -f "$HA_BACKUP" ]; then
sudo systemctl stop [email protected]
echo " Extracting HA config..."
tar -xzf "$HA_BACKUP" -C /srv/homeassistant/.homeassistant/
chown -R homeassistant:homeassistant /srv/homeassistant/.homeassistant/
sudo systemctl start [email protected]
echo " βœ… Home Assistant restored and started"
else
echo " ❌ No HA backup found for selected date"
fi
echo ""
echo "πŸ›‘οΈ Restoring Pi-hole..."
if [ -f "$PIHOLE_BACKUP" ]; then
sudo systemctl stop pihole-FTL
echo " Extracting Pi-hole config..."
tar -xzf "$PIHOLE_BACKUP" -C /etc/pihole/
sudo systemctl start pihole-FTL
echo " βœ… Pi-hole restored and started"
else
echo " ❌ No Pi-hole backup found for selected date"
fi
echo ""
echo "βš™οΈ DietPi config restore skipped (requires reboot)"
if [ -f "$DIETPI_BACKUP" ]; then
echo " DietPi backup available but not restored automatically"
echo " Manual restore: tar -xzf $DIETPI_BACKUP -C /"
fi
# Clean up
rm -rf /tmp/restore
echo ""
echo "πŸŽ‰ Restore complete!"
echo ""
echo "πŸ“Š Check services: status"
echo "🌐 Home Assistant: http://$(hostname -I | awk '{print $1}'):8123"
echo "πŸ›‘οΈ Pi-hole Admin: http://$(hostname -I | awk '{print $1}')/admin"
echo ""
echo "⚠️ If something seems wrong, check logs:"
echo " Home Assistant: journalctl -u home-assistant@homeassistant -f"
echo " Pi-hole: journalctl -u pihole-FTL -f"
EOF
sudo chmod +x /opt/scripts/restore-from-backup.sh
#=== STATUS SCRIPT ===
log "πŸ“‹ Creating status script..."
sudo tee /opt/scripts/status.sh > /dev/null <<'EOF'
#!/bin/bash
echo "πŸ΄β€β˜ οΈ DietPi Pi-hole + Home Assistant Status πŸ΄β€β˜ οΈ"
echo "=============================================="
services=("pihole-FTL" "home-assistant@homeassistant")
for service in "${services[@]}"; do
if sudo systemctl is-active --quiet $service; then
echo "βœ… $service: RUNNING"
else
echo "❌ $service: STOPPED"
fi
done
echo ""
echo "πŸ’Ύ Disk Usage:"
df -h / | tail -1
echo ""
echo "🧠 Memory Usage:"
free -h
echo ""
echo "🌑️ Temperature:"
if command -v vcgencmd &> /dev/null; then
vcgencmd measure_temp 2>/dev/null || echo "Temperature monitoring not available"
else
echo "Temperature monitoring not available"
fi
echo ""
echo "🌐 Network:"
ip route | grep default
echo ""
echo "πŸ“Š Service URLs:"
LOCAL_IP=$(hostname -I | awk '{print $1}')
echo "Pi-hole Admin: http://$LOCAL_IP/admin"
echo "Home Assistant: http://$LOCAL_IP:8123"
EOF
sudo chmod +x /opt/scripts/status.sh
# Create alias for easy access and reload it
echo "alias status='/opt/scripts/status.sh'" >> ~/.bashrc
source ~/.bashrc
#=== NETWORK OPTIMIZATION ===
log "🌐 Setting up network optimization..."
# Increase file descriptors for better performance
sudo tee -a /etc/security/limits.conf > /dev/null <<EOF
* soft nofile 65536
* hard nofile 65536
EOF
# Network tweaks for Pi
sudo tee -a /etc/sysctl.conf > /dev/null <<EOF
# Network performance tweaks
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
EOF
#=== COMPLETION ===
log "πŸŽ‰ Setup complete!"
info ""
info "πŸ”— Next steps:"
info "1. Configure Pi-hole admin password: pihole -a -p"
info "2. Reload your shell: source ~/.bashrc (or start new terminal session)"
info "3. Access services:"
LOCAL_IP=$(hostname -I | awk '{print $1}')
info " β€’ Pi-hole: http://$LOCAL_IP/admin"
info " β€’ Home Assistant: http://$LOCAL_IP:8123"
info ""
info "4. Check system status: status (or /opt/scripts/status.sh)"
info "πŸ“ Logs: /var/log/dietpi-services/"
info "πŸ”„ Auto-updates: Sundays 3 AM"
info "πŸ’Ύ Backups: Weekly Saturdays 2 AM β†’ Google Drive (if configured)"
info ""
warn "Reboot recommended to apply all network changes!"
if [ "$setup_gdrive" != "y" ]; then
warn "⚠️ Remember to set up Google Drive: sudo rclone config"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment