# 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:** 6.0.0 ### Quick start ```bash # 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: ```bash # 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`: ```bash 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 --- ## felhom-wipe.sh **Test node cleanup script with 4 wipe levels.** Removes felhom-managed data from a node in a controlled, repeatable way. Designed for test/demo nodes to reset state between testing cycles. ### Quick start ```bash # Preview what will be removed (dry run — default) sudo ./felhom-wipe.sh --level full # Execute the wipe sudo ./felhom-wipe.sh --level full --yes ``` ### Wipe levels | Level | What it removes | |-------|-----------------| | `soft` | Controller state files only: `settings.json`, `metrics.db`, `setup-state.json`, `update-state.json`, `session-data.json`, `snapshot-history.json` | | `controller` | Soft + all non-infra Docker containers, all Docker volumes (except `portainer_data`), all stack directories (skips protected stacks by default) | | `full` | `controller`-level cleanup + `felhom-data/` on all storage drives (appdata, backups). Also removes old-style `appdata/` and `backups/` directories for pre-v0.26.0 compatibility. Infra containers (including felhom-controller) are **preserved**; controller is restarted after cleanup. | | `nuclear` | Full + all infra containers (controller, traefik, cloudflared, portainer), DR markers (`.felhom-infra-backup/` on all drives), `docker system prune -af --volumes`, and all infra config directories (`/opt/docker/felhom-controller/`, `/opt/docker/traefik/`, `/opt/docker/cloudflared/`, `/opt/docker/stacks/`) | ### CLI options | Option | Description | |--------|-------------| | `--level ` | Required. One of: `soft`, `controller`, `full`, `nuclear` | | `--yes` | Execute the wipe. Default is dry-run (preview only). | | `--include-protected` | Also remove protected stacks (controller level only). | ### Path auto-detection - Reads `stacks_dir` and `data_dir` from `/opt/docker/felhom-controller/controller.yaml` if present - Reads registered storage paths from `settings.json` - Also scans `/mnt/*/` for `felhom-data/` or legacy `appdata/` directories not in the registry ### What is preserved - OS and system files - Infrastructure containers and config (unless `nuclear`) - User files: `Dokumentumok/`, `media/`, other non-felhom directories on drives - DR markers on drives (unless `nuclear`) ### Safety - Dry-run by default — shows plan without deleting anything - Interactive `YES` confirmation prompt required even with `--yes` - Must run as root (`sudo`) - Checks Docker is running before proceeding - Protected stacks skipped by default (use `--include-protected` to override) ### Redeploy after nuclear wipe ```bash curl -fsSL https://gitea.dooplex.hu/admin/deploy-felhom-compose/raw/branch/main/scripts/docker-setup.sh | bash ```