Disclosure: we may earn a commission or referral reward from CloudCone, Contabo, DigitalOcean (account credit, not cash), Hostwinds, InterServer, RackNerd, VPSDime and Vultr if you sign up through our links. It does not change the price you pay. Every other provider pays us nothing. Prices in this guide are as of the date shown and may have changed; see the comparison pages for the latest checked prices.
Guides · BLOG
How to move from shared hosting to a VPS in a weekend (2026)
Some prices quoted in this guide are out of date. It is being rebuilt on our price index; for current prices use the comparison pages, which show when each price was checked.
This is the compressed version of the shared-hosting-to-VPS story. If you're technical enough to Google Linux errors, you can do this in a weekend for a WordPress or PHP-based site. This guide breaks it into a Saturday afternoon (setup) and a Sunday morning (cutover) with realistic time estimates.
Prices as of 2026-09-22.
Prerequisites (30 minutes Friday evening)
- A VPS account. Hetzner CX22 at $4.60/mo is the recommended default. Sign up at
console.hetzner.cloud. - SSH key on your local machine.
ssh-keygen -t ed25519if you don't have one. - A backup of your shared hosting site (via cPanel → Backups, or via WordPress plugin like Duplicator).
- A quiet weekend where you can afford 30 minutes of scheduled downtime.
Saturday afternoon: build the VPS (3-4 hours)
Hour 1: Provision and harden
Log into Hetzner Cloud Console. Create a server:
- Ubuntu 24.04 LTS
- Type: CX22 (Falkenstein for EU, Ashburn for US)
- Add your SSH key.
- Create.
SSH in as root. Basic hardening:
```bash
adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
sed -i 's/^#PermitRootLogin./PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication./PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable
apt update && apt upgrade -y
apt install unattended-upgrades -y
```
Hour 2: LEMP stack
```bash
apt install -y nginx php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip mariadb-server certbot python3-certbot-nginx
mysql_secure_installation
```
Create the WordPress database:
```bash
mysql -e "CREATE DATABASE wp; \
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'CHANGEME_strong_password'; \
GRANT ALL ON wp.* TO 'wpuser'@'localhost';"
```
Hour 3: Nginx config
Create /etc/nginx/sites-available/yourdomain.com:
```nginx
server {
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.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 { deny all; }
}
```
ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Hour 4: Import your site
Download your shared hosting backup to your local machine. Extract the public_html folder and the SQL dump.
Upload to VPS:
```bash
scp -r ~/Downloads/public_html deploy@vps-ip:/tmp/
scp ~/Downloads/db.sql deploy@vps-ip:/tmp/
```
On the VPS:
```bash
sudo mv /tmp/public_html /var/www/yourdomain.com
sudo chown -R www-data:www-data /var/www/yourdomain.com
mysql wp < /tmp/db.sql
```
Update /var/www/yourdomain.com/wp-config.php with the new DB name (wp), user (wpuser), password, and host (localhost).
Test with hosts file (before DNS cutover)
Add to your local machine's /etc/hosts:
```
YOUR.VPS.IP.HERE yourdomain.com www.yourdomain.com
```
Visit yourdomain.com in your browser. If the site loads correctly, you're ready to cutover tomorrow.
Remove the hosts entry when done testing.
Sunday morning: DNS cutover (30 minutes downtime max)
30 minutes before: drop DNS TTL
At your DNS provider (Cloudflare, Namecheap, wherever): change the TTL for your A and AAAA records to 300 seconds (5 minutes). Save.
Wait 30 minutes for the old TTL to fully expire from resolvers.
Cutover time
1. Take a final backup on shared hosting. cPanel → Backups → Download.
2. Put the shared hosting into read-only mode or a maintenance page (optional; helps prevent split-brain writes).
3. Change DNS A records at your DNS provider to point at the Hetzner IP.
4. Wait 5 minutes for the low TTL to propagate.
SSL
Once DNS is pointing at Hetzner:
```bash
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
```
Certbot will detect the Nginx config, issue Let's Encrypt certs, and configure HTTPS redirect. Auto-renewal is set up automatically.
Verify
Visit yourdomain.com (in a new incognito window to avoid stale DNS cache). Site should load with a green lock. Check:
- Homepage.
- Login.
- Comments / contact form.
- Any password-protected areas.
If everything works: you're done. Total downtime: 5-15 minutes.
After the migration
Set up backups
Cron a nightly backup:
```bash
sudo nano /etc/cron.d/wp-backup
```
```
0 3 * root mysqldump wp | gzip > /root/backups/db-$(date +\%Y\%m\%d).sql.gz
0 3 * root tar czf /root/backups/files-$(date +\%Y\%m\%d).tgz /var/www/yourdomain.com
0 4 * root find /root/backups -mtime +14 -delete
```
Better: install rclone and sync backups to Backblaze B2 or DO Spaces so they survive VPS destruction.
Uptime monitoring
Sign up for UptimeRobot (free). Set a monitor for https://yourdomain.com. You will now know if the site is down.
Cancel shared hosting
- Download a final backup for insurance.
- Cancel the shared hosting.
- Keep the backup for 60 days.
Total cost comparison
- Shared hosting (Bluehost Choice Plus renewal): $18/mo → $216/year.
- Hetzner VPS after migration: $4.60/mo → $55/year.
Annual saving: $161 for a weekend of work. And every subsequent year saves another $161.
Related guides
- Shared hosting to plain VPS migration — the deeper version of this guide.
- Shared hosting to Cloudways — if you'd rather stay managed at $14/mo.
- Best hosting for a WordPress site — the options in one place.
Prices as of 2026-09-22.