I wish posix_spawn were ubiquitous; it's a much better process-launching interface than fork: it's naturally race-free and amenable to use in multi-threaded programs, and unlike fork(2), it plays well with turning VM overcommit off. (If overcommit is off and a large process forks, the system must assume that every COW page could be made process-private and reserve that much memory. Ouch.)
Unfortunately, posix_spawn is woefully underpowered. I can't make the child process a session leader (setsid) or process group leader (setpgrp). I can't set a working directory. Etcetera.
The role of posix_spawn is for spawning "helper processes", not starting new process groups. This should overwhelming be your common launch case. posix_spawn is so much faster (on BSD/Mac anyway) that fork should be outright avoided.
Processes requiring other permissions can/should be spawned by asking systemd/chron/init/launchd to launch them for you.
And extensions to let us specify failure-case or other behavior of those extensions, and so on. But at that point, we're already heading down the road of implementing a tiny DSL for "the program that posix_spawn() should run after creating the new process but before exec()ing the new executable". Why not simply write that code in the host language? You could specify a thunk of code to be sent to the new process and executed there. Oh, you'll also need to pass any data structures that code relies on--- pass a closure, not just a thunk. And garbage-collected references to any system objects that those data structures rely on. Congratulations, now you have fork()! If you squint a little, that's exactly what fork() provides you --- a closure and continuation.
Yes, you end up with a tiny DSL for specifying transformations to make to a child process: they key difference is that the kernel can execute this DSL much more efficiently than it can code written in the host language: fork closes over the entire world, and posix_spawn doesn't have to do that.
Even if VM overcommit is enabled, you shouldn't be forking from large processes, because the necessary kernel VM manipulation will kill your performance.
It's portable enough: Linux, Darwin, and the BSDs all support it. Darwin also supports posix_spawn natively. Even Cygwin supports it, although Cygwin's vfork is currently just an alias for fork.
I seem to remember Cygwin having all sorts of weird problems with fork behaviour, but I'm no C programmer let alone doing stuff on Windows, so I might be remembering incorrectly