Both approaches run your apps. The difference shows up three months later — when you update, migrate, or try to remember how anything works.

The traditional way, honestly assessed

Installing directly on the server — apt packages, tarballs, services — is how servers were run for decades, and it still works. For one single-purpose machine, it can even be the simpler option: no extra layer to learn, no images to pull. The trouble starts when the machine hosts more than one thing. Python app wants one library version, the other app wants another; a package upgrade quietly changes a config format; uninstalling leaves files everywhere. The system slowly becomes a snowflake that nobody can rebuild.

What Compose changes

Compose moves each app into its own container with its dependencies pinned, and describes the whole arrangement in a file you can read:

  • Isolation. Apps can't break each other's dependencies. Update one freely.
  • Documentation as code. The compose file is the install instructions. Six months later, you read the file and know exactly what's running.
  • Disposable infrastructure. Containers are recreated, not repaired. Data lives in named volumes, so rebuilding never touches it.
  • Reproducibility. The same file brings up the same stack on a new server in minutes. That's the difference between a backup and a real disaster-recovery plan.

Where containers genuinely don't help

Fairness matters here. Containers add a layer of concepts — images, volumes, networks — that you must learn. For a single static site or one simple service, that overhead buys you little. GPU-heavy or kernel-level workloads can also be awkward in containers. And a badly written compose file (root-owned volumes, latest tags everywhere, secrets in plain text) recreates every bad habit in a new syntax.

The dividing line in practice: the moment you want a second service on the same server, Compose pays for itself. One app, direct install is fine. Two or more, containerize.

The verdict for self-hosters

Nearly every modern self-hosting project ships a compose file, which means the community's install instructions assume that path. Following the crowd here isn't laziness — it's compatibility. You copy the project's file, adjust a few values, and docker compose up -d. The Docker Compose guide takes you from zero to a running app with exactly that pattern.