Installare Listmonk su VPS Ubuntu 24.04
Guida a Listmonk v6.2.0 su Ubuntu 24.04: PostgreSQL, config.toml, systemd, TLS e SMTP, con una spiegazione realistica dei tempi e costi di recapito.
Requisiti per una newsletter self-hosted con Listmonk
Listmonk è un gestore self-hosted di newsletter e mailing list: un binario Go, un database PostgreSQL, un file di configurazione e un'unità systemd. Un VPS di piccole dimensioni lo esegue senza problemi, perché Listmonk archivia gli iscritti e mette in coda le campagne, ma non invia direttamente i messaggi. Inoltra ogni messaggio a un server SMTP (simple mail transfer protocol), quindi il tasso di recapito dipende dalla reputazione di quel server e non da questo software.
Questa guida installa Listmonk v6.2.0, la versione corrente a luglio 2026, su Ubuntu 24.04. Sono necessari un VPS con un indirizzo IP pubblico, un nome di dominio sotto il tuo controllo e PostgreSQL 12 o versione successiva. L'installazione richiede circa un'ora. La reputazione del mittente 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 nella stessa sessione psql. -v ON_ERROR_STOP=1 fa terminare psql al primo comando non riuscito, quindi un errore di digitazione non lascia una configurazione parziale che sembra 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 non è facoltativo. 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 procedere.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Il comando restituisce listmonk. Una riga vuota indica che l'istruzione CREATE non è mai stata eseguita; controllate nuovamente l'output di psql.
Installare il binario di Listmonk
Listmonk rilascia un binario statico per ogni architettura. Verifica prima l'architettura del sistema, perché il kernel rifiuta di eseguire un binario amd64 su un VPS ARM.
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. Se listmonk --version stampa una stringa con la versione, hai 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. Il pannello di amministrazione non è quindi raggiungibile da Internet finché non configuri un reverse proxy davanti ad esso. Non modificare quella riga. In [db] trovi host = "localhost", port = 5432, user = "listmonk", database = "listmonk" e ssl_mode = "disable". Questi valori predefiniti corrispondono già al database che hai creato. Devi modificare solo la password.
ssl_mode = "disable" è corretto mentre Postgres è in ascolto sull'interfaccia di loopback dello stesso computer, perché questo traffico non lascia la macchina. Se sposti il database su un altro host, impostalo su require. In caso contrario, la password attraversa la rete in chiaro.
Modifica la riga della password in [db] in modo che corrisponda al ruolo. Poi crea l'account di servizio e rimuovi l'accesso al file da 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 prova lo stesso cat riceve Permission denied. Questo è il risultato previsto: il file contiene la password del database in chiaro e, in genere, un server ha più account di accesso. Lo stesso principio si applica a ogni servizio eseguito. Leggi quindi utenti di servizio con privilegi minimi e applicalo ovunque.
Creare lo schema con --install
--install crea le tabelle e inserisce le impostazioni predefinite. Imposta il primo accesso amministrativo con 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. Leggi una volta quella richiesta prima di automatizzarla, perché --install è il comando di prima installazione e rimuove uno schema Listmonk esistente. Eseguirlo una seconda volta su un database in uso distrugge gli iscritti. In ogni script che potrebbe essere eseguito due volte, usa --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.
Verifica 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 state trasmesse al processo; il pannello chiederà quindi di creare il primo utente nel browser.
Eseguire Listmonk con systemd
Scrivere /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 i percorsi relativi, incluso un percorso del filesystem per il caricamento dei file multimediali, rispetto a esso. After=postgresql.service ordina solo l'avvio; non attende che Postgres accetti le connessioni. Per questo Restart=on-failure gestisce il caso in cui Listmonk si avvii troppo presto 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. Se curl restituisce una riga di stato HTTP, significa che il server sta rispondendo. Se curl non riesce con Connection refused, significa che il processo è terminato durante l'avvio; journalctl -u listmonk -n 50 --no-pager indicherà il motivo. Notare che enable --now è la parte che rimane attiva dopo un riavvio: un processo avviato manualmente termina dopo il successivo aggiornamento del kernel.
Metti nginx e TLS davanti
Listmonk comunica tramite HTTP non cifrato sull'interfaccia di loopback, quindi nginx termina TLS (sicurezza del livello di trasporto) 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;
}
}È necessario aumentare client_max_body_size perché le importazioni degli iscritti e i caricamenti multimediali sono richieste di caricamento file; per impostazione predefinita, nginx rifiuta qualsiasi file di dimensioni superiori a 1 MB con 413 Request Entity Too Large. Emetti il certificato con certbot, che scrive anche le righe listen 443 ssl e configura per te il reindirizzamento dalla porta 80: la procedura è descritta 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 root alla voce Settings su https://lists.example.com. Una nuova installazione contiene http://localhost:9000, e Listmonk inserisce questo 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 riceve link che puntano alla propria macchina. Per il destinatario i link non funzionano e, per un filtro antispam, fanno pensare a un mittente che non sa configurare il proprio dominio.
Collegare SMTP, che non è presente in config.toml
Cercate una sezione SMTP in config.toml: non la troverete. Le impostazioni della posta risiedono nel database, nella tabella settings, e si modificano nel pannello di amministrazione, nelle sezioni Settings e SMTP. Per questo il file generato rimane così breve. Per lo stesso motivo, una modifica SMTP non richiede il riavvio.
Per il server SMTP avete due opzioni valide. Potete gestirne uno autonomamente. In questo caso la reputazione dipende interamente da voi e il progetto richiede un impegno significativo: gestire il proprio server di posta con Mailcow descrive cosa comporta. In alternativa, potete configurare Listmonk per usare un relay transazionale e affidare ad altri la gestione della reputazione dell'indirizzo IP.
In entrambi i casi, usate la porta 587 con STARTTLS oppure la porta 465 con TLS implicito. Non pianificate l'invio tramite la porta 25. La maggior parte dei provider VPS la blocca per impostazione predefinita nei nuovi account. Una porta 25 bloccata si manifesta esattamente come una connessione che rimane in attesa, perché i pacchetti vengono eliminati invece di essere rifiutati. Il client attende quindi il timeout invece di interrompersi immediatamente.
Eseguite un test prima di considerare la configurazione attendibile. Create una lista, aggiungete il vostro indirizzo come iscritto e inviate una campagna a un solo destinatario. Aprite il messaggio ricevuto e leggete tutte le intestazioni. L'intestazione Authentication-Results aggiunta dal lato ricevente indica se SPF e DKIM hanno superato la verifica.
La deliverability è l'obiettivo principale
Listmonk compone il messaggio, gestisce la lista e trasferisce la posta. Ogni decisione sulla consegna del messaggio nella posta in arrivo viene presa dal provider destinatario, in base all'indirizzo IP di invio e al dominio di invio. Un nuovo indirizzo IP VPS non ha alcuna cronologia, e tutti i principali provider di caselle di posta considerano l'assenza di cronologia un elemento almeno leggermente sospetto.
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; la firma deve essere eseguita dal mail server, non da Listmonk.
- Un record DMARC (domain based message authentication, reporting and conformance), che indica ai destinatari cosa fare quando i primi due controlli falliscono.
- Una casella per i messaggi di mancata consegna 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 presenta esattamente le caratteristiche 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 funzionante per annullare l'iscrizione. In un template Listmonk questo link è {{ UnsubscribeURL }}, mentre il corpo della campagna viene inserito nella posizione occupata da {{ template "content" . }}, che deve comparire esattamente una volta per template. Una campagna senza link per annullare l'iscrizione genera segnalazioni di spam invece di richieste di annullamento, e le segnalazioni sono il modo più rapido per perdere la reputazione di invio costruita in settimane.
Backup e ciò che serve realmente per un ripristino
Devono uscire dal server 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 modelli e tutte le impostazioni, incluse 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 è composto da poche righe, ma contiene la password del database, quindi trattatelo 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 vengono eseguite solo in avanti, quindi quel dump è l'unico modo per tornare indietro.
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 del blocco [db].
pq: password authentication failed for user "listmonk" indica che la password in [db] non corrisponde al ruolo PostgreSQL. Il prefisso pq indica che il driver PostgreSQL sta segnalando il rifiuto del server. La configurazione è stata quindi letta correttamente, ma le credenziali non erano corrette. Reimpostare il ruolo con sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" e inserire nel file la stessa stringa.
pq: database "listmonk" does not exist indica che il valore database in [db] non identifica un database esistente. sudo -u postgres psql -l elenca i database presenti sul server, inclusa la grafia usata per errore.
permission denied durante --install indica che il ruolo può connettersi, ma non è 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 con proprietario root:root e modalità 600. stat -c '%U:%G %a' /etc/listmonk/config.toml dovrebbe visualizzare root:listmonk 640 e la directory superiore dovrebbe essere root:listmonk 750.
Il pannello funziona, ma non arriva alcuna email. 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 server di posta per usare Listmonk?
No. Listmonk non è un server di posta. Richiede le credenziali SMTP di un server che accetta i messaggi e li consegna. Può trattarsi di un relay transazionale o di un server di posta gestito autonomamente. Impostare queste credenziali in Settings e SMTP nel pannello di amministrazione, non in config.toml, perché le impostazioni di posta sono archiviate nel database. Usare la porta 587 con STARTTLS oppure la porta 465 con TLS implicito, poiché la maggior parte dei provider VPS blocca la porta 25 in uscita per i nuovi account.
Perché i link della mia campagna puntano a localhost:9000?
L'impostazione dell'URL radice conserva ancora il valore predefinito dell'installazione, http://localhost:9000. Listmonk inserisce questo valore nei link per annullare l'iscrizione e negli URL dei contenuti multimediali quando invia una campagna. Aprire Settings nel pannello di amministrazione, impostare l'URL radice sull'indirizzo HTTPS reale e salvare. I messaggi già consegnati non possono essere corretti. Inviare quindi una campagna di test a se stessi e selezionare il link per annullare l'iscrizione prima di inviare messaggi a una lista reale.
Eseguire di nuovo --install eliminerà i miei iscritti?
Sì. --install è il programma di installazione per la prima esecuzione e rimuove lo schema Listmonk esistente, mentre --yes elimina la richiesta di conferma che avrebbe mostrato un avviso. In qualsiasi script che potrebbe essere eseguito due volte, usare --install --idempotent --yes, che non esegue alcuna operazione se le tabelle esistono già. Per applicare le modifiche allo schema in una nuova release, arrestare il servizio, creare un pg_dump, quindi eseguire --upgrade.
Perché Listmonk indica 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 riporta il rifiuto del server. Questo indica che il file di configurazione è stato trovato e letto. Reimpostare la password del ruolo con sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", inserire la stessa stringa nel file di configurazione, quindi eseguire sudo systemctl restart listmonk.