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 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.