Files
ubuntu-post-install/MODULAR.md
T
Claude 9dc8c4063d v0.9.7: Caddy + CrowdSec modules; category menu with required-gate
- services/caddy.sh (homelab): reverse proxy + auto HTTPS, own ~/docker/caddy
  folder (compose + starter Caddyfile + README).
- services/crowdsec.sh (homelab): system-level IPS (agent + firewall bouncer +
  Caddy acquisition + optional ntfy alerts), README in ~/docker/crowdsec.
- setup.sh guided flow redesign:
  * Prints REQUIRED set (essentials + glow + docker check) with a cancel option.
  * Offers Caddy first (most services proxy through it).
  * Category menu LOOP: pick category -> checklist ([installed] marked) ->
    install -> back to menu, until Done. whiptail + text fallback.
- Categories reorganized: base/homelab/utilities/media/cameras/gaming/backup;
  moved ntfy/filebrowser/portainer/uptimekuma/watchtower to utilities;
  caddy->crowdsec->authelia ordered first in homelab.

Verified: bash -n all; --list groups by category with caddy first; cancel path
prints 'Cancelled, nothing changed'; dry-run guided flow runs required + loops
menu; run-one still works.

https://claude.ai/code/session_017eA2qqq9jfF2tNtpUYL8vK
2026-06-03 17:16:32 +00:00

3.9 KiB

Modular Post-Install (setup.sh + lib/ + services/)

This is the new structure that gives you one source of truth and the ability to run just the service you want — without maintaining a pile of near-duplicate standalone scripts.

Why

The full ubuntu-post-install-*.sh scripts are great as a "run once, set up the whole box" experience, but to add or update one service you edit a 300 KB file (in two or three places). The separate setup-*.sh scripts are easy to run for one service, but duplicate logic and drift apart.

The fix is not to generate per-service scripts from the monolith (that just triples the maintenance surface). It's to have one implementation per service in a module, shared helpers in a library, and a thin dispatcher with two entry points.

Layout

setup.sh              # dispatcher: menu, run-one, --list, --dry-run, --unattended
lib/common.sh         # shared helpers: logging, prompts, ownership, Caddy wiring,
                      #   the service registry. THE single source of truth.
services/
  base.sh             # essential CLI packages (incl. glow)
  homeassistant.sh    # Home Assistant
  ...                 # one file per service

Usage

sudo ./setup.sh                  # interactive menu (whiptail or text)
sudo ./setup.sh homeassistant    # install one service
sudo ./setup.sh base glow        # install several
./setup.sh --list                # list services, grouped
sudo ./setup.sh --dry-run --unattended minecraft   # preview, no prompts

Anatomy of a service module

Each services/<name>.sh does exactly two things: register itself and define install_.

#!/bin/bash
register_service myapp homelab "What it does" 1234   # name group description [port]

install_myapp() {
    require_docker || return 1
    local DIR="$DOCKER_DIR/myapp"
    [ "$DRY_RUN" = true ] && { echo "[DRY-RUN] Would create $DIR"; return 0; }
    mkdir -p "$DIR"; ensure_docker_dir_ownership "$DIR"; cd "$DIR" || return 1
    cat > docker-compose.yml << 'YAML'
    ...
YAML
    configure_caddy_for_service "MyApp" "1234" "myapp"   # optional reverse proxy
    prompt_yn "Start now? (y/n):" "y" START && docker compose up -d
}

Helpers available from lib/common.sh: log_info/success/warning/error, prompt_yn, prompt_text, run_cmd, ensure_docker_dir_ownership, generate_password, validate_password, configure_caddy_for_service, require_root, require_docker. Globals: DOCKER_DIR, ACTUAL_USER, ACTUAL_HOME, DRY_RUN, UNATTENDED.

Every service installs to its own folder ~/docker/<name>/ with its own docker-compose.yml (the DoTheEvo selfhosted-apps-docker layout) — never a single shared compose file.

Groups

base · homelab · utilities · media · cameras · gaming · backup. The guided menu (sudo ./setup.sh) shows the required packages first with a cancel option, then offers Caddy (most services proxy through it), then a category menu you loop through — pick a category, tick services (already installed ones are marked [installed]), install, and you land back on the menu to pick the next category. Within homelab, Caddy → CrowdSec → Authelia are ordered first.

Migration status

This is an incremental migration. The big ubuntu-post-install-*-crowdsec.sh script remains the current "install everything" entry point until the modules reach parity, at which point it is retired (like the original and -no-keycloak tiers, which stay frozen as the evolution record).

Module Status
base (incl. glow) done
homeassistant done
minecraft (multi-instance, rich) porting from setupminecraft.sh
wolf (gaming) porting from setupwolf.sh
js99er (gaming) porting from setupjs99er.sh
backup (Kopia, cross-cutting) porting from setupbackup.sh
remaining ~65 services migrate from the monolith incrementally