Tradicionalmente, el software se ha creado para ser ejecutado en una máquina física. Pero cuando lo hacemos nos topamos rápidamente con un muro. El software solo puede ejecutarse en un determinado hardware. Requiere, por ejemplo, un procesador concreto.
Además, el software más complejo no suele funcionar de forma completamente autónoma, sino que está integrado en un ecosistema de software. Este incluye un sistema operativo, bibliotecas y dependencias. Para que todos los componentes interactúen correctamente, deben estar disponibles las versiones adecuadas. También hay una configuración que describe cómo se vinculan los componentes individuales entre sí.
Si quieres ejecutar varias aplicaciones en una máquina en paralelo, rápidamente surgen conflictos de versiones. Una aplicación puede necesitar una versión de un componente que sea incompatible con otra aplicación. En el peor de los casos, cada aplicación tendría que ejecutarse en su propia máquina física. Lo cierto es que las máquinas físicas son caras y no se pueden escalar fácilmente. Así que si los requisitos de recursos de una aplicación crecen, puede ser necesario migrar a una nueva máquina física.
Otro problema surge del hecho de que el software en desarrollo se utiliza en diferentes entornos. Un desarrollador escribe el código en el sistema local y lo ejecuta allí para probarlo. La aplicación pasa por varias etapas de prueba antes de pasar a producción, incluyendo un entorno de prueba para garantizar la calidad o un entorno de ensayo para que el equipo del producto lo pruebe.