[Unit] Description={{.DisplayName}} Scanner (Vulnerability Scanning) After=network-online.target Wants=network-online.target [Service] Type=simple User={{.User}} Group={{.User}} ExecStart={{.BinaryPath}} serve --config {{.ConfigPath}} Restart=on-failure RestartSec=10 # The scanner runs on its own server, so it is the only large process on the # host and the limits below are sized to the box rather than to a neighbour. # # GOMEMLIMIT is the Go soft limit: the runtime collects harder as the heap # approaches it and never fails an allocation to honour it, so a large enough # image walks straight through. It sits below MemoryHigh so the GC gets a # chance before the kernel starts reclaiming. Without the variable the scanner # defaults itself to 512 MiB (cmd/scanner/main.go), which on a 4 GB host only # wastes CPU on collection. Environment=GOMEMLIMIT={{.GoMemLimit}} # MemoryHigh throttles and reclaims; MemoryMax kills. MemorySwapMax=0 is the # one that matters: with swap available a scanner over its cap is not killed, # it is paged out, and the host thrashes instead of failing. That is what took # the shared hold host down for eleven hours on 2026-09-12 (644 MiB resident, # 1.9 GB swapped). Refusing swap turns that into an OOM kill of one job and a # clean restart ten seconds later. MemoryHigh={{.MemoryHigh}} MemoryMax={{.MemoryMax}} MemorySwapMax=0 ReadWritePaths={{.DataDir}} ProtectSystem=strict ProtectHome=yes NoNewPrivileges=yes PrivateTmp=yes StandardOutput=journal StandardError=journal SyslogIdentifier={{.ServiceName}} [Install] WantedBy=multi-user.target