Files
deploy-felhom-compose/scripts
admin 296fdbfdcb v0.22.1: Fix setup wizard bugs (detection, CSRF panic, version display, IP)
- NeedsSetup: only check for empty customer.id (not "demo-felhom")
- renderError: pass *http.Request to ensureCSRFToken (was nil → panic)
- Welcome template: remove redundant "v" prefix from version display
- IP detection: read HOST_IP env var for Docker container awareness
- docker-setup.sh: inject HOST_IP into generated docker-compose.yml
- Add logging for Hub config download in setup wizard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 13:30:32 +01:00
..

scripts/

Setup and maintenance scripts for Felhom homeserver deployments.


docker-setup.sh

Automated deployment script for Felhom customer nodes.

Takes a fresh Debian 13 server and deploys a complete Felhom homeserver stack: Docker, Traefik reverse proxy, Cloudflare Tunnel (optional), TLS certificates, FileBrowser, and the felhom-controller dashboard.

Version: 5.0.0

Quick start

# Minimal — interactive wizard for all settings
sudo ./docker-setup.sh --domain example.com --email admin@example.com

# Full — Cloudflare DNS + static IP + pre-seeded customer
sudo ./docker-setup.sh \
  --ip 192.168.0.50 \
  --domain example.com \
  --email admin@example.com \
  --cf-token "your-cloudflare-api-token" \
  --customer "customer-1"

# Hub mode — download pre-configured controller.yaml from Felhom Hub
sudo ./docker-setup.sh \
  --domain example.com \
  --email admin@example.com \
  --hub-customer "customer-1" \
  --hub-password "retrieval-password-from-hub"

CLI flags

Flag Argument Description Default
--bootstrap Install sudo on fresh Debian (run as root first)
--ip ADDRESS Static IP address (e.g., 192.168.0.50) DHCP
--gateway ADDRESS Gateway address 192.168.0.1
--dns SERVERS DNS servers, comma-separated 1.1.1.1,8.8.8.8
--interface NAME Network interface name Auto-detect
--domain DOMAIN Base domain (pre-seeds wizard) homeserver.local
--email EMAIL ACME email for Let's Encrypt
--cf-token TOKEN Cloudflare API token for DNS-01 challenge
--customer ID Customer identifier (pre-seeds wizard)
--hub-customer ID Download config from Hub: customer ID
--hub-password PASSWORD Download config from Hub: retrieval password
--traefik-password PASSWORD Traefik dashboard basicAuth password Auto-generated
--self-signed-cert Generate self-signed wildcard certificate false
--skip-filebrowser Skip FileBrowser installation false
--dry-run Show plan without making changes false
--debug Enable bash debug tracing (set -x) false
-h, --help Show help message

Installation steps

The script runs these steps in order:

