The Cost of Abstraction
Every container runs its own init process, network stack, and filesystem layer. On a 12GB VPS, these resources add up quickly. When you run five or six containers, the kernel is managing dozens of namespaces and virtual network interfaces. This creates contention for CPU cycles and memory pages. Processes on one box compete for memory, and adding a layer of abstraction means more context switches. If your workload is light, like a static site or a small API, the container overhead can consume a noticeable percentage of your available resources. You are paying for isolation you rarely need. The disk usage also grows because each image layer is stored separately, and pruning them requires active management. On a 193GB disk, this is manageable, but it is still wasted space compared to a native binary.
When Isolation Actually Helps
Isolation earns its keep when a service has complex dependencies or requires a specific runtime version that conflicts with the host. If you need Python 3.8 for one tool and Python 3.11 for another, containers keep them separate. This prevents dependency hell. It also simplifies deployment. You build an image once, and it runs identically on any machine. For a solo developer, this reduces the "it works on my machine" problem. However, for simple services like a Node.js app or a PostgreSQL database, the host environment is usually stable enough. You can pin versions in your package manager or use native version managers. The complexity of managing Docker Compose files, volumes, and networks often exceeds the benefit of the isolation itself. If your services are well-behaved and do not require exotic libraries, native execution is faster and lighter.
The Systemd Alternative
Systemd is the default init system on most modern Linux distributions. It handles process supervision, logging, and service dependencies natively. Instead of a Docker container, you define a unit file. This file tells systemd how to start, stop, and restart your service. It also handles logging via the journal, which is searchable and persistent. For scheduled tasks, systemd timers replace cron. This is cleaner because the timer and the service are linked. If the service fails, the timer logs the error. On our VPS, we run a Next.js process that serves four sites by host-based routing. We also run a separate Node process for an Instagram scheduling tool on port 3050. Both run as native systemd services. There is no Docker daemon running in the background. The memory footprint is lower, and the boot time is faster. Debugging is simpler because you see the exact process ID and resource usage directly in the OS.
Choosing the Right Tool
The decision depends on your specific stack. If you are running a simple web app, a database, and a few background workers, systemd is likely the better choice. It is lighter, faster, and easier to manage. If you are running a complex microservices architecture with many different languages and versions, Docker might be worth the overhead. For an independent studio running a film publication, a wallpaper app, and a few other tools, the simpler approach wins. We serve static builds for our highlight tool and invoice generator directly from disk via Caddy. These have no server process and no database. They do not need containers. The dynamic services run as native processes. This setup is stable, efficient, and easy to maintain. You do not need to learn Docker to run a small production environment. You need to understand your processes and how they interact. Systemd gives you that control without the abstraction layer. Check out
PracandyFlix to see what runs on this infrastructure.