Listmonk su VPS: installazione e configurazione
Installa Listmonk v6.2.0 su Ubuntu 24.04 con PostgreSQL, config.toml, systemd e TLS. Collega SMTP e valuta tempi e costi reali della deliverability.
Cosa serve a una newsletter self-hosted con Listmonk
Listmonk è un gestore self-hosted per newsletter e mailing list: un singolo binario Go, un database PostgreSQL, un file di configurazione e una unità systemd. Un piccolo VPS lo esegue senza difficoltà, perché Listmonk memorizza gli iscritti e mette in coda le campagne, ma non consegna direttamente i messaggi. Invia ogni messaggio a un server SMTP (simple mail transfer protocol), quindi il tasso di consegna dipende dalla reputazione di quel server e non da questo software.
Questa guida installa Listmonk v6.2.0, la release corrente a luglio 2026, su Ubuntu 24.04. Servono un VPS con un indirizzo IP pubblico, un nome di dominio sotto il tuo controllo e PostgreSQL 12 o versioni successive. L'installazione richiede circa un'ora. La reputazione dell'invio richiede settimane; questo aspetto viene trattato verso la fine.
Installare PostgreSQL e creare il database
Ubuntu 24.04 include PostgreSQL 16 nel proprio repository, una versione molto più recente di quella richiesta da Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlCreate il ruolo e il database in un'unica sessione psql. -v ON_ERROR_STOP=1 fa terminare psql al primo comando che restituisce un errore, evitando che un refuso lasci una configurazione parziale apparentemente completata.
sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL'
CREATE USER listmonk WITH PASSWORD 'pick-a-long-random-password';
CREATE DATABASE listmonk OWNER listmonk;
SQLOWNER listmonk è necessario. L'installazione dello schema crea tabelle, tipi, indici e funzioni, quindi il ruolo deve essere il proprietario del database. Se configurate Listmonk per usare un database di proprietà di un altro ruolo, l'installazione si interrompe con permission denied, anche dopo aver eseguito GRANT CONNECT.
Verificate che il database esista prima di continuare.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Il comando restituisce listmonk. Una riga vuota indica che il comando CREATE non è mai stato eseguito; controllate nuovamente l'output di psql.
Installare il binario Listmonk
Listmonk pubblica un binario statico per ogni architettura. Verifica prima l’architettura del sistema, perché un binario amd64 su un VPS ARM è un file che il kernel rifiuta di eseguire.
dpkg --print-architecture
cd /tmp
curl -fsSLO https://github.com/knadh/listmonk/releases/download/v6.2.0/listmonk_6.2.0_linux_amd64.tar.gz
tar -xzf listmonk_6.2.0_linux_amd64.tar.gz
sudo install -m 755 listmonk /usr/bin/listmonk
listmonk --versionSu un VPS ARM sostituisci amd64 con arm64 nel nome del file. listmonk --version che stampa una stringa con la versione è la prima conferma che il binario è compatibile con la macchina.
Generare config.toml e limitarne l'accesso
--new-config scrive config.toml nella directory di lavoro corrente. Per questo cd si trova all'interno di sh -c e non prima di sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'Il file generato è breve. In [app], address = "localhost:9000" associa il server HTTP solo all'interfaccia di loopback, quindi il pannello di amministrazione non è raggiungibile da Internet finché non si configura un reverse proxy davanti a esso. Lascia invariata quella riga. In [db] sono presenti host = "localhost", port = 5432, user = "listmonk", database = "listmonk" e ssl_mode = "disable". Questi valori predefiniti corrispondono già al database creato, quindi devi modificare soltanto la password.
ssl_mode = "disable" è corretto mentre Postgres è in ascolto sull'interfaccia di loopback dello stesso host, perché il traffico non lascia mai la macchina. Se sposti il database su un altro host, impostalo su require, altrimenti la password attraversa la rete in testo in chiaro.
Modifica la riga della password in [db] in modo che corrisponda al ruolo, quindi crea l'account di servizio e rimuovi l'accesso al file per tutti gli altri account.
sudo useradd --system --home-dir /var/lib/listmonk --create-home --shell /usr/sbin/nologin listmonk
sudo chown -R root:listmonk /etc/listmonk
sudo chmod 640 /etc/listmonk/config.tomlA questo punto l'account di servizio può leggere il file e nessun altro può farlo.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlIl primo comando stampa readable. Il secondo stampa root:listmonk 640. Qualsiasi altro account senza privilegi che provi lo stesso cat riceve Permission denied. Questo è lo scopo: il file contiene la password del database in testo in chiaro e, in genere, un server ha più di un account di accesso. Lo stesso principio si applica a ogni servizio eseguito, quindi consulta gli account di servizio con privilegi minimi e applicalo ovunque.
Creare lo schema con --install
--install crea le tabelle e inserisce le impostazioni predefinite. Impostare il primo accesso amministrativo tramite variabili d'ambiente, in modo che l'account esista prima che il pannello sia raggiungibile.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes risponde alla richiesta di conferma. Leggere una volta quella richiesta prima di automatizzarla, perché --install è l'installer iniziale e rimuove uno schema Listmonk esistente. Eseguirlo una seconda volta su un database in produzione elimina gli iscritti. In ogni script che potrebbe essere eseguito due volte, usare --install --idempotent --yes, che non esegue alcuna operazione quando le tabelle sono già presenti. Le modifiche allo schema incluse in una nuova release vengono applicate con --upgrade, mai con --install.
Verificare il risultato dal database, non dal browser.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"Il primo comando elenca le tabelle di Listmonk, tra cui subscribers, lists, campaigns, templates e bounces. Il secondo stampa admin. Un risultato vuoto del secondo comando indica che le variabili d'ambiente non sono mai arrivate al processo; di conseguenza, il pannello chiederà di creare il primo utente nel browser.
Eseguire Listmonk con systemd
Scrivete /etc/systemd/system/listmonk.service.
[Unit]
Description=Listmonk newsletter and mailing list manager
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=listmonk
Group=listmonk
WorkingDirectory=/var/lib/listmonk
ExecStart=/usr/bin/listmonk --config /etc/listmonk/config.toml
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetWorkingDirectory è importante perché Listmonk risolve rispetto a questa directory i percorsi relativi, incluso il percorso del filesystem per il caricamento dei file multimediali. After=postgresql.service stabilisce soltanto l’ordine di avvio e non attende che Postgres accetti le connessioni; Restart=on-failure gestisce quindi il caso in cui Listmonk si avvii leggermente in anticipo e non riesca a connettersi.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss dovrebbe mostrare 127.0.0.1:9000 nello stato LISTEN. curl che restituisce una qualsiasi riga di stato HTTP indica che il server sta rispondendo. Se curl restituisce Connection refused, il processo si è arrestato durante l’avvio; journalctl -u listmonk -n 50 --no-pager indica il motivo. Tenete presente che enable --now è la parte che garantisce la persistenza dopo un riavvio: un processo avviato manualmente termina al successivo aggiornamento del kernel.
Metti nginx e TLS davanti
Listmonk usa HTTP non cifrato sull'interfaccia loopback, quindi nginx termina TLS (transport layer security) e inoltra la richiesta.
server {
listen 443 ssl;
server_name lists.example.com;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}client_max_body_size deve essere aumentato perché le importazioni degli iscritti e i caricamenti dei file multimediali usano richieste POST con file, mentre nginx rifiuta per impostazione predefinita qualsiasi richiesta superiore a 1 MB con 413 Request Entity Too Large. Emetti il certificato con certbot, che scrive anche per te le righe listen 443 ssl e il redirect dalla porta 80: i passaggi sono descritti nella guida ai certificati Let's Encrypt per nginx. Apri le porte 80 e 443 e lascia chiusa la porta 9000, perché il proxy la raggiunge tramite loopback. Se il firewall non è ancora configurato, inizia dalle nozioni di base sul firewall ufw.
Apri quindi il pannello di amministrazione e imposta l'URL principale nella sezione Settings su https://lists.example.com. Una nuova installazione contiene http://localhost:9000 e Listmonk inserisce quel valore in ogni link per annullare l'iscrizione e in ogni URL multimediale incluso in un'email. Se invii una campagna prima di modificarlo, ogni destinatario riceverà link che puntano alla propria macchina. Per il destinatario i link non funzioneranno e, per un filtro antispam, sembreranno provenire da un mittente che non sa configurare il proprio dominio.
Configurare SMTP, che non si trova in config.toml
Cerca una sezione SMTP in config.toml: non la troverai. Le impostazioni della posta risiedono nel database, nella tabella settings, e si modificano nel pannello di amministrazione, alla voce Settings e SMTP. Per questo il file generato è così breve e una modifica SMTP non richiede il riavvio.
Per il server SMTP hai due opzioni concrete. Puoi gestirne uno autonomamente, assumendoti interamente la responsabilità della reputazione dell'indirizzo IP. È un progetto a sé stante: gestire un server di posta autonomamente con Mailcow spiega cosa comporta. In alternativa, puoi configurare Listmonk per usare un relay transazionale e affidare a un altro provider la gestione della reputazione dell'indirizzo IP.
In entrambi i casi, usa la porta 587 con STARTTLS oppure la porta 465 con TLS implicito. Non pianificare l'uso della porta 25 in uscita. La maggior parte dei provider VPS la blocca per impostazione predefinita sui nuovi account. Una porta 25 bloccata si comporta esattamente come una connessione bloccata: i pacchetti vengono scartati invece di essere rifiutati. Il client attende quindi il timeout invece di restituire subito un errore.
Esegui un test prima di fare affidamento sulla configurazione. Crea una lista, aggiungi il tuo indirizzo come iscritto e invia una campagna a un solo destinatario. Apri il messaggio ricevuto e leggi tutti gli header. L'header Authentication-Results aggiunto dal server ricevente indica se i controlli SPF e DKIM sono stati superati.
La deliverability è l'obiettivo complessivo
Listmonk compone il messaggio, gestisce la lista e consegna la posta al server di destinazione. Ogni decisione sulla consegna del messaggio nella casella di posta viene presa dal provider ricevente, in base all'indirizzo IP e al dominio del mittente. Un nuovo IP VPS non ha alcuna reputazione, e tutti i principali provider di caselle considerano l'assenza di storico un segnale di sospetto, anche se lieve.
Quattro elementi sono indispensabili:
- Un record TXT SPF (sender policy framework) che indichi l'host autorizzato a inviare per il dominio.
- Una chiave DKIM (domainkeys identified mail) pubblicata come record TXT, con la firma eseguita dal server di posta e non da Listmonk.
- Un record DMARC (domain based message authentication, reporting and conformance), che indichi ai server riceventi come comportarsi quando i primi due controlli falliscono.
- Una casella per i bounce che Listmonk possa leggere, in modo che gli indirizzi che rifiutano i messaggi vengano rimossi dalla lista invece di essere ritentati indefinitamente.
All'inizio invia i messaggi lentamente. Un dominio che non ha mai inviato posta e che improvvisamente consegna diecimila messaggi in un'ora ha esattamente l'aspetto di un account compromesso, quindi viene filtrato come tale. Inizia dagli iscritti più coinvolti e aumenta il volume nell'arco di alcuni giorni.
Ogni template deve inoltre contenere un link di annullamento dell'iscrizione funzionante. In un template Listmonk questo link è {{ UnsubscribeURL }}, mentre il corpo della campagna viene inserito nel punto occupato da {{ template "content" . }}, che deve comparire esattamente una volta per template. Una campagna senza link di annullamento dell'iscrizione genera segnalazioni di spam invece di richieste di annullamento, e le segnalazioni sono il modo più rapido per perdere una reputazione di invio costruita in settimane.
Backup e requisiti effettivi per un ripristino
Dal server devono essere copiati due elementi: il dump del database e config.toml. Aggiungete la directory dei file multimediali se caricate immagini nelle campagne.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpIl dump contiene gli iscritti, le campagne, i template e tutte le impostazioni, comprese le credenziali SMTP. Crittografatelo e conservatelo fuori da questo server. La pianificazione è un problema già risolto: consultate backup restic crittografati su storage remoto. config.toml contiene poche righe, ma include la password del database. Gestitelo quindi allo stesso modo.
Gli aggiornamenti seguono un ordine preciso. Arrestate il servizio, create un dump, sostituite il binario in /usr/bin, eseguite listmonk --config /etc/listmonk/config.toml --upgrade, quindi avviate il servizio. Le migrazioni dello schema procedono solo in avanti. Pertanto, quel dump è l'unico modo per tornare allo stato precedente.
Perché Listmonk non si avvia?
Leggere prima il journal con journalctl -u listmonk -n 50 --no-pager. Quasi tutti gli errori di avvio sono riportati su una riga nel blocco [db].
pq: password authentication failed for user "listmonk" indica che la password in [db] non corrisponde al ruolo Postgres. Il prefisso pq indica che il driver Postgres sta riportando il rifiuto del server. La configurazione è stata quindi letta correttamente, ma le credenziali non sono valide. Reimpostare il ruolo con sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" e inserire nella configurazione la stessa stringa.
pq: database "listmonk" does not exist indica che il valore database in [db] non identifica un database reale. sudo -u postgres psql -l elenca i database effettivamente presenti sul server, inclusa la grafia usata per errore.
permission denied durante --install indica che il ruolo può connettersi, ma non è il proprietario del database e quindi non può creare tabelle al suo interno. Correggere il problema con sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" ed eseguire nuovamente l'installazione.
Il servizio non si avvia mai e il journal indica il file di configurazione. Un processo eseguito come listmonk non può aprire un config.toml lasciato come root:root con modalità 600. stat -c '%U:%G %a' /etc/listmonk/config.toml dovrebbe stampare root:listmonk 640 e la directory che lo contiene dovrebbe essere root:listmonk 750.
Il pannello funziona, ma non arriva alcuna email. Questo non è un problema di avvio. Controllare prima Settings e SMTP, quindi il log della campagna nel pannello di amministrazione, che registra l'errore restituito dal server di posta per ogni tentativo.
FAQ
Devo avere un mio mail server per usare Listmonk?
No. Listmonk non è un mail server. Richiede le credenziali SMTP di un server che accetti i messaggi e li consegni ai destinatari. Può trattarsi di un relay transazionale oppure di un mail server gestito direttamente da te. Imposta queste credenziali nelle sezioni Settings e SMTP del pannello di amministrazione, non in config.toml, perché le impostazioni della posta sono memorizzate nel database. Usa la porta 587 con STARTTLS oppure la porta 465 con TLS implicito, perché la maggior parte dei provider VPS blocca la porta 25 in uscita sui nuovi account.
Perché i link della mia campagna puntano a localhost:9000?
L'impostazione dell'URL radice è ancora al valore predefinito dell'installazione, http://localhost:9000. Listmonk inserisce questo valore nei link di disiscrizione e negli URL dei media quando invia una campagna. Apri Settings nel pannello di amministrazione, imposta l'URL radice sul tuo indirizzo HTTPS reale e salva. I messaggi già consegnati non possono essere corretti. Invia quindi una campagna di test a te stesso e fai clic sul link di disiscrizione prima di inviare messaggi a una lista reale.
Eseguire nuovamente --install eliminerà i miei iscritti?
Sì. --install è l'installer eseguito alla prima installazione e rimuove lo schema Listmonk esistente. --yes elimina la richiesta di conferma che avrebbe mostrato un avviso. In qualunque script che potrebbe essere eseguito due volte, usa --install --idempotent --yes, che non esegue alcuna operazione se le tabelle esistono già. Per applicare le modifiche allo schema in una nuova release, arresta il servizio, crea un pg_dump, quindi esegui --upgrade.
Perché Listmonk segnala che l'autenticazione tramite password non è riuscita per l'utente listmonk?
La password nel blocco [db] di /etc/listmonk/config.toml non corrisponde a quella del ruolo Postgres con lo stesso nome. La riga del journal è pq: password authentication failed for user "listmonk". pq è il driver Postgres che inoltra il rifiuto del server. Questo significa che il file di configurazione è stato trovato e letto. Reimposta la password del ruolo con sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", scrivi la stessa stringa nel file di configurazione, quindi esegui sudo systemctl restart listmonk.