/
Go back

Docker From Start To Finish ( Part - 01 )

Understanding the Limitations of Physical Servers and Virtual Machines.

A world map with performance graphs overlay.

If I asked you, “How would you host an application you built so that users around the world can access it?”, most developers would probably think of a cloud provider such as AWS, Microsoft Azure, Google Cloud Platform, or Vercel. Others, depending on the use case, might host their application on a physical machine they own.

As developers, our goal is not just to get an application running once. We need to run it consistently across local development, staging, and production. We need to deploy updates safely, roll back when something breaks, isolate services from one another, monitor failures, and scale the system when traffic increases.

Regardless of the deployment strategy, an application has to run somewhere. Over the years, the software industry has developed several approaches to make application deployment more reliable, scalable, and repeatable.

What Is a Server?

Before discussing deployment strategies, we should define what a server means in this context.

In deployment, a server is a physical or virtual machine that runs one or more services, such as a web application, database, reverse proxy, cache, or background worker, and makes those services available to clients or other systems.

More generally, a server can also refer to software that provides a service, such as a web server or database server. In this article, however, I am using “server” to mean the machine that runs application services.

Physical Hardware Always Exists

Regardless of the deployment route, computation eventually runs on physical hardware. The difference is the level of control you have over the machine running your application code.

When you use a cloud provider, your control is limited to the abstractions that provider gives you. You may not manage the physical hardware directly, but you also avoid much of the operational burden of maintaining it.

When you use your own physical machine, you get more direct control over the hardware. However, you also become responsible for infrastructure problems that a cloud provider would normally handle for you, such as hardware maintenance, networking, power, cooling, repairs, monitoring, backups, and replacement.

Resource Utilization and Contention

No matter what kind of server you choose, there are trade-offs.

If you dedicate an entire machine to a single application, you reduce the chance of resource contention because no other application is competing for CPU, memory, disk, or network bandwidth. However, the operating system and background services still consume some resources.

This approach can become expensive. You have to consider whether the benefits of dedicated equipment are worth the cost of buying, running, and maintaining that hardware.

Most applications rarely use 100% of a machine’s resources all the time. To use a server more efficiently, you may want to run multiple applications on the same machine.

The problem is that each application may have different dependency, hardware, and runtime requirements.

For example:

App A requires Python 3.10, while App B requires Python 3.12.
App A needs one version of OpenSSL, while App B needs another.
App A crashes and consumes all available CPU.
App A fills the disk with logs.
App A exposes a port that conflicts with App B.
A system-level configuration change for one app breaks another app.

Shared infrastructure such as databases, queues, file systems, and caches can also become bottlenecks or coupling points between applications.

This creates an important question:

How can we share physical resources efficiently while still isolating applications from one another?

The Scaling Problem

There is also the issue of scaling. A single machine, no matter how powerful, has a limit. If traffic increases or the application becomes more computationally expensive, the server may eventually run out of CPU, memory, disk throughput, or network bandwidth.

At that point, there are two broad scaling strategies.

Vertical Scaling

Vertical scaling means increasing the resources of the existing machine. This could mean adding more CPU cores, more memory, faster storage, or better networking.

Vertical scaling is simple at first because the application can usually continue running on one machine. However, every machine has an upper limit.

Horizontal Scaling

Horizontal scaling means adding more machines and distributing work across them.

This gives the system more room to grow, but it also introduces new problems, such as load balancing, networking, service discovery, shared state, database bottlenecks, deployment coordination, monitoring, and failure recovery.

This is one reason infrastructure becomes more complex as applications grow. The problem is no longer just:

How do I run my application?

It becomes:

How do I run many copies of my application reliably across many machines?

Virtualization

Early virtualization work emerged from IBM mainframe time-sharing research in the 1960s. The core idea was to make better use of physical hardware by dividing it into multiple isolated computing environments.

