Usando e-mail local como ferramenta de notificação
Como usar o e-mail local do Linux para guardar notificações de cron e scripts sem depender de uma interface gráfica.
O problema
Eu executo comandos e desenvolvo no terminal Linux, além de manter tarefas agendadas em segundo plano. Nem sempre preciso de uma notificação gráfica; muitas vezes só quero saber que um processo terminou ou que uma tarefa encontrou um erro.
Uma alternativa simples é usar a caixa de e-mail local do próprio sistema como repositório dessas mensagens.
Este texto é uma atualização revisada do artigo original Using local mail as notification tool, publicado em 2022. Os exemplos foram comparados com um setup real em Debian 13.
O que continua válido
Quando um job do cron produz saída e não define MAILTO vazio, o cron pode enviar essa saída para o proprietário da crontab ou para o destinatário configurado em MAILTO.
Esse comportamento é diferente de enviar mensagens pela Internet. Aqui, o Postfix entrega a mensagem localmente, em uma caixa mbox como /var/mail/<usuário>, e o Mutt funciona como leitor no terminal.
Este guia foi validado em Debian 13. O mesmo modelo também se aplica ao Ubuntu, mas os nomes dos pacotes e os comandos de mail podem variar entre distribuições.
Instalação
No ambiente de referência, uso o bsd-mailx para enviar mensagens, o Postfix como MTA local e o Mutt para ler a caixa:
sudo apt update
sudo apt install postfix bsd-mailx mutt
Durante a configuração do Postfix, escolha o modo local quando ele estiver disponível. O objetivo é aceitar e entregar mensagens no próprio host, não transformar a máquina em servidor de e-mail público.
Confira os comandos disponíveis:
command -v mail mailx mutt
Implementações diferentes de mail e mailx não aceitam exatamente as mesmas opções. No exemplo abaixo uso a sintaxe curta -s, compatível com o bsd-mailx instalado no servidor.
No Debian 13, o Mutt pode usar /var/tmp para seus arquivos temporários. Se esse diretório estiver restrito ao root, o Mutt abre a caixa, mas falha ao exibir uma mensagem com Não foi possível criar um arquivo temporário!. Nesse caso, use um diretório temporário gravável pelo usuário:
export TMPDIR=/tmp
mutt -D | grep '^tmpdir='
O comando deve mostrar tmpdir="/tmp". Para tornar a configuração permanente apenas para o usuário atual, também é possível adicionar set tmpdir="/tmp" ao ~/.muttrc.
Testando a caixa local
Envie uma mensagem para o usuário atual:
printf '%s\n' 'This is a test' | mail -s 'Test email' "$USER@localhost"
Depois, abra o leitor:
mutt
Mostrando mensagens não lidas no prompt
No Bash, um pequeno hook pode consultar a caixa periodicamente e mostrar um aviso no prompt. O exemplo usa a saída do bsd-mailx, que marca mensagens novas ou não lidas com N ou U:
MAIL_CHECK_TIME=0
mail_prompt() {
local mail_interval=${MAILCHECK:-60}
local elapsed=$((SECONDS - MAIL_CHECK_TIME))
if [[ "$MAIL_CHECK_TIME" -eq 0 || "$elapsed" -gt "$mail_interval" ]]; then
local count
count=$(printf 'q\n' | mail -u "$USER" 2>/dev/null \
| awk '/^[[:space:]]*[NU][[:space:]]+[0-9]+/ { count++ } END { print count + 0 }')
if [[ "$count" -gt 0 ]]; then
local word='message'
[[ "$count" -gt 1 ]] && word='messages'
echo "$count unread $word. Run mutt"
fi
MAIL_CHECK_TIME=$SECONDS
fi
}
if command -v mail >/dev/null 2>&1; then
PROMPT_COMMAND="mail_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
fi
Salve o trecho, por exemplo, em ~/.mail_prompt, e carregue-o no ~/.bashrc:
source "$HOME/.mail_prompt"
Esse contador é deliberadamente específico para bsd-mailx e Bash. Outras implementações de mail podem formatar os headers de outra maneira; nesse caso, a regra do awk precisa ser testada novamente.
O Postfix entrega a mensagem na caixa local, normalmente em /var/mail/$USER. O caminho pode ser confirmado com:
postconf mail_spool_directory
No ambiente que usei, a caixa pertence diretamente ao usuário e não é necessário adicioná-lo ao grupo mail.
Usando com cron
Uma crontab pode enviar a saída de uma tarefa para a caixa local:
MAILTO=guionardo
*/15 * * * * /home/guionardo/bin/check-backups
Se o comando escrever no stdout ou no stderr, o cron encaminha essa saída como mensagem. Para testar sem esperar o agendamento:
/home/guionardo/bin/check-backups
Depois, abra o Mutt e confira a nova mensagem.
Se a tarefa for executada por um script próprio, também é possível enviar uma mensagem explicitamente:
if ! /home/guionardo/bin/check-backups; then
printf '%s\n' 'A verificação de backups falhou.' \
| mail -s 'Falha no backup' "$USER@localhost"
fi
Configuração local e segurança
Para uma caixa local, verifique se o Postfix está limitado ao loopback e se a rede autorizada contém apenas o próprio host:
postconf inet_interfaces mynetworks mydestination relayhost
Uma configuração local típica inclui loopback-only em inet_interfaces, endereços de loopback em mynetworks, o próprio host em mydestination e nenhum relayhost externo.
Não copie uma configuração de servidor de e-mail completo para este caso. Um Postfix exposto ou configurado para aceitar relay de outras redes pode transformar uma ferramenta local em um problema de abuso de e-mail.
Diagnóstico
Quando uma mensagem não aparece, confira:
postfix check
journalctl -u postfix --since '10 minutes ago'
ls -l /var/mail/"$USER"
mutt -D | grep '^tmpdir='
Também vale lembrar que uma tarefa silenciosa não gera uma mensagem automática do cron. Se não houver saída, use uma chamada explícita a mail ou registre uma mensagem de erro.
Limites desta abordagem
O e-mail local é ótimo para notificações de uma máquina: tarefas de backup, scripts de manutenção e processos demorados. Ele não substitui um serviço de alertas quando a máquina pode ficar indisponível, nem oferece histórico centralizado, entrega para celular ou garantia de recebimento externo.
As regras de MAILTO do cron estão documentadas no manual do crontab do Ubuntu. O manual básico do Postfix explica as decisões de relay e entrega local, e o manual do GNU Mailutils documenta outra implementação do comando mail.