# Avviare il server Presuppone che [setup.md](setup.md) sia già stato completato (`.env` pronto, master key generata). Le migrazioni del database non si applicano a mano: le esegue il container a ogni avvio, prima di far partire il server. Il server gira sempre via Docker, in sviluppo e in produzione allo stesso modo — non esiste un modo supportato per lanciare `uvicorn` direttamente. Il venv locale (`.venv/`) serve solo per i test, per scrivere le migrazioni Alembic e per gli script una tantum di generazione chiavi (vedi [setup.md](setup.md) e la sezione "Commands" di [CLAUDE.md](../CLAUDE.md#commands)). ## Docker + Caddy (unico workflow supportato) ```bash mkdir -p data/db data/keys data/logs # una tantum, se non già presenti docker compose up -d --build ``` - Caddy fa da reverse proxy davanti all'app e gestisce il TLS automaticamente - App su `https://localhost/` (o sul dominio configurato, vedi sotto) - Pannello admin su `https://localhost/admin` - DB, master key cifrata e log persistono in `./data/` sulla root del repo (bind mount, non volumi Docker opachi) — sopravvivono a stop/rebuild del container e sono ispezionabili/backup-abili direttamente ### Modalità dev, senza dominio (certificato self-signed) Non serve fare nulla: lasciando `SITE_ADDRESS` non impostata, Caddy usa `localhost` di default. Rilevando che non è un hostname pubblico, genera da solo un certificato dalla sua CA interna — il browser mostrerà un avviso di sicurezza al primo accesso (normale, accettalo o usa `curl -k`). ### Modalità produzione, con dominio reale Scrivere il dominio in `.env`, accanto agli altri valori: ```bash # .env SITE_ADDRESS=lottery.tuodominio.it ``` e avviare come sempre: ```bash docker compose up -d --build ``` Docker Compose legge `.env` da solo per sostituire `${SITE_ADDRESS}` in `docker-compose.yml`, quindi ogni avvio successivo usa lo stesso dominio. **Non** passarlo solo sulla riga di comando (`SITE_ADDRESS=… docker compose up -d`): il valore vale per quel comando e basta, e il primo `docker compose up -d --build` lanciato senza di esso riporta Caddy a `localhost` con un certificato autofirmato — il sito pubblico smette di rispondere con un certificato valido. Per controllare: `grep '^SITE_ADDRESS=' .env`. Il DNS del dominio deve già puntare all'IP del server, con le porte 80 e 443 raggiungibili da internet. Caddy richiede e rinnova automaticamente un certificato Let's Encrypt reale — nessuna configurazione aggiuntiva. ### Comandi utili ```bash tail -f data/logs/app.log # log dell'applicazione (scheduler, Electrum, riconciliatori, errori) docker compose logs -f app # solo output di avvio del container e di uvicorn (vedi sotto) docker compose ps # stato dei container docker compose stop # ferma senza rimuovere i container docker compose down # ferma e rimuove i container (i dati in ./data/ restano) ``` **Dove sono i log.** I messaggi dell'applicazione — connessione Electrum, scheduler dei round, riconciliatori, errori delle richieste — sono scritti **solo** in `./data/logs/app.log` (con rotazione: `app.log.1` … `app.log.5`), non sull'output del container. `docker compose logs -f app` mostra soltanto l'output di avvio del container (controllo della master key, migrazioni Alembic) e ciò che uvicorn scrive per conto suo (avvio, richieste HTTP): per capire cosa sta facendo la lotteria si legge il file. Dettagli e messaggi da conoscere in [audit-log.md](audit-log.md#log-applicativo). > **Nota sul `Caddyfile`**: è montato in sola lettura nel container `caddy` > (bind mount). Modificarlo non basta a farlo ripartire con la nuova > configurazione — `docker compose up -d --build` non ricrea `caddy` solo > perché il *contenuto* di un file montato è cambiato. Dopo una modifica al > `Caddyfile` serve un passaggio in più: > ```bash > docker compose restart caddy > ``` > (oppure, senza interrompere le connessioni esistenti: `docker compose exec > caddy caddy reload --config /etc/caddy/Caddyfile`). ### Riavvii e deploy: prima la pausa Oggi l'arresto del container non è pulito: con una pagina `/` aperta (uno stream SSE attivo) uvicorn aspetta che lo stream si chiuda e Docker uccide il processo dopo 10 secondi. Per il codice ogni `docker compose up -d --build`, `restart` o `down` è quindi un crash: durante `drawing` blocca il round, durante un invio lascia una transazione `building` da riconciliare. Prima di **qualunque** riavvio o deploy (nuova versione, modifica di `.env`, rotazione di un segreto): 1. fare il backup del database; 2. in `/admin` → Parametri → Manutenzione, premere "Interrompi dopo questo round"; 3. aspettare che il round in corso arrivi a `closed` (`GET /rounds/current` senza più un round attivo) e che "Transazioni pendenti" non abbia righe `building`; 4. riavviare, controllare in `data/logs/app.log` la riga `Electrum connected to …`, poi "Riprendi lotteria". La procedura completa, con i comandi e il perché di ogni passo, è [OPS-10 in operativita.md](operativita.md#riavvio) ("Riavvio sicuro e deploy"). ### ⚠️ Attenzione: riavvii automatici a metà round `docker-compose.yml` imposta `restart: unless-stopped` sul container dell'app: se crasha, riparte da solo. Questo però non è ancora sicuro in ogni caso: i round in `closing` e `paying_out` riprendono da soli al riavvio, ma se il crash avviene mentre un round è in `drawing`, lo scheduler non lo riprende e il round resta bloccato (limite noto, vedi [limiti-noti.md](limiti-noti.md); procedura in [operativita.md](operativita.md)). Non trattare questo setup come "non supervisionato" finché quel limite non è risolto.