Containers provide a bit more isolation between applications and users, which provides some security benefits.
It is one approach to the “it works on my machine” problem. With a container, the app developer ships their exact environment (versions of the language and library dependencies, etc. exactly as tested by the dev). If the app developer ships a high-quality container, the likelihood of something going wrong with your installation of that app will be lower, because the system it runs in is not changing; the dev ships their system, and that’s what the app uses.
It reduces our workload when supporting a new application. A recipe is generally much smaller than an app installer, because we’re not solving the dependency problems. We’re just grabbing the developer’s container(s) and proxying to them. There’s a reason Ilia has been able to add support for well over 100 applications, when it took us a much longer time to get to 100 app installers. And, they will likely not corrode as rapidly. When the upstream makes changes that effect deployment, it’s probably something in the container they ship, and not something our stack needs to address. A container is a black box, where we only care about the inputs and outputs. A web app installer has to care what’s in the box. The complexity is still there, we’re just not the ones handling it.
The cons of containers: Much heavier. A container deployment will consume an order of magnitude more disk space, take longer to install, and will run its own versions of everything with much less resource sharing. While a hundred PHP applications running under PHP-FPM will share huge swaths of memory across all of them (assuming all of them are on the same versions of everything, including PHP version), a hundred containerized installations of PHP apps will share quite a lot less.
With Linux page sharing, it won’t be the “no sharing” that seems intuitively what has to happen with isolated containers, it still shares less, maybe even close to zero, depending on how many identical pages there are. It’s definitely less than in a non-containerized deployment. Every containerized app may use a different PHP and library version (what’s in the black box, you can’t readily control), and if so, the amount of sharing across those apps may be close to zero. In the usual case for non-containerized apps, you’ll have one PHP version and roughly the same libs across all your apps, lots of opportunities for shared memory. This also effects performance. There’s more CPU cache hits when everybody is using the same pages in memory.
And, you may not be able to tell very easily how much difference there is between a container deployment and a regular app running under PHP-FPM (or a Python app server or Nodejs or whatever). The memory usage you see in top and the process list module in Webmin doesn’t make it easy to tell what’s shared. 50 PHP-FPM processes will mostly be shared memory, but will look like 50 processes consuming 20MB each RES (and much more VIRT); you paid the full price for the first app running under PHP-FPM, and now each extra one using the same version and libs will consume far less, even if the app is a different one. 50 containerized apps will look like all of their component parts running in their own namespaces (database, app server, the language interpreter, maybe redis, etc.). Maybe none of it is shared with other users processes, maybe some of it is (50 identical app containers running the same app will still be able to share a bunch of memory…but 50 different PHP apps probably won’t).