41 KiB
Infra — Personal Infrastructure as Code
English version: README.md
Questo repository contiene la configurazione Infrastructure as Code (IaC) utilizzata per gestire e mantenere allineate diverse macchine personali tramite Ansible.
L'obiettivo è avere una singola fonte di verità per:
- configurazione delle macchine
- pacchetti installati
- servizi di sistema
- configurazioni utente (dotfiles)
Il repository consente di gestire più sistemi operativi e profili macchina mantenendo una struttura modulare, riproducibile e idempotente.
Architettura del progetto
infra/
├── ansible/
│ ├── ansible.cfg
│ ├── site.yml
│ ├── inventory/
│ │ ├── hosts.yml
│ │ ├── group_vars/
│ │ └── host_vars/
│ ├── templates/
│ └── roles/
│
├── dotfiles/
│ ├── common/
│ ├── desktop/
│ ├── fedora/
│ ├── server/
│ ├── workstation/
│ ├── workstation_dev_wsl/
│ └── nymph/
│
├── scripts/
├── secrets/
├── README.md
└── README.it.md
Il repository è diviso in due componenti principali:
| Componente | Scopo |
|---|---|
| ansible | provisioning e configurazione macchine |
| dotfiles | configurazioni utente versionate |
Macchine gestite
Il repository modella attualmente host Fedora/GNOME, una workstation Fedora WSL, un server Rocky Linux 9 e un NAS Rocky Linux 9. La composizione resta separata in assi indipendenti:
common user environment
+ host-specific platform
+ role-specific software
+ independently selectable desktop
+ host hardware overrides
Matrice target:
| Host | Platform | Role | Desktop |
|---|---|---|---|
| ikaros | Fedora | Personal workstation | GNOME |
| nymph | Fedora | Desktop laptop | GNOME |
| deadalus | Fedora WSL | Workstation dev | — |
| prometheus | Rocky Linux | Server | — |
| atlas | Rocky Linux | NAS | — |
Regola operativa:
ikaros must be boring
nymph is allowed to break
ikaros usa Fedora Workstation/GNOME come desktop personale stabile; nymph usa lo stesso
target Fedora Workstation/GNOME come laptop. I gruppi legacy void e desktop restano alias di
compatibilita per eventuali host Void futuri mentre i nuovi assi sono
platform_*, role_* e desktop_*.
Nota sullo stato attuale del playbook principale:
ansible/site.ymlapplica oggi in automatico Fedora/GNOME suikarosenymphansible/site.ymlapplica il profilo Fedora WSL alla workstationdeadalusansible/site.ymlapplica il profilo server Rocky aprometheuscon DNF, systemd, dotfiles server e firewalldansible/site.ymlapplica il profilo NAS Rocky suatlastramite SSH remoto
Nodo pianificato e posticipato: Cerberus
cerberus e un nodo di management posticipato, in attesa dell'allestimento
fisico dell'ufficio nella nuova casa. Non e ancora presente nell'inventory e non
esistono ruoli o playbook che lo prendano come target.
L'hardware previsto e un Lenovo ThinkCentre M700 Tiny (Intel Core i3-6100T, 8 GB di RAM e SSD da 256 GB) con Ethernet nativa a 1 Gbps. Condividera monitor e periferiche di Ikaros tramite uno switch KVM a ingressi multipli, usando un cavo passivo DisplayPort-HDMI per il collegamento video. Il sistema operativo previsto e Fedora Sericea, la variante Fedora immutabile con compositor Wayland Sway.
Cerberus sara un management plane isolato: Ansible verra eseguito in un ambiente
Toolbox dedicato per il provisioning del futuro cluster uranus, anziche da
Ikaros o da un host non gestito. Lo stack di osservabilita rootless Podman
eseguira Grafana, Prometheus e Loki. L'SSD locale sara l'hot storage, con
metriche e log conservati per 30 giorni; esportazioni programmate trasferiranno
i dati storici piu vecchi su un dataset Atlas montato via NFS come cold storage.
Il piano di implementazione, con prerequisiti espliciti, e in AGENTS.md.
Desktop
Target operativi:
ikaros: Fedora Workstation + GNOME, desktop personale stabile/floating.nymph: Fedora Workstation + GNOME, laptop desktop con dotfiles desktop condivisi e GNOME lasciato al default Fedora.
Il profilo Void desktop resta disponibile come modello riutilizzabile per host
futuri e usa esclusivamente desktop_environment: minimal: Sway e il default,
mentre Niri si seleziona con il gruppo desktop_niri. GNOME e disponibile solo
sui target Fedora tramite desktop_gnome.
Lo stato attuale del profilo desktop include, tra le altre cose:
- dotfiles comuni e desktop
- sessioni Sway e Niri per eventuali host Void in modalita
minimal empttycon default host-specific in modalitaminimale session file Wayland persway- pacchetti Void Linux e servizi runit; le liste pacchetti Void desktop sono separate per criterio:
void_packages_baseper il runtime sistema (init, kernel, audio core, networking, firewall, hw daemons)desktop_common_packagesper l'infrastruttura condivisadesktop_minimal_packagesper applicazioni GTK eempttydesktop_sway_packagesper i binari specifici della sessione Sway
turnstileper i servizi utente Void, inclusossh-agentssh-agentcon socket stabile condiviso tra shell e SSH in~/.local/state/ssh-agent/socket- Emacs usa una sola configurazione orientata a Org e authoring, condivisa da desktop Fedora/GNOME e workstation; Vim resta l'editor di sviluppo
tmuxcon plugin gestiti da TPM al bootstrap del profilo desktop- Flatpak con remoto Flathub
- GNOME Keyring e
udiskienella modalita minimale - multi-monitor Void: sotto Sway è gestito da
kanshi
Workstation
La workstation deadalus usa Fedora in WSL sulla macchina Windows omonima, senza runtime Flatpak o Snap.
Il profilo è pensato per sviluppo e lavoro.
Nel modello Ansible usato qui, un singolo inventory host puo appartenere intenzionalmente a piu gruppi e quindi ricevere piu play nello stesso run: l'associazione non e 1 host = 1 play, ma host + gruppi = layering finale.
Il profilo workstation e agganciato al playbook principale tramite:
- layer dev Fedora
- layer WSL dedicato per sviluppo con
systemd
Lo stato attuale del profilo workstation include:
- installazione pacchetti base Fedora via dnf
- installazione e configurazione di Docker dal repository ufficiale
- installazione di Mise dal COPR ufficiale con JDK Eclipse Temurin Java 11 fissato
- gestione dei dotfiles workstation e rendering dei template dev condivisi
- preparazione di Fedora WSL con
systemdper il toolchain di sviluppo - attivazione del firewall
firewalldsui target Fedora che dichiarano regole host-specifiche
Workflow WSL previsto:
- avviare Fedora WSL almeno una volta e completare la creazione dell'utente Linux
- installare Ansible dentro la distribuzione WSL
- lanciare il playbook dalla distribuzione su
deadalusper configurare l'ambiente dev locale - usare VS Code con le estensioni Remote (
WSL,SSH,Dev Containers) dal lato Windows
Le applicazioni Windows sono installate e gestite manualmente; il profilo WSL non installa componenti di remoting Python per esse.
Server
Sistema operativo:
- Rocky Linux 9
Configurazione:
- nessun ambiente grafico
Macchina:
prometheus
Profilo orientato a servizi server e gestione di dotfiles dedicati.
Lo stato attuale del profilo server include:
- installazione pacchetti Rocky via DNF, EPEL e CRB
- installazione di Podman e podman-compose
- abilitazione dei servizi systemd dichiarati in inventory/group vars
- copia dei dotfiles server e rendering del
docker-compose.ymlper Nginx Proxy Manager e Gitea, piu l'unitapodman-compose-server(attivazione manuale) - attivazione di firewalld con SSH, Cockpit (
9090/tcp), HTTP e HTTPS abilitati - Syncthing escluso dal profilo server Rocky
Il Compose desiderato su Prometheus non include piu Navidrome ne il database PostgreSQL obsoleto.
Navidrome e Syncthing appartengono ad Atlas; Navidrome ufficiale usa invece SQLite. Il profilo non
arresta o rimuove automaticamente eventuali container legacy e non elimina /opt/postgres/data.
Nginx Proxy Manager pubblica solo 80/tcp e 443/tcp; la sua interfaccia di amministrazione e
associata a 127.0.0.1:81 ed e raggiungibile da Ikaros o Nymph con l'alias Bash npm-tunnel.
Nextcloud resta disabilitato e il profilo non crea directory /srv/nextcloud.
La fase 1 su Atlas non modifica questo deployment NPM ne i suoi dati persistenti. Dopo aver attivato
WireGuard e i servizi Atlas, configurare i proxy host NPM correnti con upstream Navidrome
http://10.0.0.2:4533 e upstream per la GUI Syncthing http://10.0.0.2:8384. Solo la GUI web di
Syncthing usa NPM; il traffico di sincronizzazione resta sulle porte native pubblicate esplicitamente solo
sull'indirizzo WireGuard di Atlas. Configurare l'autenticazione Syncthing e una policy di accesso NPM adeguata prima di pubblicare la GUI.
DuckDNS
profile_server genera ~/duckdns/duck.sh con permessi 0700, mantenendo il percorso dello
script e duck.log. Definire server_duckdns_domain negli host vars del server e salvare il
nuovo token rigenerato in vault_duckdns_token, nel Vault cifrato secrets/vault.yml
(ansible-vault edit secrets/vault.yml) oppure negli override non versionati secrets/vault.local.yml.
Non committare lo script generato e non passare il token sulla riga di comando. Il rendering
nasconde output e diff sensibili; lo script verifica TLS e passa il token a curl tramite stdin.
Il playbook non esegue lo script e non modifica la sua schedulazione esterna.
ansible-playbook ansible/site.yml --limit prometheus --tags duckdns --check --diff
ansible-playbook ansible/site.yml --limit prometheus --tags duckdns
La cancellazione dalla cronologia non revoca il token: rigenerarlo sul pannello DuckDNS. Dopo la bonifica, riclonare gli altri checkout senza unire nuovamente la vecchia storia; salvare separatamente eventuali modifiche non committate senza copiare segreti.
Migrazione dati
Dopo il provisioning Rocky, eseguire scripts/migrate_prometheus_data.sh sul server Ubuntu
sorgente. Lo script usa rsync, e in dry-run di default; richiede --quiesce-source --execute per
fermare lo stack sorgente e copiare in modo consistente soltanto i dati di Nginx Proxy Manager e
Gitea. Non sposta Navidrome o Syncthing, non avvia container, non cancella dati e non esegue il
cutover.
Utente del profilo server:
- il profilo usa
server_username,server_user_groupeserver_user_homedefiniti inansible/inventory/group_vars/server.yml - per default
server_usernameereditausername, ma puo essere sovrascritto per tutti gli host server via inventory oppure a runtime con extra vars - esempio override da CLI:
ansible-playbook ansible/site.yml --limit prometheus -e server_username=myuser
- se necessario puoi passare anche:
ansible-playbook ansible/site.yml --limit prometheus -e server_username=myuser -e server_user_group=mygroup -e server_user_home=/srv/myuser
NAS
atlas è un NAS Rocky Linux 9 raggiunto via SSH. Normalmente il pool esiste già e il profilo gestisce
solo i dataset figli. La creazione iniziale del RAIDZ2 richiede esplicitamente atlas_create_pool=true
e quattro percorsi /dev/disk/by-id/... verificati in atlas_zpool_disks. Il ruolo non partiziona,
forza, distrugge, ripristina né modifica il layout vdev di un pool esistente. I client Linux usano NFSv4,
quelli Windows/WSL SMB; l'accesso è limitato alla LAN configurata.
Per il primo avvio servono vault_atlas_admin_password_hash, vault_atlas_samba_password e
vault_atlas_immich_db_password; il primo è un hash compatibile con /etc/shadow, non una password
Cockpit in chiaro. Il bootstrap usa l'amministratore preesistente:
ansible-playbook ansible/site.yml --limit atlas \
-e atlas_connection_username=<existing-admin>
Le esecuzioni successive usano atlas_admin_username. Storage, condivisioni e firewall LAN sono
abilitati; prima dell'applicazione verificare pool, mountpoint, subnet e zona firewalld. La creazione
del pool è protetta da un gate esplicito e avviene solo se è assente. Atlas non fa più parte della VPN
WireGuard: la vecchia interfaccia è stata ritirata manualmente dopo la verifica del collegamento tra
Prometheus e Aegis. Le chiavi SSH autorizzate sono in file separati sotto
~/.ssh/authorized_keys.d/. atlas_manage_media_stack resta disabilitato finché /dev/dri, percorsi
dei container e segreto del database Immich non sono validati.
Sotto zpool Atlas crea archive (SMB), services/data con i dataset applicativi
services/data/navidrome e services/data/syncthing, media, media/music, media/photobook e
backup/hosts/prometheus. Archivio e applicazioni usano zstd; media, Syncthing e backup host usano
lz4. backup ha una riserva di 500G che copre i discendenti. SELinux targeted è persistente;
l'eventuale riavvio necessario viene segnalato, non eseguito. Atlas assegna l'interfaccia primaria
alla zona firewalld gestita, rifiuta redirect e source route, registra i martian, mantiene il reverse-path
filter loose e disabilita il forwarding IPv4. SSH consente soltanto l'amministratore dichiarato con
chiave pubblica: root, password, agent forwarding e remote forwarding sono disabilitati, mentre il
forwarding locale resta disponibile per i tunnel amministrativi. SMB3 espone Archive agli account
autorizzati da Vault sulla LAN, solo su TCP/445 con cifratura e firma obbligatorie. NFSv4 espone
soltanto media/photobook all'IP di Aegis su TCP/2049, con all_squash verso UID/GID 1100.
L'account di sistema immich usa UID/GID 1100, non ha shell di login né gruppo wheel e riceve i
gruppi video e render. Lo stack Immich futuro prevede Quadlet rootful per Server, ML, cache,
PostgreSQL e NPM su una rete Podman comune. Immich gira come 1100:1100, Server e ML ricevono
/dev/dri e Photobook è montato in sola lettura su /external/photobook. NPM pubblica 80 e 443;
l'interfaccia amministrativa resta su 127.0.0.1:81, raggiungibile via tunnel SSH.
Atlas ospita temporaneamente Navidrome e Syncthing rootless fino alla sostituzione con Uranus. I
servizi sono inizializzati ex novo, senza migrare lo stato precedente, rispettivamente sotto
/zpool/services/data/navidrome e /zpool/services/data/syncthing; la musica in
/zpool/media/music viene popolata separatamente. Sono vincolati all'indirizzo LAN di Atlas
(192.168.178.55), mai a WireGuard. wireguard_overlay collega invece Prometheus (10.0.0.1)
e Aegis (10.0.0.2): le chiavi private restano sui rispettivi host e Ansible scambia solo le pubbliche.
Prometheus apre 51820/udp; Aegis inoltra soltanto il traffico overlay→LAN dichiarato e applica
source NAT, evitando interfacce VPN su Atlas/Uranus e route statiche sul router. Navidrome (4533/tcp)
e la GUI Syncthing (8384/tcp) ammettono solo Aegis, mentre le porte native Syncthing sono limitate
alla LAN. Dopo la verifica dei servizi, configurare manualmente i Proxy Host NPM verso
http://192.168.178.55:4533 e http://192.168.178.55:8384. Il peer Prometheus include la LAN
negli AllowedIPs; aggiungere la VIP Uranus quando esisterà. Dopo il reload di firewalld, Ansible
ricarica le reti Podman rootful di Prometheus per conservare DNS e connettività del proxy.
Validare il gateway con:
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit prometheus,aegis --tags wireguard --check --diff
La prima esecuzione reale WireGuard deve includere entrambi i peer. Se Aegis ha appena installato il
layer wireguard-tools, riavviarlo manualmente e rieseguire senza --check: il ruolo attende un
handshake effettivo.
Gli snapshot ZFS ricorsivi coprono l'intero pool: 24 orari al minuto 05, 30 giornalieri alle 00:15,
8 settimanali la domenica alle 01:00 e 12 mensili il primo giorno alle 02:00. La retention elimina
solo gli snapshot con prefisso gestito atlas-auto e non esegue rollback. Lo scrub OpenZFS mensile è
previsto la prima domenica alle 03:00; il timer settimanale incompatibile è disabilitato. Il primo
snapshot orario ricorsivo è riuscito; la prima pulizia pianificata e il primo scrub schedulato
richiedono ancora una verifica a runtime.
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags snapshots,scrub --check --diff
Il backup Borg cifrato usa il sub-account Hetzner u660064-sub1, il repository relativo ./borg-data
e Borg remoto 1.4 su SSH porta 23. La chiave ED25519 del server è fissata; una chiave client dedicata
appartiene all'account borg, bloccato e senza login, sudo o gruppi supplementari. La chiave privata
resta in /etc/atlas-borg; la passphrase proviene da vault_atlas_borg_passphrase ed è resa in un
file 0600. Solo il wrapper root crea snapshot e mount; avvia il client come borg con il minimo
accesso temporaneo in lettura, senza concedergli gestione ZFS o sudo.
Il backup giornaliero parte alle 04:30 con un ritardo casuale fino a 30 minuti. Crea uno snapshot ZFS
ricorsivo temporaneo e ricostruisce tutti i dataset sotto /zpool in un albero di bind mount in sola
lettura, per inserirli in un unico archivio coerente. Il wrapper smonta ricorsivamente l'albero privato;
un helper ExecStopPost mirato rimuove eventuali mount dello snapshot nel namespace host e lo snapshot
temporaneo dopo l'uscita del processo. Borg conserva 30 archivi giornalieri, 8 settimanali e 12
mensili, poi compatta il repository. Il controllo completo di metadati e repository si svolge il 15
di ogni mese alle 06:00. Le operazioni usano un lock comune, journal e retry systemd limitati. Le
nuove esecuzioni riportano al massimo una riga di avanzamento al minuto: percentuale stimata,
dataset, file elaborati e byte originali/compressi/deduplicati. Il denominatore è la somma dei
logicalreferenced ZFS dello snapshot, non un totale Borg: può superare il 100% e non comprende
retention, compattazione o controlli. Le righe di progresso non riportano i nomi dei file; eventuali
warning possono farlo. Seguire il job con sudo journalctl -fu atlas-borg-backup.service; modifiche
all'helper non cambiano un'esecuzione già avviata.
Attivazione iniziale esplicita:
- Inserire una passphrase unica in
secrets/vault.ymlconansible-vault edit. - Generare e mostrare solo la chiave pubblica con
ansible-playbook ansible/site.yml --limit atlas --tags borg_key. - Installarla nel sub-account Hetzner, poi applicare con
ansible-playbook ansible/site.yml --limit atlas --tags packages,borg. - Copiare
secrets/recovery/atlas-borg-repokey.exportsu un supporto davvero offline: la copia locale ignorata da Git non è di per sé un backup offline.
Il ruolo inizializza solo un repository repokey assente, non accetta password SSH né host key non
fissate e non avvia manualmente il primo backup. Validazione:
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags packages,borg --check --diff
L'attivazione iniziale è riuscita: backup e controllo del repository, restore completo in una
directory temporanea confrontato con l'albero Archive, esportazione offline della chiave di recupero
e pulizia di snapshot/mount temporanei. Il 2026-09-25 un test separato da snapshot ZFS giornaliero ha
copiato un file di /zpool/archive in /var/tmp, verificando contenuto, proprietario, modalità,
mtime e ACL POSIX; copia e mount temporanei sono stati rimossi senza interrompere Borg. Non è un test
di ripristino dell'intero dataset.
Il backup USB offline è distribuito come servizio solo manuale (atlas_manage_usb_backup: true):
Ansible non formatta, sblocca, monta né avvia automaticamente il disco. Il disco esistente è stato
verificato in sola lettura il 2026-09-23: UUID LUKS 577b3c43-ea37-4611-81a9-39d555cdfbd4,
UUID ext4 interno 758e2d2e-a427-4797-aad9-39c3a9f17c7e, mapper zpool-backup. All'ispezione
era montato in /mnt/zpool-backup; il servizio richiede invece che il mapper non sia montato prima
dell'avvio. Se serve, systemd-ask-password chiede interattivamente la passphrase LUKS tramite
l'agente di systemctl start e la passa direttamente a cryptsetup, senza salvarla, esporla negli
argomenti o memorizzarla nella cache. Lo script monta il disco privatamente, crea uno snapshot ZFS
ricorsivo, copia tutti i dataset in atlas/snapshots/<timestamp>/ con rsync --link-dest, verifica
con un dry-run basato sui checksum, aggiorna atomicamente atlas/latest, smonta e chiude LUKS. Un
errore non sostituisce latest né cancella versioni complete precedenti. Borg e USB possono operare
contemporaneamente su snapshot distinti, ma la lettura concorrente può ridurre il throughput.
La copia USB conserva le ACL ma non gli attributi estesi generici, compreso security.selinux: la
policy della destinazione deve ricreare le etichette dopo un restore. Per un percorso esplicito:
ansible-playbook ansible/site.yml --limit atlas --tags restorecon \
-e '{"atlas_restorecon_paths":["/zpool/archive"]}'
Il task accetta solo percorsi sotto la radice del pool Atlas, esegue restorecon -RFv solo su quelli
indicati ed è altrimenti inattivo; non va lanciato sull'intero pool durante i run ordinari. Le vecchie
versioni USB non vengono eliminate automaticamente senza una retention deliberata. Il controllo di
capacità include il trasferimento stimato e una riserva libera di 10 GiB. Dopo un backup riuscito,
scollegare fisicamente il disco per renderlo davvero offline.
Validare la configurazione senza avviare il backup e, separatamente, un eventuale relabel pianificato:
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags usb_backup,usb_reminder --check --diff
ansible-playbook ansible/site.yml --limit atlas --tags restorecon --check \
-e '{"atlas_restorecon_paths":["/zpool/archive"]}'
Prima dell'avvio manuale smontare in sicurezza /mnt/zpool-backup, se ancora montato. Con il mapper
chiuso, sudo systemctl start atlas-usb-backup.service chiede la passphrase e avvia il backup; né la
password LUKS né un keyfile vanno in Ansible. Seguire con
sudo journalctl -fu atlas-usb-backup.service. Non esiste un timer di backup USB. Soltanto
atlas-usb-reminder.timer è schedulato il primo sabato del mese alle 10:00 Europe/Rome: invia un
promemoria al notifier 45Drives Houston, senza avviare il backup. Un test manuale ha prodotto una
notifica in 45Drives Alerts, non un'email; il log conferma l'invio della notifica, non la consegna
di posta. Il primo evento pianificato era il 2026-10-03 alle 10:00 CEST. Controllare timer e risultato
con systemctl list-timers atlas-usb-reminder.timer e in 45Drives Alerts.
Il primo tentativo USB del 2026-09-23 fallì su security.selinux e, dopo l'interruzione, lasciò
snapshot e mapper aperti. Applicato il filtro rsync, furono rimossi lo snapshot fallito, il mapper
smontato e lo stato failed; non rimase una copia valida di quel tentativo. Un run del 2026-09-24
pubblicò una versione verificata ma fallì nella distruzione dello snapshot a causa di mount
.zfs/snapshot aperti nel namespace host. Dopo la pulizia non forzata, è stato aggiunto un helper
ExecStopPost mirato e testato con uno snapshot usa-e-getta. Un run successivo del 2026-09-24 ha
verificato i checksum, pubblicato la versione ed è terminato con successo: mapper chiuso, nessuno
snapshot USB temporaneo e pool sano. Il 2026-09-25 un test di restore indipendente ha aperto il disco
in sola lettura, montato ext4 con ro,noload, copiato un file di 5.707.945 byte da atlas/latest in
una directory vuota sotto /var/tmp e confrontato contenuto, proprietario, modalità, dimensione,
mtime e ACL POSIX. Il test ha rimosso copia e mount temporanei, chiuso LUKS e lasciato il pool sano
mentre Borg continuava. È un test su file, non un esercizio completo di disaster recovery.
Il monitoraggio Atlas è eseguito ogni 30 minuti da atlas-health-monitor.timer. Sonde in sola
lettura controllano stato/errori del pool e dei vdev, scrub/resilver, SMART dei quattro dischi del
pool e dell'NVMe di sistema, temperature dei dischi e CPU, spazio di sistema/pool/snapshot, crescita
di zpool/backup e quota Hetzner tramite df -m via SSH con l'account borg e la chiave fissata.
La query remota non apre il repository Borg né il suo lock. Gli alert di crescita richiedono una
baseline di circa 24 ore. Sono controllati anche attivazione e freschezza dei timer; hook systemd
OnFailure segnalano errori di snapshot, scrub, Borg, USB, promemoria e monitoraggio. Il monitor non
riavvia Borg; avvisa solo se un run supera 14 giorni. Soglie e percorsi stabili dei dischi sono nelle
variabili host. Gli avvisi usano 45Drives Houston con deduplicazione; la consegna email non è stata
verificata. Il controllo live del 2026-09-25 non ha trovato problemi; la notifica di prova è stata
inviata e lo Storage Box risultava occupato al 22%. Dimensione dell'archivio Borg e deduplicazione
dettagliata richiedono ancora la fine del backup in corso.
ansible-playbook ansible/site.yml --limit atlas --tags monitoring --check --diff
sudo /usr/local/libexec/atlas-health-monitor --dry-run
sudo journalctl -u atlas-health-monitor.service -n 100 --no-pager
systemctl list-timers atlas-health-monitor.timer
--dry-run non invia alert e non modifica lo stato del monitor. Un controllo reale si avvia con
sudo systemctl start atlas-health-monitor.service, senza avviare servizi di backup. Per una prova
etichettata di 45Drives Alerts usare
sudo /usr/local/libexec/atlas-health-monitor --test-notification.
Timer systemd di Atlas
Tutti i nove timer gestiti sono abilitati. Gli orari sono locali ad Atlas (Europe/Rome); Borg e
monitoraggio aggiungono il ritardo casuale indicato. Tutti hanno Persistent=true: un evento perso
viene recuperato quando il timer torna attivo.
| Timer | Pianificazione (OnCalendar) |
Azione |
|---|---|---|
atlas-zfs-snapshot-hourly.timer |
*-*-* *:05:00 — ogni ora al minuto 05 |
Snapshot ricorsivo orario e retention |
atlas-zfs-snapshot-daily.timer |
*-*-* 00:15:00 — ogni giorno alle 00:15 |
Snapshot ricorsivo giornaliero e retention |
atlas-zfs-snapshot-weekly.timer |
Sun *-*-* 01:00:00 — domenica alle 01:00 |
Snapshot ricorsivo settimanale e retention |
atlas-zfs-snapshot-monthly.timer |
*-*-01 02:00:00 — primo giorno del mese alle 02:00 |
Snapshot ricorsivo mensile e retention |
zfs-scrub-monthly@zpool.timer |
Sun *-*-01..07 03:00:00 — prima domenica alle 03:00 |
Scrub ZFS |
atlas-borg-backup.timer |
*-*-* 04:30:00 — ogni giorno alle 04:30, più 0–30 min casuali |
Backup cifrato offsite |
atlas-borg-check.timer |
*-*-15 06:00:00 — giorno 15 alle 06:00, più 0–30 min casuali |
Controllo repository Borg |
atlas-usb-reminder.timer |
Sat *-*-01..07 10:00:00 Europe/Rome — primo sabato alle 10:00 |
Solo promemoria 45Drives Alerts |
atlas-health-monitor.timer |
*:0/30 — ogni mezz'ora, più 0–5 min casuali |
Controlli di salute in sola lettura |
atlas-usb-backup.service non ha timer e va avviato manualmente. Il timer del fornitore
zfs-scrub-weekly@zpool.timer è disabilitato a favore dello scrub mensile. Il futuro pull del backup
Prometheus non ha ancora un timer, perché non è implementato. Durante un backup Borg attivo,
systemctl list-timers può mostrare - per il prossimo evento senza che il timer sia disabilitato.
Per vedere la pianificazione corrente: systemctl list-timers --all su Atlas.
Nextcloud è previsto come servizio temporaneo su Atlas prima di Uranus, ma solo dopo la validazione della protezione dei dati: richiede storage applicativo, database e cache separati, segreti Vault, pubblicazione solo tramite NPM e Aegis, procedure di backup, aggiornamento e migrazione. Non distribuirlo prima di completare la checklist di protezione dei dati.
La destinazione futura per l'importazione foto iCloud è Atlas, non Aegis. Dopo la validazione dei
backup, pianificare una migrazione esplicita di iCloudPD con foto sotto /zpool/archive/Pictures e
stato applicativo/MFA fuori da Archive; testare permessi, SELinux, backup e restore prima del
cutover. L'attuale iCloudPD su Aegis e l'export NFS Photobook restano configurati fino
all'approvazione e alla verifica di questa migrazione separata. Anche il servizio Atlas sarà
temporaneo in attesa di Uranus.
Il pull dei backup di Prometheus, la valutazione delle dimensioni degli archivi Borg e i test completi
di disaster recovery restano da fare. Il backlog prioritizzato è in AGENTS.md.
Composizione della configurazione
Emacs è abilitato sui profili Fedora/GNOME e workstation; la configurazione canonica è distribuita da dotfiles_common, con Org in ~/Org/, template versionati e export PDF/HTML/Markdown/DOCX/ODT. Per abilitarlo temporaneamente su un altro profilo:
ansible-playbook ansible/site.yml --limit <host> --tags emacs -e emacs_enabled=true
La configurazione finale di una macchina è ottenuta combinando più livelli.
common configuration
+ platform configuration
+ role configuration
+ desktop configuration
+ host overrides
Esempi correnti:
ikaros -> common + platform_fedora + role_personal_workstation + graphical_desktop + desktop_gnome + ikaros
nymph -> common + platform_fedora + graphical_desktop + desktop_gnome + nymph
deadalus -> common + platform_fedora + workstation_dev_fedora + workstation_dev_wsl + deadalus
Questo approccio consente di:
- mantenere configurazioni condivise
- applicare override specifici per host
- evitare duplicazioni
- riutilizzare il profilo Void corrente su un host futuro assegnandolo a
platform_void + graphical_desktop + desktop_sway
Ruoli Ansible
I principali ruoli attualmente presenti sono:
| Role | Descrizione |
|---|---|
| base | configurazione base comune |
| packages_void | installazione pacchetti su Void |
| packages_fedora | installazione pacchetti su Fedora |
| packages_rocky | installazione pacchetti su Rocky Linux 9 |
| services_runit | gestione servizi runit |
| services_systemd | gestione servizi systemd |
| profile_desktop_common | bootstrap desktop Void condiviso |
| profile_desktop_gnome | dotfiles desktop condivisi per Fedora/GNOME |
| profile_desktop_sway | sessione desktop sway / SwayFX (Wayland) |
| profile_desktop_niri | sessione desktop Niri su Void (Wayland) |
| profile_desktop_host | override desktop specifici per host |
| profile_personal_workstation | layer stabile per workstation personale |
| profile_workstation_dev_common | configurazione dev workstation condivisa |
| profile_workstation_dev_wsl | configurazione WSL condivisa per sviluppo |
| profile_server | configurazione server |
| profile_atlas | configurazione NAS Rocky Linux 9 |
| profile_backend_phase1 | Navidrome e Syncthing rootless su Atlas |
| wireguard_overlay | overlay WireGuard Prometheus/Atlas |
| dotfiles_common | distribuzione dotfiles comuni |
| dotfiles | distribuzione configurazioni utente |
Stato attuale del playbook principale
Il playbook ansible/site.yml e attualmente composto da blocchi per asse:
all -> dotfiles_common
platform_void -> packages_void + services_runit
platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host
platform_fedora -> packages_fedora + services_systemd
platform_rocky -> packages_rocky + services_systemd
wireguard_overlay -> wireguard_overlay (dopo platform_rocky)
atlas -> profile_atlas
role_backend_phase1 -> profile_backend_phase1 (dopo atlas)
platform_fedora & role_personal_workstation -> profile_personal_workstation
platform_fedora & desktop_gnome -> profile_desktop_gnome
workstation_dev_fedora -> profile_workstation_dev_common
workstation_dev_wsl -> profile_workstation_dev_wsl (dopo platform_fedora + workstation_dev_fedora)
rocky_server -> dotfiles_common + profile_server (dopo platform_rocky)
Questo significa che, allo stato attuale:
ikarosriceve Fedora Workstation/GNOME come target desktop personale stabilenymphriceve Fedora Workstation/GNOME come target laptop- il profilo Void resta selezionabile tramite
platform_void + graphical_desktopper host futuri deadalusriceve il profilo Fedora WSL tramite play dev dedicati- il server Rocky (
prometheus) e gestito con pacchetti, servizi, dotfiles server e firewalld - il NAS Rocky (
atlas) usa un pool ZFS gia esistente, condivisioni NFSv4/SMB limitate alla LAN e Cockpit/45Drives - lo stack Compose server include soltanto
giteaenginx-proxy-manager; Navidrome e Syncthing della fase 1 sono Quadlet rootless su Atlas
Dotfiles
La directory dotfiles/ contiene le configurazioni utente versionate.
dotfiles/
├── common
├── desktop
├── server
├── fedora
├── workstation
├── workstation_dev_wsl
└── nymph
Le configurazioni sono applicate tramite Ansible e organizzate per livelli:
| Livello | Scopo |
|---|---|
| common | configurazioni condivise |
| profile | configurazioni per tipo macchina |
| host | override specifici |
Requisiti
Per utilizzare il repository sono necessari:
- Python 3
- Ansible
ansible-lintyamllintshellcheck- collection definite in
ansible/collections/requirements.yml - accesso locale o SSH alle macchine target, in base a come e definito l'inventory
Installazione base:
python3 -m pip install ansible ansible-lint yamllint shellcheck-py
ansible-galaxy collection install -r ansible/collections/requirements.yml
Gestione segreti:
- il repository supporta il caricamento opzionale di
secrets/vault.yml - il repository supporta anche
secrets/vault.local.ymlper override locali non versionati secrets/vault.yml.examplefunge da template/esempio- se
secrets/vault.ymlnon e presente, il playbook continua comunque senza caricare variabili locali opzionali - se
secrets/.vault_pass.gpgesiste viene usato automaticamente per sbloccare i vault tramitegpg; in alternativa resta supportatosecrets/.vault_passcome fallback legacy locale; se nessuno dei due file esiste Ansible richiede la password in modo interattivo
Utilizzo
Eseguire il playbook principale:
ansible-playbook ansible/site.yml
Allo stato attuale questo comando:
- distribuisce i dotfiles comuni a tutti gli host
- per
platform_voidapplica pacchetti Void e servizi runit - per
platform_void + graphical_desktopapplica bootstrap desktop condiviso, sessioni Sway/Niri e override specifici per host - per
platform_fedoraapplica pacchetti Fedora e servizi systemd aikaros,nymphedeadalus - per
platform_fedora & role_personal_workstationapplica il layer personale aikaros - per
platform_fedora & desktop_gnomeapplica il profilo GNOME aikarosenymph - per
workstation_dev_wslapplica i tweak WSL dopo il layer Fedora adeadalus, escludendo Flatpak e Snap - per
platform_rockyapplica pacchetti Rocky e servizi systemd adatlaseprometheus; quindi applica il profilo NAS adatlase il profilo server aprometheus - non riavvia automaticamente il display manager
- carica
secrets/vault.ymlsolo se presente - carica
secrets/vault.local.ymlsolo se presente, dopovault.yml, cosi gli override locali hanno precedenza
Per validare prima di applicare:
ansible-playbook ansible/site.yml --syntax-check
ansible-playbook ansible/site.yml --limit ikaros,nymph --check --diff
ansible-playbook ansible/site.yml --limit ikaros --check --diff
ansible-playbook ansible/site.yml --limit nymph --check --diff
ansible-playbook ansible/site.yml --limit deadalus --check --diff
ansible-playbook ansible/site.yml --limit prometheus --check --diff
ansible-playbook ansible/site.yml --limit atlas --check --diff
ansible-lint ansible/site.yml
ansible-lint ansible/roles
yamllint ansible/
Per testare un override dell'utente server senza modificare l'inventory:
ansible-playbook ansible/site.yml --limit prometheus --check --diff -e server_username=myuser
Per validazioni piu mirate:
ansible-playbook ansible/site.yml --limit <host> --tags <tag1>,<tag2> --check --diff
ansible-playbook ansible/site.yml --limit <host> --start-at-task "<task name>" --check --diff
ansible-lint ansible/roles/<role>
yamllint ansible/path/to/file.yml
podman-compose -f /opt/docker/server/docker-compose.yml config
ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
Tag supportati dal playbook
Per vedere l'elenco reale aggiornato dei tag disponibili:
ansible-playbook ansible/site.yml --list-tags
Allo stato attuale ansible/site.yml espone questi tag:
| Tag | Scopo | Ambito principale |
|---|---|---|
always |
pre-task sempre eseguiti, inclusi caricamento vault e validazioni preliminari | common |
ai_agents |
installazione agenti AI condivisi | Fedora, WSL |
atlas |
account, storage, condivisioni e container Atlas | NAS Atlas |
backend_phase1 |
Quadlet rootless Navidrome e Syncthing | NAS Atlas |
containers |
Quadlet rootful Atlas | NAS Atlas |
dotfiles |
distribuzione/configurazione dotfiles | tutti i profili |
dotfiles:common |
dotfiles comuni condivisi | common, workstation, server |
dotfiles:desktop |
dotfiles desktop | desktop Void, Fedora/GNOME |
dotfiles:host |
override host-specifici desktop | desktop Void |
dotfiles:server |
dotfiles dedicati al profilo server | server |
dotfiles:workstation |
dotfiles dedicati alle workstation | personal workstation, WSL |
emptty |
gestione display manager emptty |
desktop Void |
display-manager |
gestione del display manager emptty |
desktop Void |
emacs |
configurazione Emacs condivisa e dipendenze di authoring | desktop Fedora/GNOME e workstation |
fonts |
installazione font | Fedora |
fzf |
configurazione FZF | dotfiles comuni |
git |
configurazione Git e GPG desktop | Fedora/GNOME, desktop Void |
gnome |
configurazione host GNOME | Fedora/GNOME desktop |
immich |
account e Quadlet Immich | NAS Atlas |
sway |
sessione/configurazione sway / SwayFX (Wayland) | desktop Void |
niri |
sessione/configurazione Niri (Wayland) | desktop Void |
npm |
installazione pacchetti npm globali | Fedora/GNOME, desktop Void, WSL |
nvidia |
componenti NVIDIA desktop | desktop Void |
packages |
installazione e aggiornamento pacchetti | tutti i profili |
podman |
integrazione Podman Compose e Quadlet rootless | server |
portal |
configurazione xdg-desktop-portal | desktop Void |
services |
gestione servizi runit/systemd | tutti i profili |
sharing |
condivisioni NFSv4 e SMB3 | NAS Atlas |
storage |
dataset ZFS figli | NAS Atlas |
theme |
configurazione del tema GTK/Qt | desktop Void |
tmux |
configurazione e plugin tmux | desktop Fedora/Void, WSL |
vim |
configurazione Vim | dotfiles comuni |
wireguard |
overlay WireGuard Prometheus/Atlas | Prometheus, NAS Atlas |
wsl |
bootstrap e configurazione WSL | WSL |
Esempi pratici:
ansible-playbook ansible/site.yml --limit nymph --tags dotfiles:desktop,gnome --check --diff
ansible-playbook ansible/site.yml --limit ikaros --tags gnome --check --diff
ansible-playbook ansible/site.yml --limit prometheus --tags services,dotfiles:server --check --diff
Bootstrap di una nuova macchina
Una nuova macchina può essere inizializzata con i seguenti passaggi:
git clone <repo>
cd <repo-dir>
ansible-galaxy collection install -r ansible/collections/requirements.yml
ansible-playbook ansible/site.yml
Dopo l'esecuzione del playbook la macchina verra configurata secondo il profilo definito e i ruoli attualmente orchestrati.
Per aggiungere un nuovo host Void che riusa il profilo desktop preservato:
- aggiungere l'host a
platform_void; - aggiungerlo a
graphical_desktop; - usare Sway, oppure aggiungerlo a
desktop_niriper selezionare Niri; - lasciare eventuali dettagli hardware in
host_vars/<host>.yml.
I gruppi legacy void e desktop sono parent di compatibilita, quindi un host
in platform_void e graphical_desktop continua a ricevere anche le variabili
Void e desktop esistenti.
Per il flusso mail desktop esiste inoltre uno script dedicato:
scripts/bootstrap_mail.sh
Lo script si occupa del bootstrap dei secret nel keyring, del primo sync con mbsync e dell'inizializzazione di mu usando la configurazione mail generata dai template.
Se modifichi questo script, valida almeno con:
sh -n scripts/bootstrap_mail.sh
shellcheck scripts/bootstrap_mail.sh
Filosofia del progetto
Il repository segue alcuni principi chiave:
- Infrastructure as Code
- configurazione dichiarativa
- idempotenza
- ambienti riproducibili
- separazione tra configurazione sistema e configurazione utente
Questo consente di ricreare qualsiasi macchina partendo esclusivamente dal repository.
Roadmap
Possibili evoluzioni future:
- hardening sicurezza server
- configurazione backup
- testing automatico playbook
- integrazione CI
- supporto ad altre distribuzioni Linux
Licenza
Questo progetto è distribuito sotto licenza LGPL-3.0.