DEV Community

yaroslav
yaroslav

Posted on

Running Multiple Applications on One VPS: Container Orchestration Without Kubernetes

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:
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Practical Deployment Workflow

  1. Version your Compose file in Git. When you change configurations, you have full history.
  2. Use environment-specific configs. Create docker-compose.prod.yml that overrides development settings.
  3. Automate deployments. A simple bash script pulls the latest code, rebuilds images, and runs docker-compose up -d.
  4. 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.
  5. 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)