Files
pragmatic-deployer/docs/plan.md
T

4.8 KiB

Pi-first rollout plan

The Pi is the first inventory target for validation. The Ansible playbooks target the generic debian_servers group and can be used for any Debian or Ubuntu host with per-host inventory values.

Agreed design

  • Ansible is the host configuration and deployment controller.
  • Docker Compose defines each service in its own stacks/<service>/ directory.
  • The local Git checkout bootstraps Gitea; after Gitea is available, it is added as a remote.
  • Traefik is the only public HTTP(S) edge. It uses file-provider route fragments supplied alongside each service, rather than Docker labels requiring access to the Docker API.
  • All proxied services join the externally created traefik network. Databases and internal workers remain on per-stack private networks.
  • DIUN alerts about registry image changes. It uses its file provider and per-service image watch files, so it needs no Docker socket. Alerts prompt a reviewed update; they do not auto-update services.
  • restic backs up important persistent data to a separate destination with storage-aware retention and a tested restore procedure.
  • Portainer is out of scope.

Pi facts collected

  • Raspberry Pi 4 Model B Rev 1.1, 4 GB RAM, 64-bit ARM.
  • Debian GNU/Linux 13 (Trixie), kernel 6.12.47+rpt-rpi-v8.
  • Docker Engine 28.5.1 and Compose v2.40.0 are installed.
  • Root filesystem: 115 GB, about 101 GB available at discovery.
  • SSH is listening on all IPv4 interfaces at port 22. Root key login and password login are currently permitted. ufw is not installed and Fail2ban is inactive. The provided Windows public key was not in /home/pi/.ssh/authorized_keys; the pi password login still works.
  • Containers egress-proxy and uptime-kuma were stopped at the owner's request. Their files and data were left untouched.
  • Native Nginx (80/tcp) and sing-box (2080/tcp, 2081/tcp) are still running. The Pi inventory temporarily includes these ports in the UFW allow-list so the firewall rollout does not silently cut them off. Remove them from inventory when the services are retired or migrated.

Steps

  1. Resolve the Windows-to-Pi SSH route conflict: Cisco AnyConnect installs a route for the Pi's LAN subnet through its VPN interface. With Cisco disconnected, Windows SSH works on the direct Ethernet route. Separately, the installed Windows Ansible launcher still fails at startup (WinError 87); Ansible documents Windows as unsupported as a control node. WSL is excluded, so the playbooks are authored here but cannot be executed from this Windows Ansible install until a supported non-WSL controller is available.
  2. Create the ops admin account with the provided key and a Vault-protected password hash; verify key login and password-required sudo while the existing pi session/port remains available.
  3. Apply the baseline, Fail2ban, UFW allow-list, root-login denial, and key-only SSH on both port 22 and the configured replacement port.
  4. Verify a fresh connection to the replacement SSH port. Only after that succeeds, update ansible_port, set ssh_keep_legacy_port: false, and redeploy to remove port 22 from sshd and UFW.
  5. Add future service ingress by editing that host's ufw_allowed_rules list. Retire a port through ufw_remove_rules for one run. Keep Docker host-port behavior separate from UFW INPUT rules.
  6. Create the external Traefik network and deploy Traefik, DIUN, and Uptime Kuma from their separate Compose directories. Initially use test ports that avoid the Pi's native Nginx port 80. Decide whether to reuse or replace existing Uptime Kuma data before mounting it.
  7. Configure DIUN image notifications (notification channel/credentials still need choosing). Notifications prompt reviewed image updates; DIUN does not update containers automatically.
  8. Verify routing, notification delivery, reboot persistence, and a storage-aware restic backup/restore on the Pi.
  9. Deploy Gitea and push the local repository to it. Add 3x-ui, password manager, Logseq sync, portfolio, and pet projects individually, each in its own stack directory with an optional Traefik route fragment and DIUN watch file.
  10. Once the Pi runbook is stable, add the Ubuntu VPS and planned nearby VPS as inventory hosts, with per-host variables as needed.

Safety gates

  • No automated reboot during the first pilot.
  • No deletion or migration of existing application data before a backup and an explicit service cutover plan.
  • Bootstrap and verify the new admin account before disabling SSH password authentication.
  • Keep the old SSH port listening and allowed through the first hardening run. Change inventory to the new port only after a second SSH login succeeds; remove the old port in a later run.
  • Keep an existing SSH session open throughout the port migration.
  • Review UFW allow-list changes against required native and Docker-published services before applying.
  • Container updates remain manual and reviewable after DIUN alerts.