Un server solo, e il lavoro che ti porta
Un servizio cloud gestito ti toglie il lavoro di amministratore e ti presenta il conto; un server virtuale (VPS) costa poco, ma l'amministratore sei tu. Per un prodotto indie con pochi clienti, un VPS è spesso la scelta giusta, a patto di accettare tre compiti: tenerlo aggiornato, fare i backup e provarne il ripristino. Questo articolo mostra un'architettura minima con nginx, applicazione e database, e due strumenti eseguibili: un controllo di rotazione dei backup e le unità systemd che li programmano. Sul prezzo: il listino Hetzner Cloud consultato il 2026-11-13 indica piani «a partire da» 5,49 euro al mese per risorse condivise di prestazioni normali, 5,99 euro per il piano ottimizzato nei costi e 36,99 euro per risorse dedicate, tutti IVA esclusa.
Una precisazione sul titolo della serie: non ho confrontato fornitori italiani e non riporto prezzi di operatori che non ho consultato. L'esempio usa un fornitore europeo con listino pubblico perché è quello che ho potuto verificare; il metodo vale per qualunque altro.
Che cosa guardare quando scegli il fornitore
Il prezzo mensile è la voce meno informativa. Prima di scegliere un fornitore verifica, sulla sua pagina, i punti che seguono; ciascuno ha un motivo preciso.
- Dove sono i dati. Per i dati personali dei clienti conta il Paese del centro dati, perché il regolamento (UE) 2016/679 regola i trasferimenti verso Paesi terzi.
- Prezzo con e senza IVA. I listini B2B sono spesso IVA esclusa, e se non puoi detrarre l'imposta sugli acquisti quella parte è un costo tuo.
- Backup del fornitore. Se esistono, se sono a pagamento e dove stanno: non sostituiscono i tuoi, ma sono una seconda rete.
- Tipo di risorse. «Condivise» significa che la CPU è divisa con altri clienti; «dedicate» costa molto di più, come mostra il listino.
La lista nasce da una sola domanda: che cosa succede se questo server sparisce domani? Dove sono i dati risponde alla parte legale, i backup alla parte tecnica, le risorse a quella di prestazioni. L'IVA riguarda solo il bilancio e, per chi è nel regime forfettario, è un costo secco: la legge 190/2014, comma 58, esclude la detrazione per le operazioni che elenca, e per il resto delle tue spese chiedi al commercialista. Nel confronto fra piani conviene cominciare da quello più piccolo che regge il traffico stimato, sapendo che passare a uno più grande è un'operazione di routine, mentre l'opposto è più scomodo.
L'architettura minima
Il disegno è lo stesso del primo articolo: nginx accetta le connessioni dall'esterno, l'applicazione ascolta solo sull'interfaccia locale e il database accetta solo l'applicazione. Così l'unica porta aperta è quella del web server, e l'applicazione non è raggiungibile direttamente da Internet. Un firewall completa il quadro: l'autore usa ufw e fail2ban sul proprio server, ma la regola generale è semplice, chiudi tutto e apri solo la porta del web e quella di amministrazione.
Il blocco seguente è la configurazione nginx più piccola che serva: un reverse proxy verso l'applicazione. È uno schema da adattare: nginx non è installato sulla macchina in cui ho scritto l'articolo, quindi la configurazione non è stata caricata né provata, e www.example.com è un nome riservato per gli esempi. Prima di usarla esegui nginx -t sul tuo server, che controlla la sintassi senza ricaricare.
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://127.0.0.1:8080; # l'applicazione ascolta solo in locale
proxy_set_header Host $host; # senza, l'app riceve l'host di proxy_pass
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Output:
# schema da adattare: nginx non e' installato su questa macchina, la configurazione non e' stata caricata ne' provata
proxy_pass indica dove inoltrare. proxy_set_header Host $host ripristina l'host originale: senza, secondo la documentazione di nginx, il valore predefinito è l'host indicato in proxy_pass, e un'applicazione che costruisce indirizzi assoluti (per esempio nelle email) scriverebbe quello sbagliato. X-Forwarded-For e X-Forwarded-Proto dicono all'applicazione l'indirizzo del cliente e se la connessione era cifrata. La porta 80 qui serve solo come punto di partenza: il certificato HTTPS si ottiene con uno strumento come Certbot, il cui sito indica le istruzioni per nginx, e va fatto prima di pubblicare l'indirizzo.
Backup che puoi ripristinare
Un backup che non hai mai ripristinato non è un backup. Il comando di PostgreSQL per esportare un database è pg_dump; con --format=custom produce un archivio compresso che si ripristina con pg_restore, e il formato custom è descritto nella pagina di riferimento della versione 16 come uno dei più flessibili, compresso per impostazione predefinita. Le opzioni --format, --file e --dbname compaiono anche nell'aiuto del pg_dump 17.10 installato sulla macchina di lavoro. Non l'ho eseguito contro nessun database, perché la regola di questo lavoro vieta di scrivere su database locali.
Il programma di rotazione che segue esegue invece la parte di logica che puoi provare da sola. La funzione ruota ordina i file per nome, e siccome il nome contiene la data in formato ISO l'ordine alfabetico coincide con quello cronologico; tiene gli ultimi tieni file e cancella gli altri. La prova gira in una cartella temporanea con dieci file finti e un file di testo che non deve essere toccato. Il caso tieni=0 non cancella nulla: è una protezione contro la configurazione sbagliata che svuoterebbe la cartella.
# Rotazione dei backup: tieni gli ultimi N file, cancella i piu' vecchi.
# Prova in una cartella temporanea con file finti: non tocca nessun database.
import tempfile
from pathlib import Path
def ruota(cartella: Path, tieni: int):
"""Cancella i backup piu' vecchi. Il nome contiene la data ISO, quindi l'ordine
alfabetico coincide con l'ordine cronologico. Restituisce i file cancellati."""
backup = sorted(cartella.glob("db-*.dump"))
da_cancellare = backup[:-tieni] if tieni > 0 and len(backup) > tieni else []
for f in da_cancellare:
f.unlink()
return [f.name for f in da_cancellare]
with tempfile.TemporaryDirectory() as tmp:
cartella = Path(tmp)
for giorno in range(1, 11):
(cartella / f"db-2026-11-{giorno:02d}.dump").write_bytes(b"finto")
(cartella / "note.txt").write_text("non e' un backup") # non deve essere toccato
cancellati = ruota(cartella, tieni=7)
print("cancellati:", cancellati)
print("rimasti:", [f.name for f in sorted(cartella.iterdir())])
print("tieni=0 non cancella nulla:", ruota(cartella, tieni=0))
# Output:
# cancellati: ['db-2026-11-01.dump', 'db-2026-11-02.dump', 'db-2026-11-03.dump']
# rimasti: ['db-2026-11-04.dump', 'db-2026-11-05.dump', 'db-2026-11-06.dump', 'db-2026-11-07.dump', 'db-2026-11-08.dump', 'db-2026-11-09.dump', 'db-2026-11-10.dump', 'note.txt']
# tieni=0 non cancella nulla: []
Il risultato conferma la logica: dieci file, ne restano sette, i tre più vecchi sono cancellati e il file di testo resta intatto. È deterministico e identico in due esecuzioni. Il limite è importante: il programma ruota file, non verifica che siano ripristinabili. La verifica vera è una procedura manuale che metti in agenda: una volta al mese ripristini l'ultimo backup su un'altra macchina o in un database di prova, e controlli che i dati ci siano.
Programmare il backup con systemd
Su Linux il pianificatore naturale è systemd: un'unità di tipo servizio che fa il lavoro e un'unità di tipo timer che la lancia. Le due unità seguenti sono state controllate con systemd-analyze verify, che legge i file senza avviare nulla; non le ho attivate, perché richiedono un database reale e una cartella di destinazione. Il percorso /var/backups/mio-saas e il nome mio_db sono da sostituire.
# /etc/systemd/system/backup-db.service
[Unit]
Description=Backup notturno del database
[Service]
Type=oneshot
User=postgres
# %% e' il modo di scrivere un % letterale in un file di unita'
ExecStart=/usr/bin/bash -c 'pg_dump --format=custom --file=/var/backups/mio-saas/db-$(date +%%F).dump mio_db'
# /etc/systemd/system/backup-db.timer
[Unit]
Description=Esegue il backup ogni notte
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
# Output:
# systemd-analyze verify backup-db.service backup-db.timer: nessun messaggio, exit 0
Type=oneshot dice a systemd che il servizio finisce da solo. OnCalendar lo fissa alle 03:30 di ogni giorno, e Persistent=true fa recuperare l'esecuzione saltata se il server era spento a quell'ora, come descrive la documentazione. RandomizedDelaySec=10min sposta a caso l'avvio entro dieci minuti, utile se più server dello stesso fornitore partono tutti alla stessa ora. Il nome del file contiene la data, che è il formato atteso dal programma di rotazione. Il backup resta comunque sullo stesso server: copialo fuori, altrimenti un guasto del disco porta via dati e copia insieme.
Quando scalare, e come
Il percorso economico è quello verticale: stesso server, piano più grande, qualche minuto di fermo. Un secondo server compare solo quando un singolo non basta, o quando l'indisponibilità costa più della complessità. Prima di scalare misura: un'unica soglia scritta in anticipo, per esempio «CPU oltre l'80% per una settimana», evita decisioni prese sull'onda del panico. Il divario di prezzo tra i piani condivisi e quello dedicato nel listino, da 5,49 a 36,99 euro, ti dà l'ordine di grandezza del salto.
Domande frequenti
Un VPS è sicuro per un prodotto con pagamenti?
Dipende dal tuo lavoro più che dal server. Con un checkout ospitato i dati della carta non passano dal tuo server, e questo riduce molto la superficie. Restano da fare aggiornamenti, firewall e chiavi SSH, che sono compiti tuoi.
Ogni quanto devo fare i backup?
Ogni volta che il dato perso farebbe male: per la maggior parte dei piccoli prodotti, una volta al giorno. L'esempio sopra tiene sette copie giornaliere, un numero che puoi cambiare in una riga.
Meglio un VPS o un servizio gestito?
Se non vuoi imparare ad amministrare un server, un servizio gestito è una scelta onesta anche se costa di più. Il VPS conviene quando il costo è una variabile e conosci i compiti che accetti.
Che cosa sai fare adesso
Sai valutare un fornitore con quattro domande e un listino, scrivere un reverse proxy nginx minimo, programmare un backup notturno con systemd e controllare con un programma la logica di rotazione. Sai anche che manca ancora la parte più importante, il ripristino provato. Quando i clienti arriveranno, il supporto ti mostrerà dove il servizio si rompe davvero; i rischi del lancio li trovi nella pre-mortem.
Il traffico che arriva dai motori di ricerca è l'argomento del marketing organico, e quanto traffico serva davvero lo puoi stimare con la simulazione dei casi di successo.
Fonti
- Hetzner Cloud, pagina dei piani - prezzi «a partire da» 5,49, 5,99 e 36,99 euro al mese IVA esclusa, consultata il 2026-11-13
- nginx, modulo ngx_http_proxy_module - proxy_set_header, valore predefinito di Host e $proxy_add_x_forwarded_for, consultata il 2026-11-13
- PostgreSQL 16, pg_dump - formato custom e opzioni di esportazione, consultata il 2026-11-13
- systemd.timer(5) - Persistent= e RandomizedDelaySec=, consultata il 2026-11-13
- Certbot, istruzioni - progetto della Electronic Frontier Foundation per ottenere certificati HTTPS, con istruzioni per nginx, consultata il 2026-11-13
- Legge 190/2014, articolo 1, comma 58 (Normattiva) - IVA sugli acquisti nel regime forfettario, consultata il 2026-11-13

Commenti
Caricamento commenti...
Accedi per commentare