Counterpoint: there is a niche in the industry where going with containers would be easier than following the best practices without, given how likely people are to slip into doing things the lazy way, or due to how you might have limited control over the applications that you might have to run. Containers at least limit the fallout from various mistakes and nudges people towards certain architectural choices - e.g. 12 Factor Apps, so you don't cripple your app into being a "singleton app" which can only have 1 instance running, instead of just a "monolith".

It doesn't matter whether you're running Podman, Docker, Docker Compose, Docker Swarm, Hashicorp Nomad, K3s/K0s/minikube or a full K8s distro like RKE, the OCI standard is a hill that I'll die on because unless you're the person who's shipping and managing their code 100% of the time, someone else will have to figure out how to setup and run your Java/.NET/Node/Python/PHP app and moreso, what to do when one needs JDK 8, another needs JDK 11 and there are inevitable conflicts in some other system/runtime dependencies. With containers, you can limit the the impact of relatively badly written and sometimes even somewhat insecure applications, by not exposing the rest of the underlying host to them, especially when running rootless or with host remapping.

Sure, in practice it would be exceedingly nice to just deploy a single packaged executable to the server. It would be really nice to even have the choice of picking a technology that plays nicely with such simple setups, like Go, as opposed to having Java shoved down your throat, especially in combination with a separately managed Tomcat instance or something. It would also be nice to have well written applications that don't misbehave and don't trash the CPU/RAM and don't cause the server to have to OOM kill processes, and also that have proper port configuration/remapping available and also applications that have proper health checks built in, or even just well written systemd services and Ansible to manage the configuration.

But in the real world you don't always get that. So unless you like moving around different companies a lot, you need ways to manage the fallout of sub-optimal circumstances (especially legacy code that still needs to run) and containers are one way to achieve that. I wrote down some of my experiences with the subject (albeit with details changed) in my blog post "My journey from ad hoc chaos to order (a tale of legacy code, services and containers)": https://blog.kronis.dev/articles/my-journey-from-ad-hoc-chao...

That said, developing applications poorly will also mean running them poorly on Kubernetes, or not being able to set the environments up at all. Thus, I believe that it's a slippery slope with options on all ends of it being bad, be it running things directly on the server and running into the aforementioned issues, or trying to go full steam ahead with Kubernetes and failing.

The sweet spot in my experience is using containers, but in a simple setup - currently that is Docker Compose or Docker Swarm, but might become Nomad in the future. Maybe K3s in 5-10 years (single node cluster worked fine, except for RPM distros where cgroups v2 support had problems and the Traefik proxy had bad documentation for wildcard SSL/TLS certificates). Then you no longer need to even care that much about the specifics of the technologies inside of the containers either.

Edit: As an example, here's all that I (or anyone else) would need to deploy my homepage, as long as the server can run OCI images or Compose/Swarm stacks:

version: '3.7'
services:
homepage:
image: REFERENCE_TO_MY_CONTAINER_IMAGE
restart: "unless-stopped"
environment:
# All of the configuration goes here, for example:
ANALYTICS_URL: LINK_TO_MY_MATOMO_INSTANCE_GOES_HERE
ANALYTICS_ID: SOME_IDENTIFIER_SET_UP_IN_MATOMO
volumes:
- SOME_DIRECTORY/homepage/app/storage:/app/storage
- SOME_DIRECTORY/homepage/app/db/files:/app/db/files
networks:
- prod_ingress_network
deploy:
mode: replicated
replicas: 2
update_config:
parallelism: 1
delay: 10s
monitor: 10s
placement:
constraints:
- node.hostname == SERVER_HOSTNAME_GOES_HERE
resources:
limits:
cpus: "0.5"
memory: "512M"

networks:
prod_ingress_network:
driver: overlay
attachable: true
external: true

And I no longer have to worry about systemd slices, configuration files, file permissions (for the most part), Ruby or Rails versions, or install any other stuff on the server itself directly. I can just run the image and if it doesn't work, I can just let the developers know what to fix (though in this case I am the developer myself). And if it goes rogue and eats too much resources? The runtime will cap the container without it affecting the rest of the system outside of resource limits that I've set.