−50% on all plans · starting at €2.48/mo · Blog·Docs·Sales

From shared hosting to VPS — a migration guide

When to migrate, what to back up, how to set up Nginx + PHP-FPM, and DNS cutover without downtime.

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:

  1. 24 hours before cutover: reduce your DNS TTL to 300 seconds. This makes the eventual switch propagate faster.
  2. 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).
  3. Get the SSL certificate: certbot --nginx -d yoursite.com -d www.yoursite.com
  4. Test from external connections. Use a tool like dnschecker.org to confirm DNS has updated.
  5. 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:

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.


Related articles

Try FranceVPS today

14-day money-back guarantee. No card required to explore. Sovereign French infrastructure.