How to Choose a VPS for Several Apps: One Server or More?

Published on

Size a VPS for several apps from their documented RAM, CPU and disk needs, decide on one server or several, then plan isolation, ports, backups and monitoring.

A man holding a laptop in a data center aisle between rows of server racks with blue and yellow cables

You want a website, a Git server, a dashboard and n8n online, and you can't decide between one bigger VPS and a few small ones, or how big either should be. Most apps document what they need, and those numbers make the choice concrete. This guide adds up RAM, CPU and disk, weighs one server against several, then covers isolation, ports, backups and monitoring. Commands are for Ubuntu 24.04 and Debian 12.

Quick answer: Collect what each app's docs ask for: RAM and disk add up, while CPU follows the busiest app and the jobs that run at the same time. Keep the RAM total under about three quarters of the plan. One bigger VPS is simpler and shares spare capacity; give an app its own server when it wants ports 80 and 443 to itself, needs as much as the rest together, belongs to someone else or needs its own maintenance window. On a shared server, give each app its own Compose project or Linux user, a memory limit and a port on 127.0.0.1 behind one reverse proxy, back up per app and check every domain from outside.

Step 1: Add Up What Each App Documents

From each project's own documentation:

AppRAMCPU and disk
Ubuntu Server 24.041 GB minimum (cloud image); 3 GB or more suggestedDisk: 4 GB minimum (cloud image); 25 GB or more suggested
GhostA server with at least 1 GB, for its whole stack (NGINX, Node.js, MySQL)Not stated
Gitea1 GB for small teams2 cores for small teams
Nextcloud128 MB minimum, 512 MB recommended, per processNot stated
n8nNo minimum stated; grows with the data each execution handlesNot stated
Immich6 GB minimum, 8 GB recommended2 cores minimum, 4 recommended; library plus 10 to 20% on average for thumbnails and transcoded video
Coolify2 GB2 cores; 10 GB of free disk

RAM adds up. Count the system once, then every app. Whole-server figures (Ghost, Coolify) include the system, so adding them errs on the safe side. Figures per process multiply: two Nextcloud workers busy at once need 1 GB at 512 MB each. Measure apps without a figure: n8n "doesn't restrict the amount of data each node can fetch and process", so take its peak from docker stats on a test server. Our RAM guide shows how, and why the total should stay under about three quarters of the plan.

CPU follows the busiest moment. Idle apps leave their cores to the others, so CPU figures don't simply add up. Start from the highest one and look at what runs at once: a build, a photo import and the nightly backup in the same hour compete for the same vCores. Coolify's docs say to "Increase cores if running multiple workloads on one server". Spread scheduled jobs and give background work a lower CPU weight (step 3).

Disk adds up, then grows. Add the system (Ubuntu suggests 25 GB or more), each app's data, container images, logs and the dumps you keep before they go off-site; Coolify says to keep space "for Docker images, volumes, and backups". Leave room for a year of growth; our backup storage guide measures it.

A Worked Example

Ghost, Gitea, Nextcloud for a small team and n8n on one server:

PartRAM from the docs
Ubuntu 24.04 (cloud image minimum)1 GB
Ghost with NGINX and MySQL1 GB
Gitea1 GB
Nextcloud, two PHP workers at 512 MB1 GB
n8nYour measured peak
Total4 GB plus n8n

With the three-quarter margin, a 4 GB plan is already full and an 8 GB plan leaves about 2 GB for n8n and peaks; Gitea's 2 cores set the CPU floor. Add Immich and the documented RAM climbs to 10 to 12 GB plus n8n, with 4 cores recommended: at least a 16 GB plan, or a second server.

Step 2: One Bigger VPS or Several Small Ones

One bigger VPSSeveral smaller VPSs
Spare RAM and CPUShared: a quiet app leaves room for a busy oneStuck on the server that has it
Operating systemSized, patched and backed up onceOnce per server
An app runs out of memoryThe kernel may kill any process on the serverOnly that server is hit
The disk fills upWrites fail for every appOnly that server's apps
A rebootEvery app restartsOne server at a time
One app is compromisedIt shares a kernel with the othersIt runs in its own virtual machine
Apps talk to each otherOver 127.0.0.1 or a Docker networkOver the network, which you encrypt

