1) Use systemd; I've taken the time to learn it and the tooling it exposes for codifying process dependencies, how weak or strong they should be, and restart behaviors is thoroughly designed
2) Use Erlang/OTP - I went this route for an IoT product I worked on that used Erlang as the primary high-level "firmware", I codified the process tree and dependencies and custom supervisor restart behaviors for unix processes into Erlang/OTP's supervision tree; using Erlang process ports
The second approach has worked extremely well for me in the past but I would only recommend it if you're going to use a lot of Erlang to process output from the programs it spawns for you, which was the case for how I was using it at the time, the robust supervision of the processes it managed in a supervision tree was incidental to the primary use but extremely robust.
One of the earliest was the System Resource Controller in IBM AIX 3.2 in 1992. Later that same decade, in the BSD and Linux worlds, came Daniel J. Bernstein's daemontools. A whole family of toolsets grew from that design, including Bruce Guenter's daemontools-encore, Gerrit Pape's runit, Laurent Bercot's s6, Wayne Marshall's perp, Adam Sampson's freedt, and nosh. There are also upstart, Linux systemd, MacOS launchd, and the Solaris Service Management Facility.