Módulo 5 · Worker Service — Capítulo 05

Rodando como Serviço Windows / Daemon Linux

Um Worker Service precisa sobreviver a reinicializações do servidor e rodar sem ninguém logado — como transformar o processo num Serviço do Windows ou daemon systemd no Linux.

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:

C# Program.cs
var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddWindowsService(opcoes =>
{
    opcoes.ServiceName = "ProcessadorDePedidos";
});

builder.Services.AddHostedService<Worker>();

var host = builder.Build();
host.Run();
bash publicando e instalando o serviço
dotnet publish -c Release -o C:\Servicos\ProcessadorDePedidos

sc create ProcessadorDePedidos binPath="C:\Servicos\ProcessadorDePedidos\ProcessadorDePedidos.exe"
sc start ProcessadorDePedidos
Nota

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:

C# Program.cs
var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSystemd();
builder.Services.AddHostedService<Worker>();

var host = builder.Build();
host.Run();
bash /etc/systemd/system/processador-pedidos.service
[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
bash habilitando e iniciando
sudo systemctl daemon-reload
sudo systemctl enable processador-pedidos
sudo systemctl start processador-pedidos
sudo systemctl status processador-pedidos
ConfiguraçãoPapel
Restart=on-failureReinicia automaticamente se o processo cair com erro
WantedBy=multi-user.targetInicia junto com o boot do sistema, em modo multiusuário
Type=notifyO 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:

Dockerfile Dockerfile
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.

📌 Resumo do capítulo

  • AddWindowsService() adapta o host para rodar como Serviço do Windows, gerenciável via sc create/sc start.
  • AddSystemd() + um arquivo de unidade systemd fazem o equivalente no Linux, com Restart=on-failure garantindo resiliência.
  • Ambos os métodos de registro são seguros mesmo fora do contexto de serviço (não afetam dotnet run em desenvolvimento).
  • Contêineres Docker são uma alternativa comum, delegando o reinício em falha ao orquestrador.

✏️ Praticando

  1. Publique o worker do capítulo anterior e instale-o como Serviço do Windows (ou daemon systemd, se estiver em Linux/WSL), confirmando que ele inicia automaticamente.
  2. Force o processo a falhar (uma exceção não tratada fora do try/catch) e observe o comportamento de reinício configurado.