Memory bites first. When the kernel runs short, Docker's docs warn, "Any process is subject to killing, including Docker and other important applications", so set a limit per app (step 3). Updates are shared too: on Ubuntu 24.04, needrestart "restarts the affected services automatically by default", and a reboot stops every app. Separate servers avoid this: Docker describes a VM as "an entire operating system with its own kernel", while containers "all share the same kernel". Between servers, WireGuard keeps app traffic private.

On Space-Node, two Standard VPS-S plans add up to 6 vCores, 8 GB RAM and 120 GB NVMe, and one VPS-M has 4 vCores, 8 GB and 80 GB, but on two servers, spare memory on one can't help the other. Compare both on the VPS plans page; you can move to a bigger plan from the client area later.

Give an app its own server when:

  • It wants the whole server. Coolify: "It's best to use a fresh server for Coolify to avoid any conflicts with existing applications." Dokploy's installer "will fail" if port 80, 443 or 3000 is in use, and mailcow needs 80 and 443 plus the mail ports.
  • It needs as much as the rest together, like Immich above.
  • Someone else needs root. Anyone with root, sudo or the docker group can read every app's files.
  • It needs its own maintenance window, or it is the staging copy of another app.

Step 3: Keep the Apps Apart on One Server

You can mix two ways: a Compose project per app, or an account and systemd service per app.

Docker: One Compose Project per App

Give every app a folder under /srv/apps; hier(7) describes /srv as "site-specific data that is served by this system". Compose names the project after its folder and creates a network called <project-name>_default for it, so each app's containers, its database included, get a network of their own. Set limits and bind ports to 127.0.0.1, here for Grafana, whose default port 3000 is also Gitea's:

services:
  grafana:
    image: grafana/grafana
    restart: unless-stopped
    mem_limit: 1g
    cpus: 1.0
    ports:
      - "127.0.0.1:3002:3000"
    volumes:
      - grafana-storage:/var/lib/grafana

volumes:
  grafana-storage:
  • mem_limit limits the container's memory and cpus its CPU: with --cpus="1.5", Docker says "the container is guaranteed at most one and a half of the CPUs". Without limits, "a container has no resource constraints". Set the memory limit above the measured peak; Grafana recommends at least 512 MB.
  • 127.0.0.1:3002:3000 publishes container port 3000 as host port 3002, on the loopback address only. Without a host IP, Docker binds to all interfaces, "bypassing host firewall rules", as our Docker and UFW guide explains.

Start it with sudo docker compose up -d in that folder. Docker separates the apps, not the admins: "The docker group grants root-level privileges to the user", so only people you would give root belong in it.

Separate Users: One Account and Service per App

For packages and single binaries, an account per app does the separating; Gitea's docs say it "should be run with a dedicated non-root system account". systemd then limits each service:

sudo systemctl set-property gitea.service MemoryHigh=1G MemoryMax=1500M CPUWeight=50

MemoryHigh is systemd's "main mechanism to control memory usage": above it, the service is slowed down and memory is taken back. MemoryMax is "the last line of defense": if usage can't stay under it, the OOM killer acts inside that unit only. CPUWeight shares CPU time by weight against the kernel default of 100, so at 50 Gitea gets half the share of a default service when both are busy. The changes "are applied immediately, and stored on disk for future boots".

Use Docker for apps built around it (Immich "requires Docker with the Docker Compose plugin") or apps that need their own runtime versions, and an account per app for the rest.

Step 4: Ports and Domains

One reverse proxy owns ports 80 and 443 and passes each request to its app by domain name, while every app listens on its own port on 127.0.0.1. One IP address is enough: with SNI, the browser names the site during the TLS handshake, which nginx calls "A more generic solution for running several HTTPS servers on a single IP address". Plan the ports first, because defaults collide:

AppDefault port
Gitea3000, on all addresses
Grafana3000
Uptime Kuma3001
n8n5678
DokployNeeds 80, 443 and 3000 free
mailcow25, 80, 110, 143, 443, 465, 587, 993, 995 and 4190

