I serve HTTP 403 for all requests to the default vhost and log them, harvest IPs through a log aggregator (or just fail2ban) and tag them as bad bots/scanners, and eternal-ban them on all my hosts. Currently have 98451 addresses or networks in my ipset for these.
For requests to actual domains, I ban after a few unsuccessful authentication attempts. A WAF is nice to have (tedious but fun to set up) - currently working on improving my Modsecurity setup.
Other than that there is already good advice here:
keep OS/packages/installed services up-to-date
only run software from trusted (ideally signed) sources
use host and network-based firewalls
use strong encryption and authentication everywhere
only expose what is absolutely required
implement good privilege separation (even dedicated users for each app/service, proper file ownership/permissions goes a long way)
run scanners to detect possible misconfigurations/hardening measures (systemd-analyze security was mentioned, I also like lynis and debsecan)