There is nothing declarative about Dockerfiles, they are a list of imperative steps. Same for ansible playbooks, which are also often called declarative when they really aren't.
There is nothing declarative about Dockerfiles, they are a list of imperative steps. Same for ansible playbooks, which are also often called declarative when they really aren't.
But on the abstraction level of configuring a Linux system this imperative nature can be completely hidden, as demonstrated by NixOS. E.g. you can set `services.uptime-kuma.enable = true` in a NixOS configuration and it will fetch the package, setup a systemd service for it, setup a service user, and enable and start the service, without you having to care about any kind of ordering or the specific steps themselves. You can do the same for other services, and it doesn't matter in which order they are declared.
Of course someone has to first build the abstraction, which is service-specific most of the time. Though, NixOS provides lower-level options to define these service abstractions declaratively as well (think: declaring that a user account is present, or that a systemd service is setup with some configuration), so most of the time you still don't reach the imperative base underlying NixOS.
I don't think that is the case. In five years of using NixOS I never needed to reach for any of them, and I don't even know what would be available to me. Probably something about activation scripts? Again, I can't think of anything you might want to do with regard to system configuration that can't be done declaratively on NixOS.
I've also introduced some light NixOS usage at work (3 hosts, one is an uptime-kuma instance, two are Forgejo Actions runners). For that I had to get some proprietary scanner software to run on it, which I could by just putting the extracted deb package in an emulated FHS environment and setting up a service for it, all declaratively.
Even for interfacing with legacy systems and unusual stacks I don't think you will need the escape hatch. Anything that is buildable on and above the abstraction-level of "ensure a file is present at some path with some content" should be doable declaratively, and that includes setting up an unusual software stack and running it in systemd services to communicate with some other legacy system or whatever.
The escape hatch is there to modify how NixOS itself behaves, and modifying that should only be necessary to extend NixOS' core functionality. A quick search revealed that impermanence (https://github.com/nix-community/impermanence) and in some cases sops-nix (https://github.com/Mic92/sops-nix) use it, but those fundamentally extend NixOS with ephemeral root storage support and secrets management, respectively.