
Shared hosting is the perfect launchpad for any business, beginner‑friendly and maintenance‑free. But growth hits resource ceilings, slowdowns, and lack of control.
Upgrading to a VPS (Virtual Private Server) gives you dedicated resources, full root access, and isolation. However, the migration itself can be risky, one misstep can cause data loss, downtime, or broken functionality.
When Must You Upgrade?
-
Persistent slow loading despite optimization - images compressed, caching on, yet Time to First Byte stays above 600 ms and full load above 3 s. Shared CPU/RAM contention is the culprit.
-
Frequent “Resource Limit Hit” errors (508/503) - your host suspends processes or throttles your site because you’ve hit inode, entry process, or CPU minute caps.
-
Traffic spikes crash the site - a viral post or sale takes you offline. Shared plans can’t burst; VPS resources are dedicated and scalable.
-
Custom software or PHP extensions are needed - shared hosts lock down the environment. With a VPS you get root, so you can install Redis, Node.js, specific PHP modules, or even a different database version.
-
Security and compliance requirements have risen - taking payments, storing personal data, or needing PCI‑DSS/GDPR mandates isolation. A VPS gives you a private OS instance and a dedicated IP.
-
Email deliverability is tanking - shared IPs are often blacklisted. A VPS with a clean dedicated IP and proper DNS (SPF, DKIM, DMARC, rDNS) fixes your sender reputation.
-
You plan to host multiple high‑traffic sites or resell - you need to partition resources per site, not share a single pool.
How to Migrate from Shared Hosting to VPS
This guide assumes you’re moving from a cPanel‑based shared host to a Linux VPS. Every step includes detailed commands, checks, and rollback options.
Pre‑Migration Preparation
Inventory Everything
Make a complete list of what needs moving:
-
Domains/Subdomains - including parked and add‑on domains.
-
Website files - size, location (
public_html, subdirectory). -
Databases - names, users, prefixes.
-
Email accounts - addresses, passwords, forwarders, autoresponders, and stored mail (IMAP folders).
-
Cron jobs - list all (
crontab -lif you have SSH access; otherwise in cPanel). -
SSL certificates - issuing authority, expiry dates.
-
Third‑party integrations - payment gateways, CDN, external APIs that might have IP whitelisting.
-
PHP version and extensions - note the exact PHP version (e.g., 8.1.27) and active modules. Create a
phpinfo()page on your shared host and save the output.
Choose Your VPS and Environment
-
Managed vs. Unmanaged: If you’re not comfortable with command‑line, choose a managed VPS where the provider handles security and stack.
-
Alternatively, install a free control panel: CloudPanel (very lightweight, Nginx‑focused), HestiaCP (feature‑rich), or aaPanel. Paid cPanel/WHM is also an option but expensive.
-
OS: Ubuntu 24.04 LTS or Rocky Linux 9.
-
Resources: Start with at least 2 vCPU, 4 GB RAM, 50 GB NVMe SSD - you can always resize later.
-
Backup services: Ensure the VPS provider offers automatic off‑site backups or plan to set up your own (we’ll cover it).
Set Up a Testing Domain/Staging Environment
A local hosts file trick tests basic rendering, but a temporary staging subdomain lets you test everything - SSL, emails, redirects, payment gateways - under a real domain before switching live.
-
Create a subdomain like
staging.yourdomain.comand point its A record to the new VPS IP (or use a totally different test domain you own). -
This allows full, secure testing without affecting production.
Lower DNS TTL in Advance
At least 24 hours before the final switch, set the TTL (Time‑to‑Live) of your domain’s A and www records to 300 seconds (5 minutes).This speeds up propagation.
If you’re also changing nameservers, TTL change alone won’t help; you’ll need to plan a longer propagation window. In this guide we stick to A‑record change, which is quicker.
Create a Perfect, Redundant Backup of Your Shared Host
Backup everything - not just files and databases - and verify each backup is restorable.
Full cPanel Backup (if available)
-
Use cPanel’s Backup Wizard → “Full Backup”. Choose to store it in your home directory or download via FTP.
-
This single
*.tar.gzfile contains: all files, databases, email forwarders, filters, and DNS zone files. Download it and store securely off‑server.
Manual, Granular Backups
Even with a full backup, take separate manual copies for extra safety:
Files:
-
Via FTP/SFTP (e.g., FileZilla in binary mode), download the entire
public_html(or the document root of each add‑on domain). -
Compress into a
.zipor.tar.gzlocally for easy upload.
Databases:
-
Export each database via phpMyAdmin. Select the database → Export → Custom → check “Add DROP TABLE / VIEW…” and “Add CREATE DATABASE…”, format SQL, compression gzipped.
-
Alternatively, use cPanel’s “Backup” → download each database.
-
Important: Note the database name, username, and password (they will be re‑created on the VPS). If your CMS config file references a specific MySQL hostname (often
localhost), note that too.
Email (if you plan to move it):
-
Full cPanel backup includes email, but you can also:
-
Use an email client (Thunderbird/Outlook) to connect to each account via IMAP, synchronize all folders, then export locally.
-
For manual server‑level copying, if you have SSH access on shared hosting, copy
/home/username/mail/. Most shared hosts don’t give that, so the cPanel full backup is your best bet.
Cron Jobs:
-
In cPanel, go to “Cron Jobs”, take a screenshot or copy each line.
SSL Certificates:
-
Private keys and certificates can be exported from cPanel’s “SSL/TLS” area, but we’ll generate new ones on the VPS. Keep them as reference.
Email Filtering and Forwarders:
-
Document all forwarders (cPanel → Forwarders) and global email filters (cPanel → Global Email Filters). You’ll recreate them.
Verify Backups
-
Unzip a database backup and check that the SQL file looks complete (tables present).
-
Spot‑check file backup - open a few images, ensure they aren’t corrupted.
-
Golden rule: A backup that hasn’t been tested is not a backup.
Harden and Prepare Your VPS
Before restoring anything, make the server secure.
Initial SSH and Basic Security
Log in as root.
bash
ssh root@your_vps_ip
Update the system:
bash
apt update && apt upgrade -y
Create a new sudo user (replace deploy with your name):
bash
adduser deploy
usermod -aG sudo deploy
Configure SSH key authentication for the new user:
On your local machine, generate a key if you haven’t (ssh-keygen), then copy it:
bash
ssh-copy-id deploy@your_vps_ip
Disable root SSH login and password authentication (only after you’ve tested your key works in a separate terminal window). Edit /etc/ssh/sshd_config:
text
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Then sudo systemctl restart sshd. Keep the current root session open until you confirm the new user can log in.
Firewall (UFW)
bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# If you use a custom SSH port, replace OpenSSH with that port
sudo ufw enable
sudo ufw status verbose
Install Fail2ban
bash
sudo apt install fail2ban -y
sudo systemctl enable fail2ban --now
This protects SSH and eventually web services from brute‑force.
Set Timezone and Hostname
bash
sudo timedatectl set-timezone Asia/Kolkata # or your timezone
sudo hostnamectl set-hostname yourdomain.com
Enable Automatic Security Updates
bash
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
Install the Web Stack and Control Panel (Recommended)
For a safe migration that keeps you in control without deep server admin, install a lightweight control panel. We’ll use HestiaCP as an example - it’s free, open‑source, and includes a web interface, mail server, DNS, and firewall.
Install HestiaCP
Ensure you’re logged in as root (temporarily) or use sudo.
Download the installer and run:
bash
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh
bash hst-install.sh --interactive no --email your@email.com --password YourStrongPass --hostname panel.yourdomain.com --apache no --nginx yes --multiphp yes --mysql-classic yes --named yes --sieve yes --port 2023
(Adjust flags: --apache no --nginx yes gives a fast Nginx + PHP-FPM stack without Apache.)
-
The installer outputs the admin URL (
https://panel.yourdomain.com:8083) and credentials. Save them securely. -
After installation, log into the panel and go through the quick setup wizard.
-
Important: Change the default admin password immediately from the panel.
If you prefer no panel, you can manually install LEMP/LAMP, but a panel dramatically reduces mistakes.
Pre‑Restore Site Setup
Create the hosting environment for your domain(s) before moving data.
Add Your Domain in the Panel
-
In HestiaCP (or whatever panel), add a Web Domain:
yourdomain.com. -
Ensure the “Create DNS zone” option is unchecked (you will manage DNS externally or point it later).
-
The PHP version should match what you noted from the old host (e.g., PHP 8.1). Install extra PHP extensions if needed (via panel or apt).
Create a Staging Subdomain
-
Add a new web domain:
staging.yourdomain.com. This will be your full‑fidelity test environment. We’ll restore the site here first.
Set Up Database and User
-
In the panel's database section, create a new database and user with exactly the same names, username, and password as the old site. This avoids having to update config files. (If you can’t, create any and we’ll update config later.)
-
Note the MySQL host: it will be localhost unless your panel uses a separate database server.
Configure PHP Settings
-
Copy exact
upload_max_filesize,post_max_size,memory_limit, andmax_execution_timefrom the old host’sphpinfo(). Adjust in panel or viaphp.inioverrides.
Restore the Full Site to the Staging Environment
We’ll first restore everything to staging.yourdomain.com, making it a carbon copy of the production site.
Upload Website Files
Using SFTP (FileZilla, key‑based auth), upload the compressed backup of public_html to the staging.yourdomain.com document root (e.g.,/home/deploy/web/staging.yourdomain.com/public_html/).
Extract it using the panel’s file manager or via SSH:
bash
cd /home/deploy/web/staging.yourdomain.com/public_html/
unzip backup.zip # or tar -xzf backup.tar.gz
Ownership and permissions: Run
bash
sudo chown -R deploy:deploy /home/deploy/web/staging.yourdomain.com/public_html/
find /home/deploy/web/staging.yourdomain.com/public_html/ -type d -exec chmod 755 {} \;
find /home/deploy/web/staging.yourdomain.com/public_html/ -type f -exec chmod 644 {} \;
# If you have a special writable directories (uploads, cache), set them to 775/664 if needed.
If the site uses a CMS like WordPress, ensure wp-content/uploads and wp-content/cache are writable:
bash
sudo chown -R www-data:www-data /home/deploy/web/staging.yourdomain.com/public_html/wp-content/uploads
sudo chmod -R 755 /home/deploy/web/staging.yourdomain.com/public_html/wp-content/uploads
Import Databases
Via panel’s phpMyAdmin or command line:
bash
mysql -u database_user -p database_name < /path/to/backup.sql
After import, check if the site configuration file (e.g., wp-config.php) references the correct database credentials. If you used different credentials, update them now.
-
Change site URLs in the database (for WordPress):
Since the site is currently on staging.yourdomain.com, run these SQL queries to replace all old domain references:
sql
UPDATE wp_options SET option_value = 'https://staging.yourdomain.com' WHERE option_name = 'siteurl' OR option_name = 'home';
UPDATE wp_posts SET post_content = REPLACE(post_content, 'https://yourdomain.com', 'https://staging.yourdomain.com');
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'https://yourdomain.com', 'https://staging.yourdomain.com');
(If the site uses a different prefix, adjust accordingly. For other CMS, similarly update base URLs.)
Restore Cron Jobs
-
Add back all cron jobs through the panel or by editing the user’s crontab (crontab -e).
-
Disable them initially - comment them out (#) - so they don’t run during testing and interfere with live sites or send test emails. We’ll enable later.
Test the Staging Site in Full Isolation
This is the most critical safety step. Verify everything works perfectly on the staging subdomain before ever touching live DNS.
Basic HTTP Test
-
Open
http://staging.yourdomain.com(and https later) in a browser. You must see the site exactly as live. -
Click through menus, posts, pages. Check for broken links (might still point to the main domain - we’ll replace them; they should be relative or use the staging URL now).
Test HTTPS with a Trusted SSL Certificate
-
Install a Let’s Encrypt SSL for staging.yourdomain.com. In HestiaCP, go to Web → Edit → SSL → “Generate Let’s Encrypt”.
-
Force HTTPS (enable HSTS if appropriate). Check the padlock, no mixed content errors.
Functionality Checks
-
Forms: Submit a test contact form. Ensure it sends email (we haven’t configured email yet; it may fail - skip for now).
-
E‑commerce (if applicable): Add a product to cart, go to checkout, use sandbox/test payment gateway.
-
Admin panel: Log into WordPress admin (or CMS backend). Verify you can edit posts, upload media, update plugins.
-
Caching: If you use a caching plugin, clear and rebuild cache.
-
Redirects: Check any custom redirects (e.g., old URLs). Test with curl:
bash
curl -I http://staging.yourdomain.com/old-page
Database and File Sync Test
-
Make a small change on the staging site (e.g., a new post, a test comment). Then check if it persists after reload. This confirms database writability.
Check Error Logs
In the panel or via SSH, tail the site’s error log:
bash
tail -f /home/deploy/web/staging.yourdomain.com/logs/error.log
Visit pages and see if any PHP errors pop up. Fix them now - often due to missing PHP extensions or different PHP version.
Perform a Full Backup of the Staging Site (as Restore Point)
Once staging is flawless, take a snapshot: backup the staging files and database again. This will be your “golden” backup. If anything goes wrong during the final switch, you can quickly roll back.
Migrate Email (If Moving Email to VPS)
Email is notoriously tricky. Handle it with extreme care to prevent loss.
Choose Email Strategy
-
Option A (Recommended): Use an external email service (Google Workspace, Zoho, MXRoute) and only update MX records. This keeps email independent from hosting and avoids any migration pain. If you go this route, skip to step 8.
-
Option B: Host email on the VPS using the control panel’s mail server (Exim + Dovecot).
If Hosting on VPS, Restore Email Data
-
Full cPanel backup restoration: If you have the full backup, HestiaCP offers a script to restore it; otherwise you need manual work.
-
Manual method:
-
Recreate every email account in your panel with exactly the same address and password.
-
The old mail data (in
mail/directory of your backup) needs to be placed in the new mail directory. Usually the structure is/home/username/mail/yourdomain.com/account/. Preserve folder structure (cur, new, tmp). -
Use an IMAP sync tool like imapsync to transfer emails directly from the old server to the new one while both are online. This is the safest method:
-
bash
imapsync --host1 old.mail.server --user1 info@yourdomain.com --password1 XXXX --host2 localhost --user2 info@yourdomain.com --password2 YYYY
Run this for each account. This copies all folders and messages without any loss. You can do it before switching DNS - just ensure the new server accepts mail for the domain even without proper DNS (you can add a temporary hosts entry on the VPS).
Recreate email forwarders and filters manually in the panel.
Test Email Sending and Receiving on the VPS
-
Temporarily modify your local machine’s hosts file to point the mail server hostname to the VPS IP, configure an email client, and test sending.
-
Or use webmail (Roundcube/HestiaCP’s webmail) once accounts are created.
Final DNS Configuration and Production Switch
Now you switch production traffic with zero downtime.
Prepare Live DNS Records
-
A record for @ (yourdomain.com) points to VPS IP.
-
A record for www (or CNAME) points to VPS IP.
-
MX record - if you moved email to VPS, update MX to mail.yourdomain.com with priority 10, and create an A record mail pointing to VPS IP. If using external email, set MX records as per that provider.
-
SPF, DKIM, DMARC - ensure they are added. The VPS mail server will generate DKIM keys; add the public key as a TXT record.
-
rDNS (PTR record) - contact your VPS provider to set reverse DNS for your IP to mail.yourdomain.com (important for email deliverability).
Point Production Domain to the Staging Environment? No - Convert Staging to Production.
Actually, you don’t want to keep the staging subdomain. Instead, now you change the site’s configuration back to yourdomain.com on the VPS.
Method:
-
On the VPS, rename the domain: remove staging.yourdomain.com web domain (or just change its document root to your main domain’s).
-
In HestiaCP, you can add the main domain yourdomain.com and set its document root to the same directory that the staging site used.
-
Or simply create a new web domain yourdomain.com and copy everything from staging’s directory to its directory, then remove the staging domain.
-
Update URLs back in the database:
Replace staging.yourdomain.com with yourdomain.com using the same SQL replace queries as before, but reversed. -
Verify SSL: Generate a new Let’s Encrypt certificate for yourdomain.com and www.yourdomain.com.
Now your VPS has the exact production site ready.
The Cutover - Switch DNS
-
In your domain registrar’s DNS zone, update the A record for @ and www to the VPS IP. TTL should already be 300.
-
Do NOT delete the old A record yet - just change it. Save.
-
Immediately after, on the VPS, tail the access log:
bash
tail -f /var/log/nginx/domains/yourdomain.com.log # adjust path
Within a few minutes, you should see hits from remote IPs (not just your tests). This indicates propagation has begun.
Minimize Downtime with a Final Sync
If you have a constantly changing site (e‑commerce, forums), you might lose data between your final backup and DNS switch. To prevent this:
-
Option: Put the old site in maintenance mode, quickly rsync files and dump the database, import to VPS, then switch DNS. This gives a few minutes of planned downtime.
-
Better Option (zero downtime): Use a tool like rsync to sync only changed files, and use database replication (or manually re‑dump/import) without maintenance mode. With low TTL and quick propagation, the window is tiny.
-
For most small to medium sites, the chance of lost transactions during the 5‑15 minute propagation is negligible. The risk is acceptable.
Keep Old Hosting Active
Do not cancel your shared hosting for at least 72 hours. Keep it running as a fallback. If the new server has issues, you can repoint the A record back to the old IP and instantly recover.
Post‑Migration Validation and Hardening
Once traffic is flowing to the VPS, perform final checks and lock things down.
SSL and Mixed Content
-
Force HTTPS. Check with https://www.whynopadlock.com to find any insecure resources.
-
Ensure HSTS header is sent.
Check Email Flow
-
Send test emails from the site (contact form, password resets). Check if they arrive in the inbox (not spam).
-
Use mail‑tester.com to validate SPF, DKIM, DMARC, and rDNS.
Monitor Logs
-
Watch Apache/Nginx and PHP error logs for a few hours to catch any silent errors.
-
Check MySQL slow query log.
Set Up Automated Backups
-
Configure daily off‑site backups - most panels have backup modules that can upload to S3, Google Drive, or SFTP.
-
Schedule: daily database, weekly files, retention of at least 7 days.
Configure a Server Monitoring and Uptime Service
-
Install a lightweight monitoring agent (e.g., Netdata) or use external uptime robot (UptimeRobot, HetrixTools) to alert you if the server goes down.
Enable Caching for Performance
-
Implement full‑page caching with Nginx FastCGI cache or a Redis object cache.
-
If using WordPress, install a caching plugin and pair it with Redis.
Set Up a Staging Workflow for Future Changes
-
Re‑create a permanent staging.yourdomain.com subdomain for testing updates before pushing them to production. Now you have the perfect foundation.
Complete Safety‑Migration Checklist
Pre‑Flight
-
Full cPanel backup downloaded & verified
-
Granular backup of files, databases, emails, cron, filters
-
Inventory of PHP version, extensions, and settings
-
Staging subdomain created and A record pointed to VPS
-
VPS hardened (SSH key, UFW, Fail2ban, unattended upgrades)
-
Control panel installed and configured
-
TTL lowered to 300 seconds (24 h before switch)
Staging Build
-
Files uploaded, permissions set
-
Database imported, credentials matched
-
Site URLs updated to staging domain
-
SSL certificate for staging installed
-
Full functionality test (forms, checkout, admin, redirects)
-
All errors resolved, logs clean
-
Golden backup of staging created
Email Setup (if migrating email too)
-
Email accounts recreated with identical credentials
-
Mail data synced using imapsync (no loss)
-
Forwarders/filters manually recreated
-
DKIM, SPF, DMARC records prepared
-
rDNS requested from VPS provider
Cutover
-
Production domain added on VPS, URLs changed back
-
Live SSL certificate generated for production domain
-
DNS A records updated to VPS IP
-
Old hosting left running as fallback
-
Access logs monitored for live traffic
-
No cancel of old plan for 72 hours
Post‑Migration
-
Mixed content scan and HSTS
-
Email deliverability test (mail‑tester.com ≥ 9/10)
-
Automated off‑site backups configured
-
Monitoring alerts set up
-
Caching layer enabled
-
Original shared hosting cancelled only after full confirmation