A production-ready Docker Compose setup for self-hosted services on Raspberry Pi or similar home servers. This repository contains configurations for various services including reverse proxy, home automation, photo management, document management, and more.
- Production-tested configurations for Raspberry Pi
- Security-hardened with fail2ban, rate limiting, and SSL
- Automated dependency updates via GitHub Dependabot
- MQTT-based monitoring integration with Home Assistant
- Automated backups with Restic
- Template-based setup - easy to customize for your environment
- Nginx Reverse Proxy - SSL termination with Let's Encrypt (certbot)
- Pi-hole - Network-wide ad blocking with DNS and IPv6 support
- Fail2ban - Intrusion prevention with custom filters
- Home Assistant - Home automation hub
- Mosquitto - MQTT broker for IoT devices
- Matter Hub - Matter protocol bridge
⚠️ (Migration to Matterbridge planned - upstream EOL) - Music Assistant - Music aggregation service
- Immich - Photo and video management with ML features
- Paperless-ngx - Document management with OCR
- FreshRSS - RSS feed reader
- Teslamate - Tesla vehicle data logging with Grafana dashboards and Home Assistant integration
- Backup Scripts - Automated Restic backups with MQTT status reporting
- Update Monitors - Track Docker image and system updates via MQTT
- Health Checks - Nginx and system status monitoring
- Raspberry Pi 4/5 or similar ARM64 device (or adapt for x86_64)
- Docker and Docker Compose installed
- Domain names configured (for SSL certificates)
- Basic understanding of Docker and networking
-
Clone the repository:
git clone https://github.com/Spiev/docker-runtime.git cd docker-runtime -
Configure environment files:
# Each service directory contains .env.example files cd immich cp .env.example .env # Edit .env with your settings # Repeat for other services: proxy, paperless, etc.
-
Configure Home Assistant:
cd homeassistant/homeassistant/config # Copy all config templates cp configuration.yaml.example configuration.yaml cp mqtt.yaml.example mqtt.yaml cp templates.yaml.example templates.yaml cp command_line.yaml.example command_line.yaml # Edit each .yaml file: replace placeholders (YOUR_HA_DOMAIN, SENSOR_EUI, etc.)
-
Setup monitoring scripts:
cd scripts # MQTT credentials (for monitoring) cp .mqtt_credentials.example .mqtt_credentials chmod 600 .mqtt_credentials # Edit with your MQTT broker credentials # Restic backup encryption cp .restic.env.example .restic.env chmod 600 .restic.env # Edit with your backup encryption password # Production scripts (adjust paths for your setup) cp backup-to-hdd.sh.example backup-to-hdd.sh chmod +x backup-to-hdd.sh # Nginx auto-update (pulls Dependabot changes and redeploys) cp nginx_update.sh.example nginx_update.sh chmod +x nginx_update.sh # Edit GIT_REPO_DIR if different from $HOME/docker
-
Start services:
# Start individual services cd proxy docker compose up -d # Or start all services (adjust as needed)
├── .github/
│ └── dependabot.yml # Automated Docker image updates
├── proxy/ # Nginx reverse proxy + Let's Encrypt
├── pihole/ # DNS-based ad blocker
├── homeassistant/ # Home automation stack (config templates: *.yaml.example)
├── immich/ # Photo management
├── paperless/ # Document management
├── freshrss/ # RSS feed reader
├── teslamate/ # Tesla vehicle data logging + Grafana
├── fail2ban/ # Intrusion prevention configs
└── scripts/ # Monitoring and backup automation
├── *.example # Template scripts (tracked in git)
└── *.sh # Production scripts (not in git, copy from .example)
- Automatic Let's Encrypt certificate management
- Multi-domain support with SANs
- Auto-renewal every 24 hours
- Nginx security headers (HSTS, CSP, X-Frame-Options, etc.)
- Rate limiting on authentication endpoints
- Connection limits per IP
- Service-specific timeouts and upload limits
- Custom filters for Nginx auth failures, rate limiting, and scanning
- Per-service jails (Immich, Paperless, FreshRSS, Home Assistant)
- Home Assistant special handling: Monitors HA's internal log (HA returns HTTP 200 for failed logins)
- Recidive jail for repeat offenders
- DOCKER-USER iptables chain integration
- All credentials in
.envfiles (excluded from git) - Template files (
.env.example,.yaml.example) for easy setup - Home Assistant configs use
.yaml.examplesuffix to indicate placeholders need customization - File permissions enforced (600 for credential files)
- No hardcoded secrets in scripts
Monitoring scripts send status updates to Home Assistant via MQTT:
- Backup Status - Real-time backup progress and statistics
- System Updates - Raspberry Pi package and firmware status
Scripts automatically create MQTT Discovery sensors:
sensor.restic_backup_status- Backup state and metricssensor.rpi_updates- System update statussensor.nginx_version- Currently installed nginx versionsensor.nginx_update_status- Update status (up_to_date, updated, error)sensor.nginx_last_check- When the update script last ransensor.nginx_last_update- When nginx was last updated
Example automation:
automation:
- alias: "Backup Failed Alert"
trigger:
- platform: state
entity_id: sensor.restic_backup_status
to: "failed"
action:
- service: notify.mobile_app
data:
title: "Backup Failed!"
message: "Check backup logs immediately"- Immich: Backs up photo library including auto-exported database dumps
- Paperless: Exports PostgreSQL database, then backs up all documents
- Home Assistant: Backs up configuration files (yaml configs, custom_components) and HA's own backup archives
- Excludes .storage/ and .cloud/ to avoid permission issues (included in HA backups)
- Storage: External HDD with encrypted Restic repository
- Automation: Cron-scheduled with MQTT status reporting
# Add to crontab
crontab -e
# Daily backup at 2 AM with logging
0 2 * * * /path/to/scripts/backup-to-hdd.sh >> /path/to/logs/backup.log 2>&1Prerequisites:
# Mount backup drive
sudo mount /dev/sda1 /mnt/sda1 -o rw,uid=$UID,user,dmask=007,fmask=117
# Load Restic password
cd ~/docker/scripts
source .restic.envList available snapshots:
restic -r /mnt/sda1/restic-repo snapshotsRestore Home Assistant:
# Stop Home Assistant
cd ~/docker/homeassistant
docker compose down
# Restore from latest snapshot
restic -r /mnt/sda1/restic-repo restore latest \
--target /tmp/restore \
--include '**/homeassistant/homeassistant/config'
# Copy restored files
cp -r /tmp/restore/home/stefan/docker/homeassistant/homeassistant/config/* \
~/docker/homeassistant/homeassistant/config/
# For full HA restore: Extract latest backup archive
cd ~/docker/homeassistant/homeassistant/config/backup
tar -xzf <latest-backup>.tar.gz
# Then restore via HA UI: Settings → System → Backups → Upload
# Start Home Assistant
docker compose up -d
# Cleanup
rm -rf /tmp/restoreRestore Immich:
# Stop Immich
cd ~/docker/immich
docker compose down
# Restore from specific snapshot (e.g., snapshot abc123)
restic -r /mnt/sda1/restic-repo restore abc123 \
--target /tmp/restore \
--include '**/immich/library'
# Copy restored files
cp -r /tmp/restore/home/stefan/docker/immich/library/* \
~/docker/immich/library/
# Start Immich
docker compose up -d
# Cleanup
rm -rf /tmp/restoreRestore Paperless:
# Stop Paperless
cd ~/docker/paperless
docker compose down
# Restore from latest snapshot
restic -r /mnt/sda1/restic-repo restore latest \
--target /tmp/restore \
--include '**/paperless/library'
# Copy restored files
cp -r /tmp/restore/home/stefan/docker/paperless/library/* \
~/docker/paperless/library/
# Restore database from backup
cd ~/docker/paperless/library/backup
gunzip < paperless_*.sql.gz | docker exec -i paperless-db-1 psql -U paperless
# Start Paperless
docker compose up -d
# Cleanup
rm -rf /tmp/restoreUnmount backup drive:
sudo umount /mnt/sda1Important Notes:
- Always test restores regularly to ensure backups are working
- For Home Assistant: Use HA's backup archives in
backup/directory for full restore - Adjust paths (
/home/stefan/docker) to match your$DOCKER_BASE - Consider restoring to a test directory first to verify data integrity
cd proxy
# Force certificate renewal
docker compose run --rm certbot renew --force-renewal
# Check certificate status
docker exec proxy-nginx-1 ls -la /etc/letsencrypt/live/All Docker images are pinned to specific versions and managed via GitHub Dependabot:
- Proxy (nginx, certbot): Checked daily (internet-facing, security-critical)
- All other services: Checked weekly on Sundays
Nginx Auto-Update Workflow:
1. Dependabot creates PR for new nginx version (daily at 03:00)
2. GitHub Action auto-merges minor/patch updates
3. nginx_update.sh (via cron) pulls changes and redeploys
4. Status sent to Home Assistant via MQTT
Setup cron for nginx auto-updates:
crontab -e
# Add: 0 4,8,12,16,20,0 * * * /path/to/scripts/nginx_update.sh >> /path/to/logs/nginx_update.log 2>&1Other Services (manual deployment):
- Dependabot creates PRs when new versions are available
- Review changelog and merge PR
- Deploy update:
cd <service-directory>
docker compose pull && docker compose up -d# Check jail status
sudo fail2ban-client status
# Unban IP
sudo fail2ban-client unban <IP_ADDRESS>
# View banned IPs
sudo fail2ban-client bannedPostgreSQL Versions:
| Service | Version | Image |
|---|---|---|
| Paperless | 18 | postgres:18 |
| Teslamate | 18 | postgres:18-alpine |
| Immich | 14 | Custom with VectorChord |
PostgreSQL 18+ Volume Mount:
PostgreSQL 18 uses a version-specific data directory. Mount at /var/lib/postgresql (not /data):
volumes:
- ./postgres:/var/lib/postgresql # Creates 18/docker subdirectoryMajor Version Upgrade (e.g., 17 → 18):
# 1. Backup
docker exec -t <container> pg_dumpall -U <user> > backup.sql
# 2. Stop and rename old data
docker compose down
mv ./postgres ./postgres_old
# 3. Update docker-compose.yml, then start new DB
docker compose up -d db && sleep 10
# 4. Import and start all
cat backup.sql | docker exec -i <container> psql -U <user>
docker compose up -d-
Domain Configuration:
- Update
proxy/.envwith your domains - Modify
proxy/nginx/default.conf.templatefor your services
- Update
-
Script Paths:
scripts/backup-to-hdd.sh: SetDOCKER_BASEto your Docker directory
-
Hardware Dependencies:
- Home Assistant: Requires Zigbee USB dongle (
/dev/ttyACM0) - Pi-hole: Requires DNS ports (53), may conflict with systemd-resolved
- Home Assistant: Requires Zigbee USB dongle (
This is a personal homelab setup, but feel free to:
- Open issues for questions or suggestions
- Fork and adapt for your own use
- Share improvements via pull requests
This project is provided as-is for educational and personal use. Adapt as needed for your homelab.
- Security: Review all configurations before exposing to the internet
- Backups: Test restore procedures regularly
- Updates: Keep Docker images and system packages up to date
- Monitoring: Set up alerts for critical services
Built with ❤️ for the self-hosting community