Como instalar o Listmonk em um VPS Ubuntu 24.04
Instale o Listmonk v6.2.0 no Ubuntu 24.04 com PostgreSQL, systemd, TLS e SMTP. Entenda por que a reputação do servidor define a entrega.
Requisitos de uma newsletter autoalojada no Listmonk
O Listmonk é um gestor autoalojado de newsletters e listas de correio: um binário Go, uma base de dados PostgreSQL, um ficheiro de configuração e uma unidade systemd. Um VPS pequeno executa-o sem dificuldade, porque o Listmonk armazena os subscritores e coloca as campanhas em fila, mas nunca entrega o correio por si próprio. Entrega cada mensagem a um servidor SMTP (simple mail transfer protocol), por isso a taxa de entrega é determinada pela reputação desse servidor, e não por este software.
Este guia instala o Listmonk v6.2.0, a versão atual em julho de 2026, no Ubuntu 24.04. Precisa de um VPS com um endereço IP público, um nome de domínio que controle e PostgreSQL 12 ou posterior. A instalação demora cerca de uma hora. A reputação de envio demora semanas, e essa parte é abordada perto do fim.
Instalar o PostgreSQL e criar a base de dados
O Ubuntu 24.04 inclui o PostgreSQL 16 no seu próprio repositório, uma versão muito mais recente do que a necessária para o Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlCrie a role e a base de dados numa única sessão do psql. -v ON_ERROR_STOP=1 faz o psql sair na primeira instrução que falhar, para que um erro de digitação não deixe uma configuração incompleta com aspeto de concluída.
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 não é apenas uma formalidade. A instalação do esquema cria tabelas, tipos, índices e funções, por isso a role tem de ser proprietária da base de dados. Se apontar o Listmonk para uma base de dados pertencente a outra role, a instalação termina com permission denied, mesmo depois de executar GRANT CONNECT.
Confirme que a base de dados existe antes de prosseguir.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"O comando apresenta listmonk. Uma linha vazia significa que a instrução CREATE nunca foi executada. Consulte novamente o resultado do psql.
Instale o binário do Listmonk
O Listmonk disponibiliza um binário estático para cada arquitetura. Verifique primeiro a arquitetura do seu sistema, porque um binário amd64 num VPS ARM é um ficheiro que o kernel recusa executar.
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 --versionNum VPS ARM, substitua amd64 por arm64 no nome do ficheiro. listmonk --version imprimir uma cadeia de versão é a primeira confirmação de que o binário corresponde à máquina.
Gere o config.toml e restrinja o acesso
--new-config grava config.toml no diretório de trabalho atual. Por isso, cd fica dentro de sh -c, e não antes de sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'O ficheiro gerado é curto. Em [app], address = "localhost:9000" liga o servidor HTTP apenas ao loopback. Assim, o painel de administração não fica acessível a partir da Internet até colocar um reverse proxy à frente dele. Não altere essa linha. Em [db], encontra host = "localhost", port = 5432, user = "listmonk", database = "listmonk" e ssl_mode = "disable". Esses valores predefinidos já correspondem à base de dados que criou. A única linha que tem de alterar é a palavra-passe.
ssl_mode = "disable" está correto enquanto o Postgres escutar no loopback do mesmo computador, porque esse tráfego nunca sai da máquina. Se mover a base de dados para outro host, defina-o como require. Caso contrário, a palavra-passe atravessa a rede em texto simples.
Edite a linha da palavra-passe em [db] para corresponder à role. Depois, crie a conta de serviço e retire o acesso ao ficheiro de todas as outras contas.
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.tomlAgora, a conta de serviço pode ler o ficheiro e mais ninguém pode.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlO primeiro comando apresenta readable. O segundo apresenta root:listmonk 640. Qualquer outra conta sem privilégios que tente executar o mesmo cat recebe Permission denied. Esse é o objetivo: este ficheiro contém a palavra-passe da base de dados em texto simples, e um servidor normalmente tem mais do que uma conta de acesso. O mesmo princípio aplica-se a todos os serviços que executar. Por isso, leia utilizadores de serviço com privilégios mínimos e aplique-o em todo o ambiente.
Crie o esquema com --install
--install cria as tabelas e insere as definições padrão. Defina o primeiro acesso de administrador com variáveis de ambiente, para que a conta exista antes de o painel ficar acessível.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes responde ao pedido de confirmação. Leia esse pedido uma vez antes de o automatizar, porque --install é o instalador inicial e elimina um esquema Listmonk existente. Executá-lo uma segunda vez numa base de dados em produção destrói os seus subscritores. Em qualquer script que possa ser executado duas vezes, use --install --idempotent --yes, que não faz nada quando as tabelas já existem. As alterações de esquema incluídas numa nova versão são aplicadas com --upgrade, nunca com --install.
Verifique o resultado a partir do lado da base de dados, e não pelo navegador.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"O primeiro comando lista as tabelas do Listmonk, incluindo subscribers, lists, campaigns, templates e bounces. O segundo apresenta admin. Um resultado vazio do segundo comando significa que as variáveis de ambiente nunca chegaram ao processo. Nesse caso, o painel solicitará que crie o primeiro utilizador no navegador.
Execute o Listmonk com systemd
Escreva /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 porque o Listmonk resolve os caminhos relativos, incluindo um caminho do sistema de ficheiros para carregamento de media, com base nele. After=postgresql.service apenas ordena o arranque; não espera que o Postgres aceite ligações. Por isso, Restart=on-failure cobre o caso em que o Listmonk arranca um pouco cedo demais e não consegue ligar-se.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss deve mostrar 127.0.0.1:9000 no estado LISTEN. curl devolver qualquer linha de estado HTTP significa que o servidor está a responder. curl falhar com Connection refused significa que o processo terminou durante o arranque, e journalctl -u listmonk -n 50 --no-pager indicará o motivo. Note que enable --now é a parte que permanece após um reboot: um processo iniciado manualmente desaparece após a atualização seguinte do kernel.
Coloque o nginx e o TLS à frente
O Listmonk usa HTTP simples no loopback, por isso o nginx termina o TLS (segurança da camada de transporte) e encaminha o pedido.
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 tem de ser aumentado porque as importações de subscritores e os carregamentos de ficheiros multimédia são pedidos de ficheiros, e o nginx rejeita por predefinição qualquer pedido superior a 1 MB com 413 Request Entity Too Large. Emita o certificado com o certbot, que também escreve as linhas listen 443 ssl e configura o redirecionamento da porta 80 por si: os passos estão no guia de certificados Let's Encrypt para nginx. Abra as portas 80 e 443 e mantenha a porta 9000 fechada, porque o proxy acede-lhe através do loopback. Se a firewall ainda não tiver sido configurada, comece por noções básicas da firewall ufw.
Depois, abra o painel de administração e defina o URL raiz em Settings como https://lists.example.com. Uma instalação nova contém http://localhost:9000, e o Listmonk escreve esse valor em todas as ligações de cancelamento de subscrição e nos URLs de ficheiros multimédia que inclui num email. Se enviar uma campanha antes de alterar esse valor, cada destinatário receberá ligações que apontam para a própria máquina. Essas ligações falham para o leitor e, para um filtro de spam, parecem indicar um remetente que não consegue configurar o próprio domínio.
Ligar o SMTP, que não está no config.toml
Pesquise config.toml por uma secção SMTP e não encontrará nenhuma. As definições de correio estão na base de dados, na tabela settings, e são editadas no painel de administração, em Settings e SMTP. É por isso que o ficheiro gerado é tão curto. É também por isso que uma alteração ao SMTP não requer um reinício.
Existem duas opções realistas para o próprio servidor SMTP. Pode executar o seu próprio servidor. Nesse caso, a reputação fica inteiramente sob o seu controlo, e este é um projeto completo por si só: executar o seu próprio servidor de correio com Mailcow explica o que isso implica. Ou pode configurar o Listmonk para usar um relay transacional e deixar a reputação do endereço IP a cargo de outra entidade.
Em qualquer dos casos, use a porta 587 com STARTTLS ou a porta 465 com TLS implícito. Não planeie usar a porta de saída 25. A maioria dos fornecedores de VPS bloqueia-a por predefinição em contas novas. Uma porta 25 bloqueada parece exatamente uma ligação bloqueada, porque os pacotes são descartados em vez de rejeitados. Por isso, o cliente aguarda um timeout em vez de falhar rapidamente.
Faça um teste antes de confiar na configuração. Crie uma lista, adicione o seu próprio endereço como subscritor e envie uma campanha para um único destinatário. Abra a mensagem recebida e leia os cabeçalhos completos. O cabeçalho Authentication-Results, adicionado pelo lado recetor, indica se SPF e DKIM foram aprovados.
A capacidade de entrega é o trabalho todo
O Listmonk cria a mensagem, acompanha a lista e entrega o email. Todas as decisões sobre a chegada desse email à caixa de entrada são tomadas pelo provedor recetor, com base no endereço IP de envio e no domínio de envio. Um novo IP de VPS não tem histórico, e todos os grandes provedores de caixas de correio consideram a ausência de histórico ligeiramente suspeita.
Quatro elementos são obrigatórios:
- Um registo TXT de SPF (sender policy framework) que identifique o host autorizado a enviar pelo seu domínio.
- Uma chave DKIM (domainkeys identified mail) publicada como registo TXT, com a assinatura feita pelo servidor de email e não pelo Listmonk.
- Um registo DMARC (domain based message authentication, reporting and conformance), que indique aos recetores o que fazer quando os dois primeiros mecanismos falharem.
- Uma caixa de correio para bounces que o Listmonk leia, para que os endereços que rejeitam emails sejam removidos da lista em vez de serem tentados indefinidamente.
Depois, envie mensagens lentamente no início. Um domínio que nunca enviou emails e passa subitamente a entregar dez mil mensagens numa hora tem exatamente o perfil de uma conta comprometida, por isso é filtrado como tal. Comece pelos subscritores mais ativos e aumente o volume ao longo de vários dias.
Todos os templates também precisam de um link de cancelamento de subscrição funcional. Num template do Listmonk, esse link é {{ UnsubscribeURL }}, e o corpo da campanha é inserido onde aparece {{ template "content" . }}, que deve surgir exatamente uma vez em cada template. Uma campanha sem link de cancelamento de subscrição gera denúncias de spam em vez de cancelamentos, e as denúncias são a forma mais rápida de perder uma reputação de envio que demorou semanas a construir.
Backups e o que uma restauração realmente exige
Dois itens devem sair do servidor: o dump da base de dados e config.toml. Adicione o diretório de media se carregar imagens para as campanhas.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpEsse dump contém os subscritores, as campanhas, os templates e todas as definições, incluindo as credenciais SMTP. Por isso, encripte-o e mantenha-o fora deste servidor. O agendamento já está resolvido: consulte backups restic encriptados para armazenamento remoto. config.toml tem poucas linhas, mas contém a palavra-passe da base de dados, por isso deve ser tratado da mesma forma.
As atualizações seguem uma ordem definida. Pare o serviço, faça um dump, substitua o binário em /usr/bin, execute listmonk --config /etc/listmonk/config.toml --upgrade e inicie o serviço. As migrações do esquema só avançam, por isso esse dump é a sua única forma de voltar ao estado anterior.
Por que o Listmonk falha ao iniciar?
Leia primeiro o journal com journalctl -u listmonk -n 50 --no-pager. Quase todas as falhas de inicialização aparecem numa linha do bloco [db].
pq: password authentication failed for user "listmonk" significa que a palavra-passe em [db] não corresponde à role do Postgres. O prefixo pq indica que o driver do Postgres está a reportar a rejeição do servidor. Isso significa que a configuração foi lida corretamente, mas as credenciais estavam erradas. Redefina a role com sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" e coloque a mesma string no ficheiro.
pq: database "listmonk" does not exist significa que o valor database em [db] não identifica uma base de dados existente. sudo -u postgres psql -l lista o que existe efetivamente no servidor, incluindo a grafia usada por engano.
permission denied durante --install significa que a role consegue ligar-se, mas não é proprietária da base de dados. Por isso, não pode criar tabelas nela. Corrija com sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" e execute novamente a instalação.
O serviço nunca inicia e o journal indica o ficheiro de configuração. Um processo executado como listmonk não consegue abrir um config.toml deixado como root:root com o modo 600. stat -c '%U:%G %a' /etc/listmonk/config.toml deve mostrar root:listmonk 640, e o diretório acima deve ser root:listmonk 750.
O painel funciona, mas não chega nenhuma mensagem. Isso não é um problema de inicialização. Verifique primeiro Settings e SMTP. Depois, consulte o log da própria campanha no painel de administração. Esse log regista o erro devolvido pelo servidor de email em cada tentativa.
FAQ
Preciso do meu próprio servidor de e-mail para usar o Listmonk?
Não. O Listmonk não é um servidor de e-mail. Precisa de credenciais SMTP para um servidor que aceite e entregue as suas mensagens. Esse servidor pode ser um relay transacional ou um servidor de e-mail que mantenha. Defina essas credenciais em Settings e SMTP no painel de administração, não em config.toml, porque as definições de e-mail ficam armazenadas na base de dados. Use a porta 587 com STARTTLS ou a porta 465 com TLS implícito, porque a maioria dos fornecedores de VPS bloqueia a porta de saída 25 em contas novas.
Porque apontam para localhost:9000 os links da minha campanha?
A definição do URL raiz ainda está no valor predefinido da instalação, http://localhost:9000. O Listmonk grava esse valor nos links de cancelamento da subscrição e nos URLs de multimédia no momento do envio da campanha. Abra Settings no painel de administração, defina o URL raiz como o seu endereço HTTPS real e guarde. As mensagens já entregues não podem ser corrigidas. Por isso, envie uma campanha de teste para si próprio e clique no link de cancelamento da subscrição antes de enviar mensagens para uma lista real.
Executar --install novamente elimina os meus subscritores?
Sim. --install é o instalador da primeira execução e remove o esquema existente do Listmonk. --yes remove a confirmação que o avisaria desse procedimento. Em qualquer script que possa ser executado duas vezes, use --install --idempotent --yes. Esse comando não faz nada quando as tabelas já existem. Para aplicar as alterações do esquema numa nova versão, pare o serviço, faça um pg_dump e execute --upgrade.
Porque diz o Listmonk que a autenticação por palavra-passe falhou para o utilizador listmonk?
A palavra-passe no bloco [db] de /etc/listmonk/config.toml não corresponde à role do Postgres com o mesmo nome. A linha do journal é pq: password authentication failed for user "listmonk". pq é o driver do Postgres a transmitir a rejeição do servidor, o que significa que o ficheiro de configuração foi encontrado e lido. Redefina a palavra-passe da role com sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", escreva a mesma cadeia no ficheiro de configuração e execute sudo systemctl restart listmonk.