Virtualization lets us divide the resources of a physical machine into multiple isolated virtual machines. Each virtual machine receives its own allocation of CPU, memory, storage, and networking while still sharing the same underlying hardware.

Virtualization adds a hypervisor layer that presents each virtual machine with virtualized hardware, such as virtual CPUs, virtual memory, virtual disks, and virtual network interfaces.

Each virtual machine behaves like an independent machine, even though it is sharing the same physical host.

Types of Hypervisors

Hypervisors can run at different levels of the system.

Type 1 Hypervisors

Type 1 hypervisors, also called bare-metal hypervisors, run directly on the physical hardware without needing an underlying host operating system.

They are commonly used in enterprise and cloud environments because they usually provide better performance, isolation, and efficiency.

Type 2 Hypervisors

Type 2 hypervisors, also called hosted hypervisors, run on top of a host operating system as regular applications.

They are convenient for personal development machines because they are easier to install and use. Tools like VirtualBox and VMware Workstation are common examples of this model.

Why Virtual Machines Were Useful

Virtual machines were revolutionary and are still widely used across many areas, including cloud hosting, cybersecurity, testing, and enterprise infrastructure.

They provide stronger isolation than running multiple applications directly on the same host. Each VM has its own guest operating system, file system, network configuration, and process space.

VMs also make it possible to capture a machine’s state using snapshots or images. This allows teams to recreate a machine later or move it to another compatible environment.

However, VMs also introduce new problems.

Limitation 1: Portability

VMs are not always easy to move between environments.

A VM may depend on a compatible hypervisor, CPU architecture, drivers, networking setup, storage configuration, disk format, and cloud-specific metadata.

Because of this, a VM image created for one environment may not run cleanly in another without conversion or reconfiguration.

Limitation 2: Guest OS Overhead

To provide strong isolation, each VM includes its own guest operating system.

This is useful because each application can have its own machine-like environment. However, it also means that every VM carries the overhead of a full operating system.

If several VMs are running the same operating system on the same physical host, each VM still has its own copy of that guest OS. This leads to duplicated system files, duplicated background services, and higher resource usage.

Limitation 3: Large Images and Snapshots

Because a VM includes the operating system, application dependencies, configuration, and machine state, VM images and snapshots can become very large.

This makes them slower to create, store, transfer, and version compared to lighter deployment artifacts.

VMs give each application its own machine-like environment, which is powerful. However, they do not automatically give developers a clean, lightweight, versioned package for the application itself. The application is bundled together with the whole machine environment.

Limitation 4: Snowflake Servers and Environment Drift

There is also the issue of snowflake servers.

A snowflake server is a machine that has been manually changed so many times that nobody can recreate it exactly.

For example, someone in an organization might SSH into a server, install a package, edit a configuration file, change an environment variable, or patch a dependency. If those changes are not documented or automated, the server slowly drifts away from other environments that are supposed to be identical.

This can cause production, staging, and development environments to behave differently.

A server that is changed over time is known as a mutable server. Mutable servers are not always bad, but they become risky when changes are manual, undocumented, and hard to reproduce.

Why This Leads to Containers

Given the scale of modern software, teams have moved toward repeatable build artifacts, Dockerfiles, infrastructure-as-code tools like Terraform, configuration management tools like Ansible, CI/CD pipelines, and immutable deployment workflows.

The goal is to make environments more reproducible, reliable, and resistant to human error.

Physical servers give us direct hardware control.

Virtual machines give us machine-level isolation and allow multiple workloads to share the same physical hardware more safely.

However, VMs still package an entire operating system with each workload. They are heavier than necessary when the main thing we want to ship is the application, its dependencies, and the command needed to run it.

This is where containers come in.

Containers do not completely replace VMs. In fact, many containers still run on top of VMs in cloud environments. But containers solve a different problem: they make application packaging, deployment, and runtime consistency easier across compatible environments.

That will be the topic of Part 2.

ILY.

Related posts