Skip to content
Back to Blog
Linux & Server11 min read

How to Set Up Cheap VPS Hosting: 8 Advanced Moves

A step-by-step walkthrough from provider selection to a hardened production VPS, with real commands and config you can copy.

Written by Abdul AbrorTechnical Hosting Support Engineer
How to Set Up Cheap VPS Hosting: 8 Advanced Moves
On this page

You can spin up a VPS in sixty seconds, but a production-ready server takes deliberate steps. I've set up dozens of budget boxes for clients who needed more control than shared hosting but couldn't justify premium providers. The difference between a throw-away experiment and a stable platform comes down to eight moves most tutorials skip.

This walkthrough assumes you've never deployed a VPS before or you've done it once and want to tighten the setup. We'll go from provider selection to a hardened, monitored box running real workloads.

Move 1: Pick a provider that fits your workload

Don't choose on price alone. Check three things: network uptime SLA, whether they charge for bandwidth overages, and how fast you can deploy from their API or panel.

For general web hosting and small apps, look at Hetzner, Vultr, Linode, or DigitalOcean. Hetzner gives you the most RAM and disk per dollar if you're in Europe. Vultr and DigitalOcean have data centers in more regions. Linode has straightforward billing and no surprise fees.

Avoid providers that bundle cPanel or Plesk into the base price unless you need those panels—you're paying for software you might not use. A bare Ubuntu or Debian image is the starting point for custom setups.

Once you've signed up, create your first instance with at least 1 GB RAM. Anything less will swap under normal web traffic. Pick a recent LTS distribution: Ubuntu 24.04 or Debian 12. Skip CentOS Stream unless you have a specific reason; the support model changed and it's less predictable for production.

Move 2: Log in and lock down SSH immediately

The moment your VPS goes live, bots will start probing port 22. I've seen thousands of failed root login attempts within the first hour of a fresh deploy.

Log in as root using the credentials your provider emailed:

ssh root@your_vps_ip

First command: create a non-root user with sudo.

adduser deploy
usermod -aG sudo deploy

Test that the new user can escalate:

su - deploy
sudo ls /root

If that works, copy your SSH key to the deploy user. From your local machine:

ssh-copy-id deploy@your_vps_ip

Now disable root login and password authentication. Edit /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Change these lines:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

Restart SSH:

sudo systemctl restart ssh

Open a second terminal and confirm you can still log in as deploy before closing your root session. If you lock yourself out, you'll need the provider's console.

Move 3: Configure a firewall with sane defaults

UFW is the simplest firewall front-end for iptables. Install and enable it with three rules: SSH, HTTP, HTTPS.

sudo apt update && sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Check the status:

sudo ufw status verbose

If you need custom ports later—say 3306 for remote MySQL—add them explicitly. Never set the default incoming policy to allow.

Move 4: Automate security updates with unattended-upgrades

Manual patching is fine until you forget for three months and miss a kernel CVE. The unattended-upgrades package applies security fixes automatically.

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades

Select "Yes" when prompted. The default config will install security updates daily and reboot if needed. Check /etc/apt/apt.conf.d/50unattended-upgrades if you want to tweak reboot times.

You'll still need to manually upgrade major distribution releases, but day-to-day patches happen in the background.

Move 5: Install Fail2Ban to block brute-force attempts

Even with key-only SSH, bots will hammer your logs. Fail2Ban watches /var/log/auth.log and bans IPs after repeated failures.

sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

The default jail for SSH works out of the box. After a few hours, check who's banned:

sudo fail2ban-client status sshd

You'll see a list of IPs that tried to brute-force root. If you run Nginx or Apache, enable the corresponding jails in /etc/fail2ban/jail.local to block HTTP auth attacks and exploit scans.

Move 6: Set up swap even if you have enough RAM

A 1 GB VPS can hit OOM under load and the kernel will kill random processes. A swap file acts as a pressure valve.

Create a 2 GB swap:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Make it permanent by adding it to /etc/fstab:

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Adjust swappiness so the kernel prefers RAM:

sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

A swappiness of 10 means the kernel will only swap when RAM usage exceeds 90%. For database servers, consider setting it to 1.

Move 7: Deploy a reverse proxy with automatic HTTPS

