1. Por que não basta rodar dotnet run em produção
Rodar um worker manualmente num terminal funciona para testar, mas não sobrevive a um reboot do servidor, a um logout, ou a uma falha que derrube o processo. Para produção, o processo precisa ser gerenciado pelo próprio sistema operacional: iniciado automaticamente no boot, reiniciado se cair, com logs de sistema integrados.
2. Serviço do Windows
O pacote Microsoft.Extensions.Hosting.WindowsServices adapta o host
para rodar como um Serviço do Windows de verdade:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(opcoes =>
{
opcoes.ServiceName = "ProcessadorDePedidos";
});
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();
dotnet publish -c Release -o C:\Servicos\ProcessadorDePedidos
sc create ProcessadorDePedidos binPath="C:\Servicos\ProcessadorDePedidos\ProcessadorDePedidos.exe"
sc start ProcessadorDePedidos
AddWindowsService() não faz nada quando o processo roda fora do
contexto de Serviço do Windows (por exemplo, durante dotnet run
normal em desenvolvimento) — é seguro deixar essa linha sempre presente, ela só ativa o
comportamento especial quando de fato registrado como serviço via sc create.
3. Daemon systemd no Linux
O equivalente Linux usa Microsoft.Extensions.Hosting.Systemd e um
arquivo de unidade systemd:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSystemd();
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();
[Unit]
Description=Processador de Pedidos
[Service]
Type=notify
ExecStart=/var/servicos/processador-pedidos/ProcessadorDePedidos
Restart=on-failure
User=www-data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable processador-pedidos
sudo systemctl start processador-pedidos
sudo systemctl status processador-pedidos
| Configuração | Papel |
|---|---|
Restart=on-failure | Reinicia automaticamente se o processo cair com erro |
WantedBy=multi-user.target | Inicia junto com o boot do sistema, em modo multiusuário |
Type=notify | O processo avisa o systemd quando terminou de inicializar (suportado nativamente pelo host .NET) |
4. Alternativa: contêiner Docker
Uma alternativa cada vez mais comum a instalar diretamente no SO é empacotar o worker como imagem Docker, deixando o orquestrador (Docker Compose, Kubernetes) responsável por reiniciar o processo em caso de falha:
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
WORKDIR /app
COPY bin/Release/net8.0/publish/ .
ENTRYPOINT ["dotnet", "ProcessadorDePedidos.dll"]
5. Onde isso te leva
Com o worker rodando de forma confiável em produção, o último capítulo deste módulo junta tudo num projeto prático: um worker completo processando pedidos pendentes das procedures do Módulo 2, com escopo de DI correto, logging estruturado e agendamento.