Virtual Machines (VMs) and containers are two different ways to isolate workloads on a single physical host. A VM virtualizes the hardware: the hypervisor emulates a full machine, and each VM runs its own operating-system kernel, giving strong isolation at the cost of seconds-to-minutes of boot time and hundreds of MB of RAM overhead per instance. A container shares the host kernel and isolates only the process tree, filesystem, network namespace, and resource limits via cgroups and namespaces — boot time drops to milliseconds and memory overhead to a few MB. Containers (Docker, Podman, containerd) are the right unit for application packaging; VMs remain the right unit for running mutually distrusting tenants on shared hardware.
In a self-hosting context
Almost every self-hostable SaaS replacement in this directory ships as a container image — Mattermost, Nextcloud, Gitea, Plausible Analytics — because container packaging eliminates the "works on my machine" gap. Running them inside a VM (your VPS) gives you the security boundary against other tenants and the upgrade safety of full snapshotting; running each app as a container inside that VM gives you the upgrade-by-image-tag ergonomics. See Docker Compose for the operator-side glue.