Host multiple React applications on one VPS

An agency might host several demos, a marketing site and a small internal tool. Sharing one VPS is an option when you decide how resources are divided and what happens when one application receives a traffic spike.

The number of projects a server can host is not a function of available ports. Routes, databases, builds and overlapping peaks determine capacity. This guide explains how to organize and verify that sharing.

Give every application a boundary

Assign each application a release directory, supervised process, local port and its own environment variables. A shared reverse proxy selects the destination by domain. Restarts should target one identifiable service without touching the others.

For different clients, examine service accounts and permissions too. Processes sharing an account can access files readable by that account. A separate port is not security isolation. Containers or separate machines may be needed for the required boundary.

Plan domains and local ports

This table illustrates one arrangement. Replace the example.com names with your domains. Local ports are not public URLs: the browser connects to the proxy.

Each domain needs a valid certificate and appropriate configuration. Check absolute links, authentication callbacks and cookies for that domain. Sharing one domain under separate paths requires additional prefix and asset configuration. Subdomains often avoid that coupling.

On small screens, scroll the table horizontally.

An example inventory, not a capacity measurement.
ApplicationDomainLocal listener
Marketing sitewww.example.com127.0.0.1:3101
Demodemo.example.com127.0.0.1:3102
Internal tooltools.example.com127.0.0.1:3103

Budget for peaks that overlap

Record idle, ordinary load and representative peak usage for each service. Add shared services, the operating system and deployment headroom. Count a shared database once in the total budget, with its own peak.

Then load two applications at the same time. Isolated tests can miss competition for CPU, disk and database connections. If a team presents demos during working hours, activity periods may coincide.

Build a memory budget from your measurements

Set explicit resource boundaries

On a system with the required controllers, systemd can monitor and constrain a process group. In a drop-in for the tested service, MemoryHigh introduces reclaim pressure, while MemoryMax sets a hard boundary that can lead to termination. CPUQuota caps CPU time.

This fragment illustrates the syntax. Replace the values after measuring; they are not recommended Next.js sizing. An application hitting its limit can slow down or fail, so inspect latency, memory events and logs. The boundary must cover child processes as well as the parent.

override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=512M
MemoryMax=768M
CPUQuota=100%

Keep builds from overwhelming neighboring apps

Plan builds and cleanup separately from active processes. When possible, build in compatible CI and deploy a verified artifact. Otherwise include the build in the shared resource budget and avoid concurrent expensive compilations.

Use a directory per release and verify before switching. Existing connections and assets needed by already-open tabs require attention. Database backups remain separate from application archives; periodically test restoration using test data.

Evaluate sleep for intermittent projects

PRNext can release idle workers’ memory at the cost of wake-up work. That may help when idle periods are long and initial visits can accept the delay. Frequent probes or background requests can change the behavior.

Before adding projects, test simultaneous wake-ups too. PRNext’s published memory probe covers one small application; it does not establish a guaranteed site count. Databases, the proxy, system and caches retain their own requirements.

Understand sleep settings and their limitations

Verify each application’s public deployment

Sources and further reading

Back to the blog