A VPS (Virtual Private Server) is the workhorse of professional web hosting. You get a virtual machine with root access, a public IP, and full control — without managing physical hardware.
| Provider | Cheapest VPS | Data Centers | Strengths | Best For |
|---|---|---|---|---|
| DigitalOcean | $4/mo (512MB) | 14 regions | Simple UI, great docs, managed DBs | Startups, learning |
| Hetzner | €3.79/mo (2GB) | EU + US | Best price/performance ratio | Price-conscious, EU hosting |
| Linode (Akamai) | $5/mo (1GB) | 11 regions | Reliable, good support | General purpose |
| Vultr | $2.50/mo (512MB) | 32 locations | Most locations, bare metal option | Edge deployments |
| OVH | €3.50/mo (2GB) | EU focused | Cheap dedicated servers too | EU, budget dedicated |
Every new VPS should go through this hardening process before deploying anything:
# 1. Connect as root (first time only)
ssh root@YOUR_SERVER_IP
# 2. Update the system
apt update && apt upgrade -y
# 3. Create a non-root user
adduser deploy
usermod -aG sudo deploy
# 4. Set up SSH key authentication for the new user
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
# 5. Harden SSH - edit /etc/ssh/sshd_config
sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sudo systemctl restart sshd
# 6. Set up firewall
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp # SSH (custom port)
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# 7. Install fail2ban (brute-force protection)
sudo apt install -y fail2ban
sudo systemctl enable fail2ban
# 8. Set up automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# On your server (as deploy user):
# Install your runtime (example: Node.js via nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install --lts
# Clone your application
cd /opt
sudo mkdir myapp && sudo chown deploy:deploy myapp
git clone git@github.com:you/myapp.git /opt/myapp
cd /opt/myapp
npm install --production
# Set up environment variables
sudo cp .env.example /etc/myapp.env
sudo chmod 600 /etc/myapp.env
# Edit with your production values
# Create systemd service (as shown in Chapter 4)
# Set up Nginx reverse proxy (as shown in Chapter 4)
# Obtain SSL certificate with Certbot
# On server:
cd /opt/myapp
git pull origin main
npm install --production
sudo systemctl restart myapp
# From your local machine:
rsync -avz --delete \
--exclude='node_modules' \
--exclude='.env' \
./dist/ deploy@server:/opt/myapp/
ssh deploy@server 'cd /opt/myapp && npm install --production && sudo systemctl restart myapp'
# Build locally or in CI, push to registry
docker build -t myregistry/myapp:v1.2.3 .
docker push myregistry/myapp:v1.2.3
# On server:
docker pull myregistry/myapp:v1.2.3
docker stop myapp && docker rm myapp
docker run -d --name myapp -p 3000:3000 --env-file /etc/myapp.env myregistry/myapp:v1.2.3
| Tool | Language | Features | When to Use |
|---|---|---|---|
| systemd | Any | Built into Linux, restart policies, logging | Always (it's already there) |
| PM2 | Node.js | Cluster mode, zero-downtime reload, monitoring | Node.js apps without Docker |
| Supervisor | Any | Simple config, process groups | Legacy systems, multiple processes |