Shared hosting works for small sites until it doesn't. The classic signs: pages slowing down at peak hours, control-panel restrictions blocking software you need, your host suspending your account because some other tenant ran a bad query, or simply outgrowing what an "unlimited" plan actually delivers. This is the practical migration guide from shared hosting to a VPS.
Decide if you're actually ready
Before migrating, be honest: a VPS gives you control, but you become responsible for everything that the shared host used to handle automatically. Updates, security patches, backups, monitoring, mail server config — all on you. If your site is currently 100% static and rarely changes, shared hosting may still be the better fit.
You're ready for a VPS if: you're comfortable on the command line, you can troubleshoot Linux issues with documentation, you understand at minimum what HTTP, SSH, DNS, and certificates are, and you have time to spend ~2 hours setting up your stack initially plus 30 min/month maintaining it.
Choose your VPS size
For a typical migrated site, our Cloud VPS Basic (€3.48/mo: 1 GB / 1 vCPU / 25 GB) handles WordPress sites up to ~10K monthly pageviews. Advanced (€7.48/mo: 2 GB / 1 vCPU / 60 GB) handles up to ~50K. If you're not sure, start with Basic — you can resize up in 90 seconds if needed.
Provision the VPS and prepare the OS
Pick Ubuntu 24.04 LTS. After provisioning, SSH in as root, then immediately:
# Update everything
apt update && apt upgrade -y
# Create non-root user with sudo
adduser yourname
usermod -aG sudo yourname
mkdir /home/yourname/.ssh
cp /root/.ssh/authorized_keys /home/yourname/.ssh/
chown -R yourname:yourname /home/yourname/.ssh
chmod 700 /home/yourname/.ssh
chmod 600 /home/yourname/.ssh/authorized_keys
# Disable root SSH and password auth
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
# Firewall
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
# Set timezone
timedatectl set-timezone Europe/Paris
Install the LEMP stack
apt install -y nginx mariadb-server php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-intl php8.3-bcmath php8.3-redis redis-server certbot python3-certbot-nginx
mysql_secure_installation
Create a database and user for your site:
mysql -e "CREATE DATABASE wp_yoursite CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -e "CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'STRONG_RANDOM_PASSWORD';"
mysql -e "GRANT ALL ON wp_yoursite.* TO 'wp_user'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
Back up from shared hosting
Two things to back up:
Files. Most shared hosts give you SSH (or at least SFTP). Use rsync over SSH if you can:
# From your local machine, fetching from old host
rsync -avz --progress yourshareduser@oldhost.example.com:/home/youshareduser/public_html/ /tmp/oldsite/
If your shared host only offers FTP (some still do), use lftp's mirror command. Worst case, use cPanel's "Backup" tool to download a tarball.
Database. Use mysqldump from the old host (often via cPanel's PHPMyAdmin export):
mysqldump -u olduser -p old_database_name > olddb.sql
Transfer to the VPS
# From your local machine
scp -r /tmp/oldsite yourname@new-vps-ip:/tmp/
scp olddb.sql yourname@new-vps-ip:/tmp/
# On the VPS
sudo mkdir -p /var/www/yoursite.com
sudo cp -r /tmp/oldsite/* /var/www/yoursite.com/
sudo chown -R www-data:www-data /var/www/yoursite.com
# Import database
mysql -u root wp_yoursite < /tmp/olddb.sql
Update wp-config.php (or equivalent)
Update the database credentials, host (now localhost), and any path references. For WordPress, also update the site URL via WP-CLI before going live:
cd /var/www/yoursite.com
sudo -u www-data wp search-replace 'http://oldsite.example.com' 'https://newsite.example.com'
Configure Nginx
Create /etc/nginx/sites-available/yoursite.com:
server {
listen 80;
server_name yoursite.com www.yoursite.com;
root /var/www/yoursite.com;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}
location ~ /\.(ht|env) { deny all; }
}
Enable it:
ln -s /etc/nginx/sites-available/yoursite.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Test before DNS cutover
Edit your local /etc/hosts to point yoursite.com at the new VPS IP temporarily. Test in a browser. Verify: pages load, login works, images display, forms submit, search works. Fix anything broken before going live for real users.
The DNS cutover
Once tested, this is the only externally-visible step. To minimize downtime:
- 24 hours before cutover: reduce your DNS TTL to 300 seconds. This makes the eventual switch propagate faster.
- Cutover: change the A record to your new VPS IP. Most providers propagate within minutes; full global propagation is up to the new TTL (5 minutes).
- Get the SSL certificate:
certbot --nginx -d yoursite.com -d www.yoursite.com - Test from external connections. Use a tool like dnschecker.org to confirm DNS has updated.
- After 24 hours: raise TTL back to 3600+ for normal operation.
Don't kill the old site immediately
Keep the old shared hosting account running for at least a week. If you discover something missing — an image, a database row, a custom .htaccess rule — you can still grab it. After a week of zero issues, you can cancel.
Set up backups, properly
You're now responsible for your own backups. The minimum viable approach:
# /etc/cron.daily/backup-yoursite
#!/bin/bash
DATE=$(date +%Y%m%d)
mysqldump -u root wp_yoursite | gzip > /backups/db-$DATE.sql.gz
tar czf /backups/files-$DATE.tar.gz /var/www/yoursite.com
find /backups -mtime +30 -delete
# Sync to off-site
rclone sync /backups remote:my-backup-bucket/yoursite/
Test that you can restore from these backups. An untested backup is theater.
What you've gained
Once migrated:
- Full root access — install whatever you need
- No more "your account is suspended for using too much CPU"
- SSH for real (not the crippled SSH some shared hosts offer)
- Full mail server control if you want to handle mail
- Custom subdomain configurations, multiple sites on one VPS
- The ability to grow with your traffic without "upgrading plans"
The complexity tradeoff is real. Shared hosting hides a lot of operational work. After migration, you've taken that work back. For most sites that have outgrown shared hosting, that's a feature, not a bug.