I create a separate user for each app, and use the systemd exec configuration [1] for sandboxing [2]. Some apps only get read-only access to their own files, and no Internet access, for example (along with many other restrictions). I have some systemd drop-in units that I frequently reuse.
For standard services, I use Apparmor with the default `apparmor-profiles`, as well as fail2ban with some additional firewall rules.