sudo ss -ltnp shows which process holds each listening TCP port.

With Caddy, the routing is a few lines and Caddy gets a certificate for every name by itself. Debian 12 and Ubuntu 24.04 both ship Caddy 2.6.2 (sudo apt install caddy); for production, Caddy's install page recommends its own official packages. In /etc/caddy/Caddyfile:

git.example.com {
    reverse_proxy 127.0.0.1:3000
}

grafana.example.com {
    reverse_proxy 127.0.0.1:3002
}

status.example.com {
    reverse_proxy 127.0.0.1:3001
}

Then run sudo systemctl reload caddy. Certificates need each domain's A/AAAA records pointing at the server and ports 80 and 443 open. Caddy passes the Host header through and sets X-Forwarded-For and X-Forwarded-Proto. For nginx and apps under PM2, see multiple Node.js apps on one VPS.

Two catches. Some installers bring a proxy (Ghost's offers to set up NGINX, Dokploy runs Traefik on 80 and 443): route the other sites through it, or give that app its own server. And apps that build links need their public address: Gitea's ROOT_URL is "useful if the internal and the external URL don't match"; for n8n, see our n8n reverse proxy guide.

Step 5: Back Up Each App So You Can Restore One

Restoring the whole server rolls every app back; when one app breaks, you want only its data back. Per app, note its folder, its Docker volumes, its database dump (PostgreSQL, MariaDB) and its secrets. The /srv line in the include file of our restic guide covers every folder under /srv/apps. Named volumes such as grafana-storage are managed by Docker outside that folder, so archive them to /var/backups first, as our Docker volumes guide shows. To restore one app next to the original:

sudo -i
set -a; . /etc/restic/env; set +a
restic snapshots
restic restore latest --target /tmp/restore --include /srv/apps/gitea

restic puts the files in /tmp/restore/srv/apps/gitea. Stop that app, swap in the restored folder and start it again; the other apps keep running. On a Space-Node VPS, backups are not included in the plan price, so run your own like this or arrange them through a support ticket.

Step 6: Watch Each App, and the Server From Outside

On the server, systemd-cgtop lists control groups "ordered by their CPU, memory, or disk I/O load":

sudo systemd-cgtop -m

-m sorts by memory and -c by CPU, which runs up to 100% per vCore. Debian 12 and Ubuntu 24.04 use cgroup v2, so Docker's systemd cgroup driver puts each container in system.slice as docker-<id>.scope; sudo docker stats --no-stream shows them by name, as in the RAM guide.

A monitor on the same VPS goes down with it. Give every domain its own HTTP check from another machine, for example with Uptime Kuma, so an alert names the app that failed. For trends, the sar history in our upgrade guide shows when the whole server gets tight.

Which One Fits

  • One VPS when the apps are yours, the documented RAM fits under three quarters of a plan and one maintenance window suits them all.
  • A separate VPS for one app when it wants the whole server (Coolify, Dokploy, mailcow) or needs as much as the rest (Immich).
  • A VPS per owner when clients or other people need root.
  • Split later if you're unsure: an app with its own folder, port and domain moves with a copy and a DNS change, as in moving a website to a new VPS.

FAQ

How many applications can one VPS run? There's no fixed number; RAM usually decides first. Add the documented figures and your measured peaks, count the system once and stay under about three quarters of the plan's RAM. Platforms count too: Coolify asks for 2 cores and 2 GB for itself.

Is Docker safer than separate Linux users? It separates more: processes in one container "cannot see, and even less affect" those in another, and each project gets its own network. But containers share the host's kernel and the docker group is root-level. For an app or owner you don't trust, use a separate VPS.

Do I need a separate IP address for every domain? No. With SNI, one address serves many HTTPS sites through one reverse proxy. Only services that need the same port, such as two mail servers on port 25, need another address or server.

Can several apps share one database server? Yes, with a database and a user per app: one cache to size and one service to dump. But restarting or upgrading it stops every app that uses it. Apps whose Compose setup runs its own database, such as Immich, keep theirs.

Sources

All opened on 31 October 2026: