Why Run Multiple Projects on One Server?
If you're a freelancer, agency, or solo developer, spinning up a new VPS for every project gets expensive fast. A single managed Linux VPS can comfortably host 5–10 projects at once, each isolated and independent, without one crashing the others.
The trick is using the right tooling: process managers to keep each app alive, a reverse proxy to route traffic to the right port, and sensible naming conventions so you don't confuse which project is which at 2 a.m.
This guide walks through the architecture, the tools, and the gotchas.
The Architecture: Process Manager + Reverse Proxy
The standard pattern for running multiple projects on one Linux server is simple:
- Each project runs on its own port — Project A on 3001, Project B on 3002, etc.
- A reverse proxy (usually Nginx) sits on port 80/443 — it listens for incoming traffic and forwards requests to the right port based on the domain.
- A process manager (PM2, systemd, or Supervisor) — keeps each app running, restarts it if it crashes, and handles logging.
This separation means if Project A has a memory leak and dies, Project B keeps running. The reverse proxy detects the failure and can serve a 502 error or fallback page without taking down the whole server.
Step 1: Choose a Process Manager
You have three main options:
PM2 (Recommended for Node.js)
PM2 is the easiest to learn and works great for Node.js, Python, and Go apps. Install it once globally:
npm install -g pm2
pm2 startup
pm2 save
Then start each app with a descriptive name:
pm2 start app.js --name "client-dashboard"
pm2 start server.py --name "api-service" --interpreter python3
pm2 start index.js --name "blog-site"
Check status anytime:
pm2 status
PM2 auto-restarts apps on reboot and if they crash. It also handles log rotation and gives you a centralized log viewer.
Systemd (More Control, Steeper Learning Curve)
If you want fine-grained control or are running non-Node apps, systemd is powerful. You create a unit file for each app:
[Unit]
Description=Client Dashboard
After=network.target
[Service]
Type=simple
User=vibe
WorkingDirectory=/home/vibe/projects/client-dashboard
ExecStart=/usr/bin/node app.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Then enable and start it:
sudo systemctl enable client-dashboard
sudo systemctl start client-dashboard
sudo systemctl status client-dashboard
Systemd is more verbose but integrates deeply with Linux and gives you better resource limits and dependencies.
Supervisor (Older, Still Solid)
Supervisor is language-agnostic and works well for Python and PHP apps. It's less trendy than PM2 but battle-tested. Skip it unless you have a specific reason — PM2 or systemd are better starting points.
Step 2: Set Up a Reverse Proxy with Nginx
Nginx listens on ports 80 and 443, reads the incoming domain name, and forwards traffic to the right port. Install it:
sudo apt update
sudo apt install nginx
Create a config file for each project in /etc/nginx/sites-available/:
sudo nano /etc/nginx/sites-available/client-dashboard
Paste this template:
upstream client_dashboard {
server 127.0.0.1:3001;
}
server {
listen 80;
server_name dashboard.example.com;
location / {
proxy_pass http://client_dashboard;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable the site and test the config:
sudo ln -s /etc/nginx/sites-available/client-dashboard /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Repeat for each project, changing the upstream port and domain name. Nginx will route traffic based on the Host header (the domain), so each project gets its own subdomain or domain.
Step 3: Use SSL/TLS with Let's Encrypt
Don't run unencrypted. Certbot makes SSL free and automatic:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d dashboard.example.com -d api.example.com -d blog.example.com
Certbot updates your Nginx configs automatically and sets up renewal. Check the renewal is working:
sudo certbot renew --dry-run
Step 4: Organize Your Projects
Create a clear directory structure so you don't lose track of which project is which:
/home/vibe/projects/
├── client-dashboard/
│ ├── app.js
│ ├── package.json
│ └── .env
├── api-service/
│ ├── server.py
│ ├── requirements.txt
│ └── .env
└── blog-site/
├── index.js
├── package.json
└── .env
Use consistent naming: lowercase, hyphens, no spaces. Document which port each app uses in a README at the top level or in your process manager config.
Step 5: Manage Secrets and Environment Variables
Each project needs its own .env file with database URLs, API keys, and other secrets. Never commit .env to git.
If you're using PM2, you can also pass environment variables in an ecosystem.config.js file:
module.exports = {
apps: [
{
name: 'client-dashboard',
script: 'app.js',
cwd: '/home/vibe/projects/client-dashboard',
env: {
NODE_ENV: 'production',
PORT: 3001,
DATABASE_URL: 'postgres://...',
},
},
{
name: 'api-service',
script: 'server.py',
cwd: '/home/vibe/projects/api-service',
interpreter: 'python3',
env: {
FLASK_ENV: 'production',
PORT: 3002,
},
},
],
};
Then start all apps with:
pm2 start ecosystem.config.js
Step 6: Monitor and Debug
Check logs for each project:
pm2 logs client-dashboard
pm2 logs api-service --lines 100
Check resource usage:
pm2 monit
If Nginx isn't routing traffic correctly, check the error log:
sudo tail -f /var/log/nginx/error.log
If a project is crashing, check its logs and restart it:
pm2 restart client-dashboard
Common Pitfalls
Port Conflicts
If two apps try to use the same port, one will fail to start. Use a spreadsheet or a comment in your config to track which app uses which port. Better yet, automate it with a script that assigns the next available port.
Memory Leaks
One project with a slow memory leak will eventually consume all RAM and crash. Set memory limits in PM2:
pm2 start app.js --max-memory-restart 500M
Or in systemd with MemoryLimit= in the unit file.
Domain Routing Mistakes
If traffic for dashboard.example.com ends up at the wrong app, double-check the Nginx server_name directive and make sure DNS is pointing to your server.
Forgetting to Enable Sites
Creating an Nginx config file doesn't activate it. You must symlink it to sites-enabled/ and reload Nginx. If a domain works for one project but not another, this is often the culprit.
Scaling Beyond One Server
If you grow to 20+ projects or need higher availability, you'll eventually want separate servers or containers. But for most freelancers and small agencies, one well-managed Linux VPS handles 5–10 projects fine.
Tools like Vibesies abstract away some of this setup — you get a pre-configured Linux environment with Claude Code or Codex built in, so you can focus on building and deploying your projects instead of wrestling with systemd and Nginx. That said, the architecture above still applies whether you're self-managing or using managed hosting.
Checklist: Running Multiple Projects on One Linux Server
- Choose PM2, systemd, or Supervisor and install it
- Create a process manager config or unit file for each project
- Assign each project a unique port (3001, 3002, 3003, …)
- Install and configure Nginx as a reverse proxy
- Create Nginx config files for each domain/project
- Enable Nginx sites and test the config
- Set up SSL with Certbot
- Organize projects in a clear directory structure
- Create
.envfiles for secrets - Test that each domain routes to the correct app
- Set up log rotation and monitoring
- Document which port each project uses
- Set memory limits to prevent one app from crashing others
Conclusion
Running multiple projects on one Linux server is a practical, cost-effective way to host your work. The key is separating concerns: each app runs on its own port, a reverse proxy routes traffic by domain, and a process manager keeps everything alive. With PM2, Nginx, and a bit of organization, you can manage dozens of projects without much overhead.
As you scale, you'll refine your setup — adding monitoring, automating deployments, maybe moving to containers. But the fundamentals of developer hosting — process isolation, reverse proxying, and sensible naming — stay the same.