I'll provide the article directly for you:
Running Multiple Applications on One VPS: Container Orchestration Without Kubernetes
When you're running a startup or small business with modest traffic, deploying multiple applications to a single VPS is a practical reality. You might have a main web application, a separate API service, a background job processor, and monitoring tools—all needing to coexist peacefully on the same server. While Kubernetes is the industry standard for container orchestration, it's often overkill for deployments under 10,000 requests per second or without complex infrastructure requirements. The good news: you have several proven alternatives that are simpler to manage, cheaper to run, and perfectly adequate for most real-world scenarios.
This guide walks through practical orchestration approaches for running multiple containerized applications on a single VPS, with honest assessments of tradeoffs, real pricing considerations, and deployment patterns you can implement today.
Why Not Just Run Everything Directly?
Before diving into container solutions, let's acknowledge the naive approach: running applications natively on the host OS with systemd or manual process management. This works until it doesn't. You lose isolation between applications—a memory leak in one can starve others. Dependency conflicts become nightmares. Scaling, even to multiple servers, requires rebuilding everything. Containers solve these problems by packaging each application with its exact dependencies, ensuring consistency from development to production.
The question isn't "containers or no containers"—it's "how much orchestration do we need to manage them effectively?"
Docker Compose: The Sweet Spot for Small Deployments
For most VPS deployments with 2-5 applications, Docker Compose is the practical choice. It's simple enough to understand in an afternoon but powerful enough to handle real-world complexity.
How Docker Compose Works
Docker Compose defines your entire application stack in a single docker-compose.yml file. Each service gets its own container, with networking, volume management, and environment variables declared declaratively. When you run docker-compose up -d, everything starts in the correct order with the correct configuration.
Here's a realistic example: a Node.js web app, a PostgreSQL database, and a Redis cache:
version: '3.8'
services:
web:
image: myapp:latest
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
restart: unless-stopped
volumes:
- ./logs:/app/logs
db:
image: postgres:15-alpine
environment:
- POSTGRES_PASSWORD=secure_password
- POSTGRES_DB=myapp
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
cache:
image: redis:7-alpine
restart: unless-stopped
volumes:
db_data:
Deploy this once to your VPS, and it's reproducible forever. The same stack runs identically on your laptop, a colleague's machine, and production.
Real-World Considerations
Networking: By default, services communicate via hostnames matching their service names. web talks to db simply by connecting to postgres://db:5432. No port forwarding nightmares.
Persistence: Volumes (db_data in the example) ensure data survives container restarts. Named volumes are managed by Docker and persisted in /var/lib/docker/volumes/.
Restarts: The restart: unless-stopped policy keeps services alive if they crash, but respects manual stops. This prevents cascading failures.
Limits: Docker Compose doesn't include built-in resource limits. If one container consumes all available RAM, others suffer. You'll need to add deploy.resources.limits (requires Docker Swarm mode) or manage this manually via systemd service wrapper.
Alternative: Systemd Service Units + Docker Run
If Docker Compose feels like too much abstraction, you can manage individual containers with systemd—the init system on every modern Linux VPS. This hybrid approach gives you the isolation of containers with the direct control of systemd.
Create a systemd service file for each application:
# /etc/systemd/system/myapp-web.service
[Unit]
Description=MyApp Web Service
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/bin/docker run --rm \
--name myapp-web \
-p 3000:3000 \
-e DATABASE_URL=postgres://user:pass@myapp-db:5432/myapp \
--network myapp-network \
--restart=unless-stopped \
myapp:latest
ExecStop=/usr/bin/docker stop myapp-web
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Then: systemctl enable myapp-web && systemctl start myapp-web
Advantages: Direct systemd integration means journalctl -u myapp-web shows logs. Systemd handles restarts, dependencies, and ordering. You can set memory/CPU limits directly in the service file with MemoryLimit=512M.
Disadvantages: No automatic networking between services—you must create a Docker bridge network explicitly. Configuration is more verbose. Secrets management (passwords, API keys) requires additional tooling.
Orchestration Approaches Comparison
| Approach | Best For | Setup Time | Learning Curve | Resource Overhead | Scaling |
|---|---|---|---|---|---|
| Docker Compose | 2-5 services, development-like workflow | 30 min | Low | < 50MB | Manual (copy to another server) |
| Systemd + Docker | Mixed containers + native services | 1 hour | Medium | < 20MB | Manual (copy service files) |
| Docker Swarm | 2-10 servers, light orchestration | 2 hours | Medium | 100-200MB | Built-in (swarm mode) |
| Nomad (HashiCorp) | Complex multi-app, multi-server | 4+ hours | High | 200-300MB | Excellent, declarative |
| Kubernetes | High availability, complex networking | Days | High | 500MB+ per node | Excellent, but overkill for most |
Resource Management and Monitoring
A VPS with 2GB RAM hosting three containerized applications needs careful resource planning.
Setting Limits
With Docker Compose:
services:
web:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Limits are hard caps—the container is killed if it exceeds them. Reservations are soft guarantees; Docker tries to provide at least this much, but allows oversubscription if other containers aren't using their allocation.
Monitoring Stack
For a single VPS, a lightweight monitoring solution suffices:
- Prometheus (pulls metrics every 15 seconds, ~100MB): collects CPU, memory, network stats
- Grafana (the visualization layer, ~50MB): displays dashboards
- cAdvisor (Google's container monitoring, ~30MB): exports per-container metrics to Prometheus
This stack fits comfortably on a 4GB VPS. Docker Hub hosts pre-built images; spin them up with Compose in under 10 minutes.
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
Practical Deployment Workflow
- Version your Compose file in Git. When you change configurations, you have full history.
-
Use environment-specific configs. Create
docker-compose.prod.ymlthat overrides development settings. -
Automate deployments. A simple bash script pulls the latest code, rebuilds images, and runs
docker-compose up -d. - Log aggregation. Send container logs to a central location (even a syslog server on your VPS) so you don't lose them when containers restart.
- Backup volumes regularly. Database containers store data in volumes. Automated backups are non-negotiable.
Choosing Your VPS Provider
VPS pricing for running multiple applications ranges from $5/month (1GB RAM, shared CPU) to $100+/month (16GB RAM, dedicated cores). For a three-application stack, you'll want at minimum:
- 2GB RAM (tight, requires careful tuning)
- 2 CPU cores (one core per major application)
- 50GB storage (room for containers, logs, database backups)
At this spec, expect $15-30/month from providers like DigitalOcean, Linode, or Hetzner. Providers like ServerToolPick compare these options and help identify the right fit for your workload—they track real performance metrics and customer reviews.
Conclusion
Running multiple applications on a single VPS doesn't require enterprise orchestration tools. Docker Compose handles the common case elegantly: you define your stack once, deploy it anywhere, and scale horizontally by spinning up new VPS instances when you outgrow one.
Start with Compose if you have 2-5 services and your traffic fits on a single server. Add systemd integration if you need tighter control over restarts and logging. Graduate to Docker Swarm or Nomad only when you're running multiple servers and manual coordination becomes painful—typically once you've sustained 50,000+ requests per second or need to deploy across geographic regions.
The key insight: the right orchestration tool is the simplest one that still solves your problem. Docker Compose solves it for most small deployments. Use it, monitor it, and only add complexity when your current approach demonstrably breaks down.
Top comments (0)