Step Function Description
1 install_base_packages() Install system packages: curl, git, htop, jq, openssl, apache2-utils, etc.
2 configure_static_ip() Configure static IP (optional). Supports NetworkManager, systemd-networkd, ifupdown.
3 install_docker() Install Docker CE + Compose plugin. GPG key, repo setup, daemon.json with DNS fallback, traefik-public network.
4 install_traefik() Deploy Traefik v3.6.7 reverse proxy with configurable TLS (Let's Encrypt DNS-01/HTTP-01 or self-signed).
4b install_cloudflare_tunnel() Deploy Cloudflare Tunnel (optional, only if tunnel token provided in wizard).
5 generate_self_signed_cert() Generate self-signed CA + wildcard cert (optional, if --self-signed-cert flag set).
6 run_config_wizard() Interactive wizard or Hub download. Generates controller.yaml with customer settings.
7 install_filebrowser() Deploy FileBrowser Quantum with auto-discovered drive mounts (optional).
8 install_controller() Deploy felhom-controller (privileged container with system access).
9 install_tools_and_configure() Install ctop, lazydocker, Docker shell aliases.

TLS certificate modes

The script supports three mutually exclusive TLS modes:

  1. Let's Encrypt + Cloudflare DNS-01 (recommended)

    • Requires: --email + --cf-token
    • Works with Cloudflare Tunnel (no public port 80 needed)
    • Automatic renewal via Traefik
  2. Let's Encrypt + HTTP-01

    • Requires: --email (no --cf-token)
    • Requires port 80 publicly accessible
    • Does NOT work with Cloudflare Tunnel
  3. Self-signed certificate

    • Requires: --self-signed-cert
    • Generates 10-year wildcard cert with custom CA
    • CA cert copied to user home for manual device import

Hub download mode

When both --hub-customer and --hub-password are provided, the script downloads a pre-configured controller.yaml from the Felhom Hub instead of running the interactive wizard:

GET https://hub.felhom.eu/api/v1/config/{customer_id}
Header: X-Retrieval-Password: {password}

On success:

  • Saves downloaded YAML as controller.yaml (permissions 600)
  • Extracts domain, email, CF tokens for use by subsequent setup steps (Traefik, Cloudflare Tunnel)
  • Skips the interactive wizard entirely

On failure:

  • Logs a warning with HTTP status code
  • Falls back to the interactive wizard

Hub credentials are created in the Hub web UI at https://hub.felhom.eu/configs.

Configuration wizard

When Hub download is not used, the interactive wizard prompts for:

Section Fields
Customer identity ID, display name, domain, email
Infrastructure secrets Cloudflare Tunnel token, CF API token
Paths System data partition mount point
Dashboard password bcrypt-hashed login password (optional)
Git sync App catalog repo URL, username, token
Monitoring 5x healthchecks.io ping UUIDs (heartbeat, system, DB dump, backup, integrity)

Validation:

  • Customer ID is required (cannot be empty or demo-felhom)
  • Domain is required (cannot be homeserver.local)

Output files

Path Description
/opt/docker/felhom-controller/controller.yaml Controller configuration (perms 600)
/opt/docker/felhom-controller/docker-compose.yml Controller container definition
/opt/docker/traefik/traefik.yml Traefik static configuration
/opt/docker/traefik/dynamic/dashboard.yml Traefik dashboard route
/opt/docker/traefik/docker-compose.yml Traefik container definition
/opt/docker/stacks/filebrowser/docker-compose.yml FileBrowser container definition
/opt/docker/cloudflared/docker-compose.yml Cloudflare Tunnel definition (optional)
/var/log/docker-setup.log Full installation log

Safety features

  • Error trapping: set -euo pipefail + ERR trap with diagnostic collection
  • Dry-run mode: Full preview without state changes
  • Input validation: IP format, required fields, credential checks
  • Atomic writes: Temp file + rename for sensitive config files
  • Permission hardening: 600 for secrets, 644 for certificates
  • Idempotency: Checks for existing installations, skips if already done
  • Docker fallback: Falls back from Debian 13 (trixie) to 12 (bookworm) repo if needed
  • DNS verification: Tests DNS resolution before proceeding
  • Health checks: Verifies Docker daemon ready, containers running after each deploy

Prerequisites

  • OS: Debian 13 (Trixie) — minimal support for other distros with warnings
  • Privileges: Must run with sudo
  • Network: Internet connection for package downloads and Docker image pulls
  • DNS: Must resolve before script runs (verified via getent hosts download.docker.com)

Bootstrap mode

On a fresh Debian install without sudo:

# As root — install sudo and configure user
./docker-setup.sh --bootstrap

# Then as regular user — full setup
sudo ./docker-setup.sh --domain example.com --email admin@example.com

Shell aliases installed

After setup, these aliases are added to the user's .bashrc:

dc='sudo docker compose'
dcu='sudo docker compose up -d'
dcd='sudo docker compose down'
dcl='sudo docker compose logs -f'
dps='sudo docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
dlogs='sudo docker logs -f'
dexec='sudo docker exec -it'
dprune='sudo docker system prune -af'

Helper tools installed

  • ctop — Top-like interface for container metrics
  • lazydocker — Terminal UI for Docker management