Caddy is the fastest way to get HTTPS working without certificate headaches. It requests and renews Let's Encrypt certs automatically.

Install Caddy:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy -y

Edit /etc/caddy/Caddyfile:

yourdomain.com {
    reverse_proxy localhost:3000
}

Replace localhost:3000 with wherever your app listens. Reload Caddy:

sudo systemctl reload caddy

Point your domain's A record to the VPS IP. Within two minutes Caddy will provision a certificate and serve HTTPS. No manual cert dance, no cron job for renewals.

If you prefer Nginx, you'll need Certbot and a separate renewal hook. Caddy does it in one config block.

Move 8: Schedule daily backups to object storage

Provider snapshots are convenient but expensive at scale. Object storage from the same provider or Backblaze B2 costs a fraction per GB.

Install rclone:

sudo apt install rclone -y

Configure a remote (interactive):

rclone config

Select your provider—Backblaze, Wasabi, DigitalOcean Spaces, whatever. You'll paste access keys from their panel.

Create a backup script at /usr/local/bin/backup.sh:

#!/bin/bash
set -e

BACKUP_DIR="/home/deploy/backups"
DATE=$(date +%Y%m%d)
TARGET="$BACKUP_DIR/backup-$DATE.tar.gz"

mkdir -p "$BACKUP_DIR"

# Tar important directories
tar -czf "$TARGET" /etc /home/deploy /var/www

# Sync to object storage
rclone copy "$TARGET" remote:your-bucket/backups/

# Keep only last 7 days locally
find "$BACKUP_DIR" -type f -mtime +7 -delete

Make it executable:

sudo chmod +x /usr/local/bin/backup.sh

Add it to root's crontab:

sudo crontab -e

Append:

0 2 * * * /usr/local/bin/backup.sh

Backups run at 2 AM daily. Test the script manually first to catch any path or permission issues.

What about monitoring and alerts?

You've built a solid foundation. The missing piece is knowing when something breaks.

Install a lightweight monitoring agent. Netdata runs locally and exposes a web dashboard on port 19999. For remote monitoring, UptimeRobot pings your endpoints every five minutes and emails you on downtime. I use both: Netdata for system metrics, UptimeRobot for uptime checks.

If you want log aggregation, point your syslog to a remote collector like Papertrail or Logtail. Free tiers handle low-traffic sites without issue.

For budget setups, you don't need a full observability stack. A simple check that sends an email when disk usage hits 80% or when the web server stops responding is enough to catch most fires before they spread.

Lock it down, then build on it

A cheap VPS becomes a stable platform when you apply these eight moves: pick a provider with predictable billing, harden SSH before the bots find you, firewall everything except the ports you need, automate security patches, block brute-force attempts, add swap as a safety net, deploy a reverse proxy that handles HTTPS automatically, and schedule daily backups to object storage.

Every other feature—databases, application runtimes, CDN integration—builds on this foundation. Get these right and your five-dollar-a-month box will outlast servers that cost ten times as much but were deployed carelessly.

FAQ

Can I run multiple sites on one cheap VPS?

Yes. A 2 GB instance can handle several low-traffic WordPress sites or static generators behind Caddy or Nginx with virtual host configs. Watch your RAM usage and add swap if needed.

Should I use a control panel like cPanel or Plesk?

Only if you need the GUI or you're reselling hosting. Panels add overhead—200-500 MB of RAM—and licensing fees. For personal projects or a handful of sites, the command line is faster and cheaper.

How do I move from shared hosting to a VPS?

Export your databases, tar your files, copy them to the VPS with scp or rsync, then recreate your database and virtual host config. Update your DNS A records to point to the new IP. Test before changing DNS; you can edit your local hosts file to preview the site on the new server.

What if I need more resources later?

Most providers let you resize instances from their panel. You'll get a few minutes of downtime while they migrate your disk to a larger VM. For zero-downtime scaling, set up a second VPS, sync your data, and switch DNS. Budget providers won't have automatic load balancing, so plan your architecture early if you expect traffic spikes.

Is unattended-upgrades safe for production?

Yes, for security updates. It won't upgrade the kernel or distribution release without your intervention. I've run it on production servers for years without breakage. If you're paranoid, configure it to download updates but require manual approval before installing.