One approach I've used for running arbitrary code, with arbitrary dependencies, without having to rely on much support from the host system, is to create an Arx file (a shell snippet followed by tar gz data, which is self-extracting/executing) using `nix bundle`. On one hand it feels like a "poor man's container image"; but it's better in some ways since it doesn't need any daemons, etc. to be running.
For example, we had an EC2 machine whose init would fetch and run a shell script from S3, which spun up a big JVM application. We started getting strange errors from deep inside the JVM, which seemed to be caused by some security update somewhere in the stack (either ours or Amazon's); but that machine image had evolved via snapshots of snapshots, etc. and was a pain to figure out what it was running, let alone update and manage it in a sane way. As a quick fix I bundled a known-good JVM into an Arx file (along with other dependencies like Bash, etc. since it's Nix, so I might as well!) and put that on S3 in place of the old shell script. That got the EC2 machines working again, without having to mess with the (already messy) image; we gained a more sensible way of specifying dependencies (via Nix); and, since we were now building an artifact, rather than slinging a raw script, we could do stuff like running Shellcheck on the code. This definition was also easy to re-use when we eventually switched it to ECS containers (just changing the `nix bundle` options).
For example, we had an EC2 machine whose init would fetch and run a shell script from S3, which spun up a big JVM application. We started getting strange errors from deep inside the JVM, which seemed to be caused by some security update somewhere in the stack (either ours or Amazon's); but that machine image had evolved via snapshots of snapshots, etc. and was a pain to figure out what it was running, let alone update and manage it in a sane way. As a quick fix I bundled a known-good JVM into an Arx file (along with other dependencies like Bash, etc. since it's Nix, so I might as well!) and put that on S3 in place of the old shell script. That got the EC2 machines working again, without having to mess with the (already messy) image; we gained a more sensible way of specifying dependencies (via Nix); and, since we were now building an artifact, rather than slinging a raw script, we could do stuff like running Shellcheck on the code. This definition was also easy to re-use when we eventually switched it to ECS containers (just changing the `nix bundle` options).