The history of application development is also the history of packaging these applications for different platforms and operating systems.
To run an application, specific requirements need to be fulfilled, such as installing and configuring the basic operating system, core Python packages, Python extensions, configuring networking, and connecting to third-party systems like databases and storage.
While the developer knows the application and its dependencies best, a system administrator usually provides the infrastructure and configures the system.
This process can be error-prone and hard to maintain, so servers are often configured for a single purpose and connected by network.
Virtual machines can be used to emulate a full server, allowing multiple isolated servers to run on the same hardware.
Containers solve the problems of managing application dependencies and running more efficiently than virtual machines, as they do not require running a whole operating system including the kernel.