Each of those has tradeoffs compared to Dockerfiles (I have no need for bazel, but if I did, then adding `rules_oci` might be a win-win, rather than using a Dockerfile). If I used Nix, then the Nix dockerTools would be a huge win (I don't use Nix). If I were shipping Go programs, `ko` would likely be a good baseline.
Buildah is the only serious alternative in my opinion.
You lose automatic layer caching, but in exchange you can use the same tools (RUN, ADD, etc) within a much more powerful shell environment.
I wrote a Buildah wrapper that uses a shell script harness to polyfill the familiar Dockerfile syntax while adding several extra goodies - mainly the ability to bake runtime arguments (mounts, ports...) into the image. Very handy!
Buildah's ability to mount the container in an unshare environment is pretty magical for copying stuff in and out of it.
That said, in the end I'd still rather build containers with something other than an imperative sequence of commands, so my heart is going to be forever with nix2container and bazel's rules_oci.
Dockerfiles is the language for specifying a set of instructions for building container images.