Como instalar o Listmonk no Ubuntu 24.04
Instale o Listmonk v6.2.0 no Ubuntu 24.04 com PostgreSQL, systemd e TLS. Configure o SMTP e entenda por que a entrega depende da reputação.
O que um newsletter auto-hospedado no Listmonk exige
O Listmonk é um gerenciador auto-hospedado de newsletters e listas de e-mail: um binário Go, um banco de dados PostgreSQL, um arquivo de configuração e uma unidade systemd. Um VPS pequeno executa o software sem dificuldade, porque o Listmonk armazena os assinantes e enfileira as campanhas, mas nunca entrega as mensagens por conta própria. Ele encaminha cada mensagem para um servidor SMTP (simple mail transfer protocol), portanto sua 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. Você precisa de um VPS com um endereço IP público, um nome de domínio sob seu controle e PostgreSQL 12 ou mais recente. A instalação leva cerca de uma hora. A reputação de envio leva semanas, e essa parte é abordada perto do final.
Instalar o PostgreSQL e criar o banco de dados
O Ubuntu 24.04 fornece o PostgreSQL 16 em seu próprio repositório, uma versão bem mais recente do que a exigida pelo Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlCrie a role e o banco de dados em uma ú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 aparência 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 é opcional. A instalação do schema cria tabelas, tipos, índices e funções, portanto a role precisa ser proprietária do banco de dados. Se você apontar o Listmonk para um banco de dados pertencente a outra role, a instalação será interrompida com permission denied, mesmo depois de executar GRANT CONNECT.
Confirme que o banco de dados existe antes de continuar.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Esse comando exibe listmonk. Uma linha vazia significa que a instrução CREATE não foi executada. Nesse caso, verifique novamente a saída do psql.
Instalar o binário do Listmonk
O Listmonk disponibiliza um binário estático para cada arquitetura. Verifique a sua primeiro, porque um binário amd64 em um VPS ARM é um arquivo que o kernel se recusa a 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 --versionEm um VPS ARM, substitua amd64 por arm64 no nome do arquivo. listmonk --version imprimir uma string de versão é a primeira confirmação de que o binário corresponde à máquina.
Gerar config.toml e restringir 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 arquivo gerado é curto. Em [app], address = "localhost:9000" vincula o servidor HTTP apenas ao loopback. Assim, o painel de administração não fica acessível pela internet até que você configure um proxy reverso na frente dele. Não altere essa linha. Em [db], você encontra host = "localhost", port = 5432, user = "listmonk", database = "listmonk" e ssl_mode = "disable". Esses valores padrão já correspondem ao banco de dados criado. Portanto, a única linha que você precisa alterar é a senha.
ssl_mode = "disable" está correto enquanto o Postgres escuta no loopback no mesmo computador, porque esse tráfego não sai da máquina. Se você mover o banco de dados para outro host, defina como require. Caso contrário, a senha atravessará a rede em texto simples.
Edite a linha da senha em [db] para que ela corresponda à role. Em seguida, crie a conta de serviço e retire o arquivo do acesso de todos os outros usuários.
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 arquivo, e ninguém mais pode.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlO primeiro comando exibe readable. O segundo exibe root:listmonk 640. Qualquer outra conta sem privilégios que tente executar o mesmo cat recebe Permission denied. Esse é o objetivo: o arquivo contém a senha do banco de dados em texto simples, e um servidor normalmente tem mais de uma conta de login. O mesmo princípio se aplica a todos os serviços que você executar. Portanto, leia usuários de serviço com privilégio mínimo uma vez e aplique esse princípio em todos os serviços.
Crie o esquema com --install
--install cria as tabelas e insere as configurações padrão. Defina o primeiro login 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 prompt de confirmação. Leia esse prompt uma vez antes de automatizá-lo, porque --install é a primeira execução do instalador e remove um esquema existente do Listmonk. Executá-lo uma segunda vez em um banco de dados em produção destrói seus assinantes. 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 em uma nova versão são aplicadas com --upgrade, nunca com --install.
Verifique o resultado pelo banco de dados, 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 lista as tabelas do Listmonk, entre elas subscribers, lists, campaigns, templates e bounces. O segundo exibe admin. Um resultado vazio no segundo comando significa que as variáveis de ambiente nunca chegaram ao processo. Nesse caso, o painel solicitará a criação do primeiro usuário 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 caminhos relativos, incluindo um caminho de upload de mídia no sistema de arquivos, com base nele. After=postgresql.service apenas define a ordem de inicialização. Ele não espera o Postgres aceitar conexões. Por isso, Restart=on-failure cobre o caso em que o Listmonk é iniciado cedo demais e não consegue se conectar.
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 retornando qualquer linha de status HTTP significa que o servidor está respondendo. curl falhando com Connection refused significa que o processo morreu durante a inicialização, e journalctl -u listmonk -n 50 --no-pager informará o motivo. Observe que enable --now é a parte que sobrevive a uma reinicialização: um processo iniciado manualmente desaparece após a próxima atualização do kernel.
Coloque o nginx e o TLS na frente
O Listmonk usa HTTP simples no loopback, portanto o nginx termina o TLS (segurança da camada de transporte) e encaminha a solicitação.
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 precisa ser aumentado porque as importações de assinantes e os uploads de mídia são envios de arquivos, e o nginx rejeita por padrão qualquer valor acima de 1 MB com 413 Request Entity Too Large. Emita o certificado com o certbot, que também grava as linhas listen 443 ssl e o redirecionamento da porta 80 para você: as etapas estão em guia do certificado Let's Encrypt para nginx. Abra as portas 80 e 443 e mantenha a porta 9000 fechada, pois o proxy a acessa pelo loopback. Se o firewall ainda não foi configurado, comece por conceitos básicos do firewall ufw.
Em seguida, abra o painel de administração e defina a URL raiz em Settings como https://lists.example.com. Uma instalação nova contém http://localhost:9000, e o Listmonk grava esse valor em cada link de cancelamento de inscrição e URL de mídia que inclui em um email. Envie uma campanha antes de alterá-lo, e cada destinatário receberá links que apontam para a própria máquina. Eles falham para o leitor e, para um filtro de spam, parecem indicar um remetente que não consegue configurar o próprio domínio.
Conectar o SMTP, que não fica no config.toml
Procure uma seção de SMTP em config.toml. Você não encontrará nenhuma. As configurações de e-mail ficam no banco de dados, na tabela settings, e são editadas no painel de administração, em Settings e SMTP. Por isso, o arquivo gerado permanece tão curto. Essa também é a razão pela qual uma alteração no SMTP não exige reinicialização.
Há duas opções adequadas para o próprio servidor SMTP. Execute o seu próprio servidor. Nesse caso, a reputação ficará inteiramente sob seu controle, e isso será um projeto completo por si só: executar seu próprio servidor de e-mail com Mailcow explica o que isso envolve. Ou direcione o Listmonk para um relay transacional e deixe outra empresa cuidar da reputação do IP.
Em qualquer uma das opções, use a porta 587 com STARTTLS ou a porta 465 com TLS implícito. Não planeje usar a porta de saída 25. A maioria dos provedores de VPS a bloqueia por padrão em contas novas. Uma porta 25 bloqueada parece exatamente uma conexão travada, porque os pacotes são descartados em vez de recusados. Por isso, o cliente espera até atingir o timeout, em vez de falhar imediatamente.
Teste antes de confiar no serviço. Crie uma lista, adicione seu próprio endereço como assinante 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 receptor, informa se SPF e DKIM foram aprovados.
A capacidade de entrega é todo o trabalho
O Listmonk cria a mensagem, acompanha a lista e entrega o e-mail ao servidor de destino. Toda decisão sobre a chegada desse e-mail à caixa de entrada é tomada pelo provedor de recebimento, usando o endereço IP de envio e o domínio de envio. Um novo IP de VPS não tem histórico algum, e todos os grandes provedores de caixas de correio tratam a falta de histórico como um sinal levemente suspeito.
Quatro itens são obrigatórios:
- Um registro 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 registro TXT, com a assinatura feita pelo servidor de e-mail, e não pelo Listmonk.
- Um registro DMARC (domain based message authentication, reporting and conformance), que informa aos servidores de recebimento o que fazer quando os dois primeiros mecanismos falharem.
- Uma caixa de correio para mensagens devolvidas que o Listmonk leia, para que os endereços que rejeitam e-mails saiam da lista em vez de serem tentados novamente indefinidamente.
Depois, envie as mensagens lentamente no início. Um domínio que nunca enviou e-mails e, de repente, entrega dez mil mensagens em uma hora tem exatamente o perfil de uma conta comprometida, portanto é filtrado como tal. Comece pelos assinantes mais engajados e aumente o volume ao longo de vários dias.
Todo template também precisa de um link de cancelamento de inscrição funcional. Em um template do Listmonk, ele é {{ UnsubscribeURL }}, e o corpo da campanha aparece no local ocupado por {{ template "content" . }}, que deve aparecer exatamente uma vez em cada template. Uma campanha sem link de cancelamento de inscrição gera reclamações de spam em vez de cancelamentos, e as reclamações são a maneira mais rápida de perder uma reputação de envio que levou semanas para construir.
Backups e o que uma restauração realmente precisa
Duas coisas precisam sair do servidor: o dump do banco de dados e config.toml. Adicione o diretório de mídia se você fizer upload de imagens para as campanhas.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpEsse dump contém assinantes, campanhas, modelos e todas as configurações, incluindo as credenciais SMTP. Portanto, criptografe-o e mantenha-o fora deste servidor. Agendar isso é um problema resolvido: consulte backups restic criptografados em armazenamento remoto. config.toml tem poucas linhas, mas contém a senha do banco de dados. Portanto, trate-o da mesma forma.
As atualizações seguem uma ordem. 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 de esquema só avançam. Portanto, esse dump é sua única forma de voltar à versão anterior.
Por que o Listmonk falha ao iniciar?
Leia primeiro o journal com journalctl -u listmonk -n 50 --no-pager. Quase toda falha de inicialização aparece em uma linha do bloco [db].
pq: password authentication failed for user "listmonk" significa que a senha em [db] não corresponde à role do Postgres. O prefixo pq indica que o driver do Postgres está informando a rejeição do servidor. Portanto, a configuração foi lida corretamente, mas as credenciais estão erradas. Redefina a role com sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" e coloque a mesma string no arquivo.
pq: database "listmonk" does not exist significa que o valor de database em [db] não identifica um banco de dados existente. sudo -u postgres psql -l lista o que realmente existe no servidor, incluindo a grafia usada por engano.
permission denied durante --install significa que a role consegue se conectar, mas não é proprietária do banco de dados. Por isso, ela não pode criar tabelas nele. Corrija com sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" e execute a instalação novamente.
O serviço nunca inicia e o journal identifica o arquivo de configuração. Um processo executado como listmonk não consegue abrir um config.toml definido como root:root com o modo 600. stat -c '%U:%G %a' /etc/listmonk/config.toml deve exibir root:listmonk 640, e o diretório acima dele deve ser root:listmonk 750.
O painel funciona, mas nenhum email chega. Isso não é um problema de inicialização. Verifique primeiro Settings e SMTP. Depois, verifique o log da própria campanha no painel de administração. Ele registra o erro retornado 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. Ele precisa das credenciais SMTP de um servidor que aceite suas mensagens e faça a entrega. Esse servidor pode ser um relay transacional ou um servidor de e-mail administrado por você. Defina essas credenciais em Settings e SMTP no painel de administração, não em config.toml, porque as configurações de e-mail ficam no banco de dados. Use a porta 587 com STARTTLS ou a porta 465 com TLS implícito, pois a maioria dos provedores de VPS bloqueia a porta de saída 25 em contas novas.
Por que os links da minha campanha apontam para localhost:9000?
A configuração da URL raiz ainda está com o valor padrão da instalação, http://localhost:9000. O Listmonk grava esse valor nos links de cancelamento de inscrição e nas URLs de mídia no momento em que a campanha é enviada. Abra Settings no painel de administração, defina a URL raiz como seu endereço HTTPS real e salve. As mensagens que já foram entregues não podem ser corrigidas. Portanto, envie uma campanha de teste para você mesmo e clique no link de cancelamento de inscrição antes de enviar uma lista real.
Executar --install novamente apagará meus inscritos?
Sim. --install é o instalador usado na primeira execução e remove o schema existente do Listmonk. --yes remove o prompt que avisaria sobre isso. 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 de schema em uma nova versão, pare o serviço, faça um pg_dump e execute --upgrade.
Por que o Listmonk informa que a autenticação por senha falhou para o usuário listmonk?
A senha 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 repassando a rejeição do servidor. Isso significa que o arquivo de configuração foi encontrado e lido. Redefina a senha da role com sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", grave a mesma string no arquivo de configuração e execute sudo systemctl restart listmonk.