TL;DR: A VPS (Virtual Private Server) is a virtual machine you rent in a data center. Unlike shared hosting, you have full root access and can run any software continuously. Unlike serverless (Vercel, Netlify), it can run background workers, databases, and persistent processes. You connect via SSH, manage it via the command line, and pay a fixed monthly fee (typically $4–20/month).
Why AI Coders Need to Know This
As a vibe coder, you will hit a wall with serverless hosting eventually. Maybe your app needs a background worker that runs every few minutes. Maybe you want to self-host a database instead of paying Supabase's pricing. Maybe you're running a Python AI service that times out on Vercel's serverless functions. Maybe you just want full control over your stack.
When you ask Claude or Cursor to "help me deploy this app properly," there is a good chance it will say "deploy to a VPS." And then it will generate a wall of Linux commands, nginx config blocks, and systemd service files that you have never seen before. If you don't understand what a VPS is and why it exists, that output is incomprehensible.
This guide closes that gap. You will understand what a VPS is, how it differs from your other hosting options, when to use it, and enough about the setup to follow AI-generated deployment instructions with confidence.
Real Scenario
You built a Next.js app with a background job that processes payments every hour, and a PostgreSQL database that Vercel's serverless architecture can't handle efficiently. You type:
Prompt I Would Type
I have a Next.js app with a Node.js backend that runs hourly cron jobs.
I also need to self-host a PostgreSQL database.
Vercel isn't working well for this — the cron jobs time out.
What's the best way to deploy this? Walk me through the infrastructure setup
step by step, assuming I'm starting from scratch.
Claude's answer will almost certainly involve a VPS. Here's what it generates and what every piece means.
What AI Generated
# Step 1: SSH into your VPS
# Replace 1.2.3.4 with your VPS IP address, and ~/.ssh/my-key with your key path
ssh -i ~/.ssh/my-key ubuntu@1.2.3.4
# Step 2: Update the system and install Node.js
sudo apt update && sudo apt upgrade -y # update package list and installed packages
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - # add Node.js repo
sudo apt install -y nodejs # install Node.js 22
node --version # verify: should print v22.x.x
# Step 3: Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib
sudo systemctl start postgresql # start the database server
sudo systemctl enable postgresql # auto-start on server reboot
# Step 4: Clone your app
cd /var/www # standard location for web apps
sudo git clone https://github.com/you/your-app.git
cd your-app
npm install # install dependencies
cp .env.example .env # copy env template — fill in your values
nano .env # edit env variables (Ctrl+O to save, Ctrl+X to exit)
# Step 5: Create a systemd service to keep your app running
# This replaces "npm start" — systemd restarts your app if it crashes
sudo nano /etc/systemd/system/myapp.service
# Contents of /etc/systemd/system/myapp.service
[Unit]
Description=My Next.js App
After=network.target # wait for network before starting
[Service]
Type=simple
User=ubuntu # run as non-root user for security
WorkingDirectory=/var/www/your-app
ExecStart=/usr/bin/node server.js # the command that starts your app
Restart=on-failure # restart automatically if it crashes
RestartSec=10 # wait 10 seconds before restarting
Environment=NODE_ENV=production # set environment variables here too
[Install]
WantedBy=multi-user.target
# Step 6: Enable and start the service
sudo systemctl daemon-reload # reload systemd config
sudo systemctl enable myapp # start on boot
sudo systemctl start myapp # start now
sudo systemctl status myapp # check it's running
# Step 7: Install nginx as a reverse proxy
sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/myapp
# Contents of nginx config — routes web traffic to your Node.js app
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000; # forward requests to your Node.js app
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;
}
}
# Step 8: Enable the site and add SSL
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t # test config for syntax errors
sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx # install Let's Encrypt SSL tool
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com # get free SSL certificate
Understanding Each Part
What a VPS Actually Is
A physical server in a data center has significant compute resources — dozens of CPU cores, hundreds of GB of RAM, terabytes of storage. A VPS provider (DigitalOcean, Hetzner, Linode) uses virtualization software to carve that physical server into multiple isolated virtual machines. Your VPS gets a guaranteed slice of those resources.
Unlike shared hosting where hundreds of websites share the same server without isolation, your VPS is your own environment. You have root (administrator) access. You can install any software, configure the firewall, run background processes, and access the filesystem. It runs 24/7 — unlike serverless functions that spin up on demand and shut down when idle.
SSH: How You Access a VPS
SSH (Secure Shell) is the command-line protocol you use to connect to and control your VPS remotely. When you run ssh ubuntu@1.2.3.4, you're opening a terminal session on the remote server. Everything you type runs on the VPS, not your local machine. This is how you deploy code, manage services, and configure the server.
SSH uses key-based authentication: you generate a key pair (public + private), give the public key to the VPS provider during setup, and use your private key to connect. It's more secure than passwords. See What Is DNS? for how your domain name points to the VPS IP address.
systemd: Keeping Your App Running
If you just run node server.js on the VPS and close your terminal, the process dies. systemd is the Linux process manager that keeps your app running as a background service, automatically restarts it if it crashes, and starts it when the server reboots. That's what the .service file configures.
nginx: The Traffic Director
Your Node.js app runs on port 3000 (or whatever you configure). But web browsers use port 80 (HTTP) and port 443 (HTTPS). nginx sits between the internet and your app: it listens on ports 80/443, handles SSL certificates, serves static files efficiently, and forwards everything else to your app. This is called a reverse proxy.
Hosting Options Compared
| Type | Examples | Control | Persistent Processes | Best For |
|---|---|---|---|---|
| Shared Hosting | Bluehost, SiteGround | Very low | Limited | Simple WordPress sites |
| Serverless/PaaS | Vercel, Netlify, Railway | Medium | No (functions only) | Next.js, static sites, APIs |
| VPS | Hetzner, DigitalOcean, Linode | Full (root) | Yes | Full-stack apps, background workers, self-hosted DBs |
| Dedicated Server | OVH, Hetzner Dedicated | Full | Yes | High-traffic, bare-metal performance |
What AI Gets Wrong About VPS Deployment
1. Assuming Serverless When You Need Persistent State
AI sometimes generates deployment instructions for Vercel when your app has requirements that clearly need a VPS — persistent WebSocket connections, self-hosted databases, background workers, or file system state. Before accepting deployment advice, tell the AI explicitly: "This app needs [background jobs / a self-hosted database / persistent connections]."
2. Running Apps as Root
AI-generated setup scripts sometimes run your app as the root user — the Linux equivalent of running everything as administrator. This is a security risk: if your app has a vulnerability, an attacker would have root access to your entire server. Always create a dedicated non-root user for your app to run as.
3. Skipping the Firewall
A freshly provisioned VPS is often wide open — all ports accessible from the internet. AI deployment guides frequently skip firewall configuration. As a minimum, configure UFW (Ubuntu's firewall) to only allow SSH (port 22), HTTP (port 80), and HTTPS (port 443):
sudo ufw allow ssh
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable
4. Outdated Commands
AI training data has a knowledge cutoff. VPS deployment commands change — package names, nginx syntax, certbot flags. Always verify the commands match your OS version. On Ubuntu 24.04, some older package names don't exist. Ask the AI to confirm the commands for your specific distribution and version.
5. Not Setting Up Backups
VPS data is not automatically backed up. If the server fails or you delete something important, it may be gone. Most VPS providers offer automated snapshot backups for a few extra dollars a month. Enable them. For databases, set up automated PostgreSQL dumps to an S3 bucket.
How to Debug VPS Issues with AI
In Cursor
When a VPS command fails, paste the full error output into Cursor chat with your OS version: "Running on Ubuntu 24.04. This command failed with this error: [paste output]. What went wrong?" Linux errors are often cryptic; Cursor is good at explaining them in plain language.
In Windsurf
For complex deployment architectures, use Windsurf's Cascade to create a deployment plan: "Given my app requirements (Node.js backend, PostgreSQL, hourly cron jobs, WebSocket support), generate a complete VPS setup plan for Ubuntu 24.04 with security hardening included." The codebase context helps it generate deployment scripts that match your actual app structure.
In Claude Code
Claude excels at reading and explaining nginx and systemd config. Paste a config file and ask: "Explain what each line of this nginx config does and identify any security issues." Also useful for generating the right journalctl commands to read your service logs when something breaks.
Key Diagnostic Commands
sudo systemctl status myapp— is your app running?sudo journalctl -u myapp -n 50— last 50 lines of app logssudo nginx -t— test nginx config for errors before reloadingsudo ss -tlnp— what ports are listening on the server?curl http://localhost:3000— is the app responding locally?
What to Learn Next
Frequently Asked Questions
A VPS (Virtual Private Server) is a virtual machine that runs on a physical server in a data center. You get a dedicated slice of compute resources (CPU, RAM, storage) that behaves like your own computer — you have root access, can install any software, and run any kind of application continuously.
Use a VPS when you need persistent processes (background workers, cron jobs, WebSockets), a self-hosted database, custom software that can't run serverless, or when your app's compute needs exceed what free/hobby tiers offer at a reasonable cost. Vercel and Netlify are excellent for most front-end and API work.
Hetzner is widely considered the best value — a 2 vCPU, 4GB RAM VPS costs around €4.51/month. DigitalOcean Droplets start at $4/month, Linode/Akamai at $5/month. For managed options, Railway starts with a free tier and usage-based pricing. Avoid very cheap unbranded VPS providers with no SLA.
You need Linux basics: SSH to connect, apt or yum to install packages, nano or vim to edit files, systemctl to manage services, and basic file navigation. AI tools (Claude Code, Cursor) can guide you through most VPS tasks if you share the terminal output and ask for help step by step.
nginx is a web server and reverse proxy. On a VPS, nginx sits in front of your application and handles incoming HTTP requests — routing traffic, serving static files, terminating SSL, and forwarding API requests to your app process. It's fast, lightweight, and used by about 33% of all web servers.