Moving an overgrown cPanel website to a VPS
A long, careful guide for one of the biggest steps a website owner takes.
Your site started small on shared hosting. Now it has years of PHP code, a large MySQL database and regular "resource limit" errors. A VPS gives it its own memory and CPU, but moving it is not a copy and paste. cPanel quietly did a lot of work for you: it ran the web server, set up PHP, kept each site in its own user and handled email. On a VPS, you rebuild each of those yourself.
This guide covers the whole move. You will take stock of what cPanel does for your site, choose a server size and a software stack that behaves like cPanel, export a big database safely, fix the settings that break when you leave cPanel, test everything before anyone sees it, and switch over with as little downtime as possible. It assumes Ubuntu or Debian on the VPS and a PHP site such as WordPress, Joomla, a forum or a custom app. Expect to spend an evening on a rehearsal and a short, planned window on the real switch.
4 GB VPS plans in stock, a common size for a large PHP site
- 4 GB KVM VPS Special3 CPU · 4 GB RAM · 60 GB disk · 7 TB bandwidth · New York, Los Angeles, Chicago$59.99/yrabout $5.00/moView at RackNerd
- Ryzen-4GB (Linux)2 CPU · 4 GB RAM · 30 GB disk · 3 TB bandwidth · New York, Chicago, Seattle$265.57/yrabout $22.13/moView at RackNerd
- KVM-4GB4 CPU · 4 GB RAM · 130 GB disk · 3 TB bandwidth · Los Angeles, Dallas, Chicago$295.08/yrabout $24.59/moView at RackNerd
In stock when this page loaded. See every plan
What is in this guide
- A few words you will see
- Is a VPS the right move?
- The plan in one list
- Before you start
- 1. Take stock of the cPanel account
- 2. Measure the database and pick a size
- 3. Decide what happens to email
- 4. Build and secure the VPS
- 5. Install a cPanel-like stack
- 6. Create a user and PHP pool for the site
- 7. Set up the website in Apache
- 8. Export the files and database
- 9. Import the database
- 10. Put the files in place
- 11. Fix what breaks when you leave cPanel
- 12. Tune MariaDB and PHP for your server
- 13. Test with your hosts file
- 14. Rehearse, then do the real switch
- 15. After the move
- Troubleshooting
- Limits
- When to step up
- What next
A few words you will see
| Term | What it means |
|---|---|
| cPanel | The control panel on your shared hosting. It manages the web server, PHP, databases, email and DNS for you. |
| LAMP | Linux, Apache, MariaDB or MySQL, and PHP: the classic stack for PHP sites, and close to what cPanel runs. |
| PHP-FPM | The program that runs PHP code for the web server. It keeps a group of PHP workers ready. |
| Pool | One group of PHP-FPM workers, with its own user and settings. One pool per site keeps sites apart. |
| MariaDB | A drop-in replacement for MySQL, and the default in Debian and Ubuntu packages. |
| InnoDB | The usual table type in MySQL and MariaDB. Its "buffer pool" is the memory it uses to cache data. |
| mysqldump | The standard tool for exporting a database to a text file of SQL statements. |
| .htaccess | A file in your site's folders that changes Apache settings, such as redirects and pretty URLs. |
| DEFINER | The database user recorded as the owner of views, triggers and stored routines. It often refers to a cPanel user that will not exist on the VPS. |
| Cutover | The moment you switch the domain from the old host to the new one. |
Is a VPS the right move?
Signs your site has outgrown shared hosting:
- "Resource limit reached" or 508 errors, especially at busy times.
- The host has warned you about CPU, memory or process use.
- Pages that use the database are slow, and the database is several hundred MB or more.
- You need a PHP setting, extension or background process that shared hosting will not allow.
Before you commit, be honest about two alternatives. A bigger shared or reseller plan may fix resource limits with no extra work, and keeps cPanel's email and backups. And if you never want to touch a command line, a managed hosting service is a better fit than any unmanaged VPS. A RackNerd VPS is unmanaged: after the move, updates, security and backups are your job. Shared hosting or a VPS covers this choice in more depth.
The plan in one list
- Write down everything cPanel does for your site.
- Build the new server and the software stack, before copying anything.
- Copy the files and database once, and fix what breaks.
- Test the new site privately, while the old one keeps running.
- Rehearse the final copy and time it.
- On the day: freeze the old site, copy the latest data, switch DNS.
- Keep the old account for a few weeks, then cancel.
The old site stays live until the very last step, so you can stop at any point without harm.
Before you start
- A full cPanel backup downloaded to your own computer. This is your safety net if anything goes wrong.
- Your cPanel login, and SSH access to the shared account if your host offers it. SSH makes big databases much easier.
- Access to your domain's registrar and to wherever its DNS records live.
- A note of when the shared hosting renews, so you can finish well before then.
- Some comfort with the Linux command line. Getting started and Linux server tools cover the basics.
In the commands below, cpuser stands for your cPanel username, cpuser_app for your database name, cpuser_appuser for the database user, example.com for your domain, and VPS_IP and SHARED_HOST for the two servers. Replace them with your own.
1. Take stock of the cPanel account
Most failed migrations fail because of something nobody noticed cPanel was doing. Go through cPanel and write down each item below. The page names vary a little between hosts and cPanel versions.
| What to record | Where to find it in cPanel |
|---|---|
| Every domain, add-on domain and subdomain, and its folder | Domains |
| Every database and database user | MySQL Databases |
| PHP version and settings such as memory_limit | MultiPHP Manager and MultiPHP INI Editor, or a "Select PHP Version" page |
| PHP extensions the site uses | The PHP extensions page, if your host has one, or a temporary phpinfo file |
| Scheduled jobs, with the exact commands | Cron Jobs |
| Email accounts, forwarders and autoresponders | Email Accounts and Forwarders |
| Every DNS record, if cPanel runs your DNS | Zone Editor |
| Redirects and password-protected folders | Redirects and Directory Privacy |
| SSL certificates | SSL/TLS Status |
To see the exact PHP version and extensions, you can create a file called info.php in public_html containing <?php phpinfo();, open it in your browser, save the page, and delete the file straight away. It shows details an attacker would like to see.
Also find your app's config file, such as wp-config.php, configuration.php or .env. It holds the database name, user and password, and sometimes full file paths like /home/cpuser/public_html. You will need all of these.
2. Measure the database and pick a size
The database usually decides the server size. The MySQL Databases page in cPanel often shows each database's size. With SSH or phpMyAdmin, you can also run this query:
SELECT table_schema AS db,
ROUND(SUM(data_length + index_length) / 1024 / 1024) AS size_mb
FROM information_schema.tables
GROUP BY table_schema;
Rough starting points, which vary a lot with traffic and code. Watch the server in the first weeks and adjust.
| Database size | Traffic | A sensible starting plan |
|---|---|---|
| Under about 500 MB | Modest | 2 GB VPS, or 4 GB for comfort |
| About 500 MB to 2 GB | Modest to busy | 4 GB VPS |
| About 2 GB to 5 GB | Busy | 6 GB to 8 GB VPS |
| Larger, or very busy | Heavy | A dedicated server, or a separate database server |
Why the database matters so much: MariaDB is fastest when the data it uses often fits in memory. On shared hosting, your database shares one big server's memory with everyone else. On a VPS, it shares only with your own PHP workers, so you can give it a proper amount. Disk also matters: you need room for the site, the database, a dump file during the move, and backups. Plan for at least three times the database size free. How to read VPS specs explains disk types.
6 GB and 8 GB VPS plans in stock, for databases of several GB
- 6 GB KVM VPS Special6 CPU · 6 GB RAM · 100 GB disk · 12 TB bandwidth · New York, Los Angeles, Chicago$89.99/yrabout $7.50/moView at RackNerd
- 8 GB KVM VPS Special7 CPU · 8 GB RAM · 150 GB disk · 20 TB bandwidth · New York, Los Angeles, Chicago$119.99/yrabout $10.00/moView at RackNerd
- KVM-6GB5 CPU · 6 GB RAM · 170 GB disk · 4 TB bandwidth · Los Angeles, Dallas, Chicago$268.59/yrabout $22.38/moView at RackNerd
- Ryzen-6GB (Linux)3 CPU · 6 GB RAM · 45 GB disk · 4 TB bandwidth · New York, Chicago, Seattle$297.97/yrabout $24.83/moView at RackNerd
In stock when this page loaded. See all 6 matching plans
3. Decide what happens to email
This is the decision people forget until mail stops arriving. On shared hosting, cPanel runs email for your domain. A fresh VPS does not. You have three options:
| Option | Effort | Notes |
|---|---|---|
| Move mail to a dedicated email service | Low to medium | Usually the best choice. Reliable delivery, spam filtering and webmail are handled for you. Move mailboxes with the service's import tool. |
| Keep a small shared plan for email only | Low | Works if your host allows it. Point the MX records at it and the website at the VPS. |
| Run a mail server on the VPS | High | Not recommended. It is hard to keep secure and out of spam folders, and uses a lot of memory. |
There is a second, quieter email job: the mail your site sends, such as contact forms, password resets and order confirmations. On cPanel, PHP's mail function just worked. On a VPS it will not, unless you set something up. The reliable way is to send through an email or transactional email service over authenticated SMTP, using your app's SMTP settings or a plugin. Some providers restrict outgoing connections on port 25 from new servers, so SMTP on port 587 with a login is the usual choice. Set up the SPF and DKIM records the service gives you. Domains and DNS explained covers those records.
4. Build and secure the VPS
Treat the VPS as a new server and secure it before installing anything: update it, create your own user, switch to SSH keys, turn off password login and turn on the firewall. Your first hour on a new RackNerd VPS walks through each step, and securing your VPS goes further.
Pick a data centre near most of your visitors. Choosing a VPS location shows how to test.
5. Install a cPanel-like stack
cPanel servers usually run Apache, and most cPanel sites depend on .htaccess files for redirects, pretty URLs and access rules. nginx is fast and popular, but it ignores .htaccess, so every rule would need rewriting by hand. For a migration, Apache with PHP-FPM is the least painful choice. You can always change later. Web servers compares the options.
Install Apache, MariaDB, PHP-FPM and the common PHP extensions:
sudo apt update
sudo apt install apache2 mariadb-server php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl php-bcmath ssl-cert
Compare php -m with the extension list from step 1, and install anything missing with sudo apt install php-NAME. If a package name is not found, search with apt search php-.
Turn on the Apache modules this guide uses, and open the web ports:
sudo a2enmod proxy_fcgi setenvif rewrite headers ssl
sudo systemctl restart apache2
sudo ufw allow "Apache Full"
The PHP-FPM package may suggest enabling a global PHP config for Apache. Skip it. Your site will use its own pool, set up in step 6.
Secure MariaDB with its setup script. It is called mariadb-secure-installation on newer systems and mysql_secure_installation on older ones:
sudo mariadb-secure-installation
Accept the defaults that remove anonymous users, remote root login and the test database. MariaDB listens only on 127.0.0.1 by default on Debian and Ubuntu. Keep it that way.
A note on PHP versions. Run php -v to see the version your system provides. If it is newer than the one on cPanel, test carefully: old code can break on newer PHP. If your site only works on an old, unsupported PHP version, that is a warning sign. Updating the app is the safer fix. Third-party packages that provide other PHP versions exist, but follow their own documentation and understand who maintains them before relying on them.
6. Create a user and PHP pool for the site
On cPanel, your site's PHP code ran as your own account user, not as a shared web server user. That kept your files private from other sites. You can do the same on a VPS: create a Linux user for the site and a PHP-FPM pool that runs as that user.
Use your cPanel username for the site user. Then the site lives at /home/cpuser/public_html, exactly as before, and any full paths in your config files and cron jobs keep working.
sudo adduser --disabled-password --gecos "" cpuser
sudo mkdir -p /home/cpuser/public_html /home/cpuser/logs /home/cpuser/tmp
sudo chown -R cpuser:cpuser /home/cpuser
sudo chmod 750 /home/cpuser
sudo usermod -aG cpuser www-data
The last line lets Apache read the site's files through the group, so it can serve images, CSS and scripts. PHP code runs as cpuser through the pool.
Find your PHP version folder with ls /etc/php. In the commands below, PHPVER stands for it, for example 8.3. Create the pool file:
sudo nano /etc/php/PHPVER/fpm/pool.d/cpuser.conf
Put this in it:
[cpuser]
user = cpuser
group = cpuser
listen = /run/php/cpuser.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
php_admin_value[error_log] = /home/cpuser/logs/php-error.log
php_admin_value[upload_tmp_dir] = /home/cpuser/tmp
php_admin_value[session.save_path] = /home/cpuser/tmp
php_value[memory_limit] = 256M
php_value[upload_max_filesize] = 64M
php_value[post_max_size] = 64M
php_value[max_execution_time] = 120
Use the PHP settings you recorded in step 1 instead of these example values. The pm.max_children number is a starting guess. Step 12 shows how to set it properly. Check the config and restart PHP-FPM:
sudo php-fpmPHPVER -t
sudo systemctl restart phpPHPVER-fpm
For example, with PHP 8.3 these are php-fpm8.3 -t and php8.3-fpm. If you host several sites, give each its own user and pool.
7. Set up the website in Apache
Create a site config file:
sudo nano /etc/apache2/sites-available/example.com.conf
Put this in it. The first block answers on plain HTTP. The second answers on HTTPS with a temporary self-signed certificate, so you can test the site before the real certificate exists:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /home/cpuser/public_html
<Directory /home/cpuser/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/cpuser.sock|fcgi://localhost"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /home/cpuser/public_html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
<Directory /home/cpuser/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/cpuser.sock|fcgi://localhost"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
AllowOverride All lets your existing .htaccess files work. Options -Indexes stops Apache listing the contents of folders with no index page. Enable the site, turn off Apache's default site, check the config and reload:
sudo a2ensite example.com
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl restart apache2
A full restart, rather than a reload, makes sure Apache picks up its new membership of the site's group from step 6. For add-on domains, add another config file with its own ServerName and DocumentRoot.
8. Export the files and database
There are three ways to get your data out of cPanel. Use the first one your host allows.
With SSH on the shared account (best)
Log in to the shared account over SSH. Your host's help pages give the address and port, which is sometimes not the usual 22. Then, from your home folder:
cd ~
tar czf site-files.tar.gz public_html
mysqldump --single-transaction --quick --routines --triggers --default-character-set=utf8mb4 -u cpuser_appuser -p cpuser_app | gzip > cpuser_app.sql.gz
Enter the database password when asked. What the options do:
--single-transactiontakes a consistent copy of InnoDB tables without locking the site.--quickstreams large tables instead of loading them into memory, which matters on a tight shared account.--routines --triggersinclude stored routines and triggers, which are easy to lose.--default-character-set=utf8mb4keeps emoji and accented characters intact.
If the database has old MyISAM tables, --single-transaction does not protect them, so export at a quiet time. Keep both files in your home folder, never in public_html, where anyone could download them.
With cPanel's Backup page
Most cPanel accounts have a Backup page that lets you download a home directory backup and a separate backup of each database, both compressed. This works well for large databases, as the server does the export. Download them to your computer.
With phpMyAdmin (last resort)
phpMyAdmin's export works for small databases, but often times out or produces broken files for large ones. If it is your only option, export a few tables at a time.
Getting the files to the VPS
From the VPS, copy them straight from the shared account. Add -P PORT if your host uses a different SSH port:
scp cpuser@SHARED_HOST:site-files.tar.gz ~/
scp cpuser@SHARED_HOST:cpuser_app.sql.gz ~/
If you downloaded backups to your computer, upload them from there with scp FILE alex@VPS_IP: instead. For very large file trees, rsync is better, as it can be re-run later and only sends changes. Step 14 uses that.
9. Import the database
Create the database and user on the VPS with the same names and password as on cPanel. Then your app's config file works without changes. Open the MariaDB shell with sudo mariadb and type:
CREATE DATABASE cpuser_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'cpuser_appuser'@'localhost' IDENTIFIED BY 'THE_SAME_PASSWORD';
GRANT ALL PRIVILEGES ON cpuser_app.* TO 'cpuser_appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Typing the password in the MariaDB shell keeps it out of your shell history. If your old database used a different character set, check it on cPanel with SHOW CREATE DATABASE cpuser_app; and match it.
Now import. Dumps from cPanel often contain DEFINER clauses naming a cPanel user that does not exist on the VPS. Views, triggers and routines then fail to import, or fail later. Strip those clauses during the import:
zcat ~/cpuser_app.sql.gz | sed -E 's/DEFINER=`[^`]+`@`[^`]+`//g' | sudo mariadb cpuser_app
Without a DEFINER, each view or routine is owned by the user that imports it, which is fine for most sites. If your app relies on a specific owner, check its documentation.
A large import can take a long time. If it stops with "MySQL server has gone away" or a packet size error, raise max_allowed_packet, as shown in step 12, restart MariaDB, drop and recreate the database, and import again.
Check the import is complete. Run this on both servers and compare the results:
SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'cpuser_app';
USE cpuser_app;
SELECT COUNT(*) FROM wp_posts;
Replace wp_posts with a few of your app's busiest tables. The table counts should match exactly, and the row counts of the tables you pick should match too.
10. Put the files in place
sudo tar xzf ~/site-files.tar.gz -C /home/cpuser
sudo chown -R cpuser:cpuser /home/cpuser/public_html
sudo find /home/cpuser/public_html -type d -exec chmod 750 {} +
sudo find /home/cpuser/public_html -type f -exec chmod 640 {} +
These permissions let the site user read and write everything, let Apache read through the group, and keep everyone else out. Lock down the config file that holds the database password:
sudo chmod 600 /home/cpuser/public_html/wp-config.php
Use your own app's config file name. PHP runs as cpuser, so it can still read the file. If your app checks the database host, localhost is correct on the VPS, as it usually was on cPanel.
Delete the archive and dump from your home folder once you are sure the import worked. They hold your whole site and database.
11. Fix what breaks when you leave cPanel
These are the problems that catch almost everyone. Check each one before testing.
- cPanel's PHP handler lines. cPanel's PHP version manager often adds a block to
.htaccessthat starts with a comment about a "cPanel-generated handler" and contains anAddHandlerline naming a PHP version. On the VPS this line can make Apache show raw PHP code or return errors. Remove the whole block. - php_value and php_flag lines. These
.htaccesssettings only work with an older way of running PHP. With PHP-FPM they cause a 500 error. Move each setting into the pool file from step 6, or into a.user.inifile in public_html, which PHP-FPM reads:memory_limit = 256M upload_max_filesize = 64M - Hard-coded paths. If you did not use your cPanel username for the site user, search for the old path and update it:
grep -rl "/home/cpuser" /home/cpuser/public_html --include=*.php - Cron jobs. Recreate each one from step 1 in the site user's crontab with
sudo crontab -u cpuser -e. cPanel cron commands often call PHP through a path like/usr/local/bin/phpor a versioned cPanel binary. On the VPS, use/usr/bin/php. - Outgoing mail. Set up SMTP as decided in step 3. Contact forms and password resets will silently fail otherwise.
- Caching and security plugins. Some write server-specific rules or paths. Clear their caches, and check their settings after the move.
- Password-protected folders. cPanel's Directory Privacy stores passwords in files under your home folder. Check the
AuthUserFilepath in each.htaccessand make sure the file exists on the VPS. These files are usually in a.htpasswdsfolder in your cPanel home, outside public_html, so the archive from step 8 does not include them. Copy that folder separately to the same path under/home/cpuser, and give it to the site user withchown -R.
Find every .htaccess line that needs attention with:
grep -rn -E "php_value|php_flag|AddHandler|AuthUserFile" /home/cpuser/public_html --include=.htaccess
12. Tune MariaDB and PHP for your server
Default settings are made for tiny servers. A few changes make a big difference for a large database. Create a settings file:
sudo nano /etc/mysql/mariadb.conf.d/99-tuning.cnf
Example values for a 4 GB VPS that runs both the site and the database. They are illustrative starting points, not a recipe:
[mysqld]
innodb_buffer_pool_size = 1G
max_connections = 80
max_allowed_packet = 64M
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
Restart with sudo systemctl restart mariadb.
- innodb_buffer_pool_size is the most important setting. On a server shared with PHP, roughly a quarter to a third of total RAM is a common starting point. If the database is smaller than that, there is no need to go higher. On a server that only runs the database, it can be much larger.
- max_connections should be a little above the total of your PHP pools'
pm.max_children. Each connection can use memory, so do not set it in the thousands. - The slow query log records queries that take longer than two seconds. Check
/var/log/mysql/slow.logafter a few days. It often points to one or two queries worth fixing.
Now set pm.max_children properly. It is the most PHP workers that can run at once, and each one uses memory. Once the site is running and has had some test traffic, check how much memory each worker uses:
ps -eo rss,user,comm | grep php-fpm
The first column is memory per process in KB. Work out the rest from a simple sum. This example is illustrative: on a 4 GB server, leave about 1 GB for the system and Apache and 1 GB for MariaDB. That leaves about 2 GB for PHP. If each worker uses about 80 MB, 2,000 MB divided by 80 MB is 25 workers. Set pm.max_children a little below that and restart PHP-FPM.
Setting it too high is worse than too low. Too low makes visitors wait briefly at busy moments. Too high makes the server run out of memory and slow everything down. Swap is a safety net, not extra RAM.
Finally, check OPcache is on. It keeps compiled PHP in memory and makes large PHP apps much faster. It is usually enabled by default. Check with a temporary phpinfo page, as in step 1, and look for "opcache.enable" set to On. Delete the page afterwards.
13. Test with your hosts file
You can use the new site privately while the world still sees the old one. On your own computer, add a line to the hosts file:
VPS_IP example.com www.example.com
On macOS and Linux the file is /etc/hosts. On Windows it is C:\Windows\System32\drivers\etc\hosts, edited as administrator. Your browser now goes to the VPS for your domain. Expect a certificate warning, because of the temporary certificate from step 7. That is expected for testing, so accept it for this test only.
Test as a real user would, and keep notes:
- Load the home page and the busiest pages, and compare their speed with the old site.
- Log in, and log in to the admin area.
- Search, filter and page through long lists, which are the heaviest database queries.
- Upload an image or file, to test permissions on upload folders.
- Submit a contact form, and reset a password, to test outgoing mail.
- If you take payments, use the payment provider's test mode, never a real card.
- Run each cron job by hand once.
Watch the logs in a second terminal while you test:
sudo tail -f /var/log/apache2/example.com-error.log /home/cpuser/logs/php-error.log
Remove the hosts file line when you finish, or you will keep seeing the new server after you thought you had stopped testing.
14. Rehearse, then do the real switch
A big database can take a long time to export, copy and import. You need to know how long before choosing a time to switch. So do a rehearsal: repeat steps 8 and 9 once and time each step. That total is roughly how long the site will be frozen on the day.
At least a day before, lower the TTL on your domain's A records to a short value, such as 300 seconds, so the switch takes effect quickly. If cPanel runs your DNS, also move DNS now. Copy every record from the Zone Editor, including MX and TXT records for email, to your registrar or DNS provider, and change the nameservers. Do this before the switch day, so DNS problems and site problems never happen at the same time. Domains and DNS explained covers this.
On the day, at a quiet time:
- Freeze the old site. Put it into maintenance mode, or turn off logins and checkout, so no new data is written. Pause its cron jobs in cPanel.
- Copy the latest files. rsync only sends what changed. Run it as your own user, not with sudo, so it uses your SSH login. It pulls into a folder in your home, which you then copy into place:
Addrsync -a cpuser@SHARED_HOST:public_html/ ~/site-sync/ sudo rsync -a ~/site-sync/ /home/cpuser/public_html/ sudo chown -R cpuser:cpuser /home/cpuser/public_html-e "ssh -p PORT"to the first command for a non-standard SSH port. rsync must be available on both servers. On the VPS, install it withsudo apt install rsyncif it is missing. On the shared account, it usually is available when SSH is offered. To make the day itself faster, run the first command once during the rehearsal, so the final run only sends the latest changes. Without SSH on the shared account, upload just the changed folders, such as uploads. - Export the database again, as in step 8, and copy it across.
- Replace the test database. In
sudo mariadb, runDROP DATABASE cpuser_app;andCREATE DATABASE cpuser_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;, then import as in step 9. The user and its permissions remain. - Check the counts again, as in step 9.
- Switch DNS. Change the A records for the domain and www to the VPS address.
- Get the real certificate. Once the domain points at the VPS, install and run Certbot. It replaces the temporary certificate in your config:
sudo apt install certbot python3-certbot-apache sudo certbot --apache -d example.com -d www.example.com - Take the site out of maintenance mode on the VPS, if the frozen state was copied across.
- Watch. Follow the access log to see visitors arrive:
sudo tail -f /var/log/apache2/example.com-access.log.
If something goes badly wrong, switch the A records back to the old host. Because the old site was only frozen, not changed, it is still a working copy. That is why the old account stays until the end.
15. After the move
- Watch the first week. Check
free -h, the error logs and the slow query log daily. Adjustpm.max_childrenand the buffer pool if memory runs tight. - Set up backups now, not later. Back up the files and a nightly database dump to somewhere off the server. Using a storage VPS for backups has a script you can adapt. Test a restore.
- Keep it updated. Turn on automatic security updates, and keep your app, themes and plugins updated as before. Nobody does this for you now.
- Database admin. Do not install phpMyAdmin openly on the internet. Use a desktop database tool, such as DBeaver or HeidiSQL, through an SSH tunnel:
ssh -L 3306:127.0.0.1:3306 alex@VPS_IP, then connect the tool to127.0.0.1on your own computer. - Protect logins. Large PHP sites attract password-guessing bots. A tool such as fail2ban can block repeat offenders. Follow its documentation.
- Raise the DNS TTL back to a normal value after a few days.
- Cancel the shared account last. Wait two weeks or so, check that its access log shows no real visitors, download one final full cPanel backup to keep offline, then cancel before the renewal date.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 500 Internal Server Error | A php_value, php_flag or unsupported line in .htaccess | Check the site's Apache error log. Move PHP settings to the pool or .user.ini |
| Browser downloads PHP files, or shows raw code | cPanel's AddHandler block in .htaccess, or the PHP handler lines missing from the site config | Remove the cPanel handler block. Check the FilesMatch lines in step 7 |
| 503 Service Unavailable | PHP-FPM pool not running, or wrong socket path | Run sudo systemctl status phpPHPVER-fpm and check the socket path matches in both files |
| "Error establishing a database connection" | Database user, password or name differs from the config file | Recreate the user with the same details, or update the config |
| Import fails with an access denied or DEFINER error | DEFINER clauses naming a cPanel user | Import again with the sed command in step 9 |
| "MySQL server has gone away" during import | max_allowed_packet too small | Raise it as in step 12, restart MariaDB, and import into a fresh database |
| Strange characters instead of accents or emoji | Character set mismatch | Export with --default-character-set=utf8mb4 and create the database with utf8mb4 |
| White page with no error | A missing PHP extension, or errors hidden | Check the PHP error log. Compare php -m with your list from step 1 |
| Uploads fail with permission errors | Upload folder not owned by the site user | Run the chown command in step 10 again |
| Endless redirect loop | A forced-HTTPS rule before HTTPS works, or the site URL setting differs | Check the 443 block exists, and check the app's site URL setting |
| Contact form does nothing | No outgoing mail on the VPS | Set up SMTP as in step 3 |
| Slow pages under load | Too few PHP workers, a small buffer pool, or slow queries | Measure as in step 12 and check the slow query log |
| Server runs out of memory | pm.max_children or the buffer pool set too high | Lower them, then restart PHP-FPM and MariaDB |
Limits
- You lose cPanel. No more one-click installs, email management or point-and-click backups. You can install a control panel on a VPS, but cPanel itself needs a paid licence for VPS use. Free panels exist too. Check any panel's licence and memory needs before you rely on it.
- Some downtime for writes is hard to avoid. For the length of the final copy, the site must be frozen, or late changes are lost. Rehearse to keep it short.
- Old code can break. A newer PHP or MariaDB version can expose old bugs. Test thoroughly before switching, not after.
- Online shops need extra care. If you handle card payments or personal data, check your obligations before moving. Never copy live payment details onto test systems.
- Fair use still applies. Unmetered does not mean unlimited. A VPS that runs at full CPU for long periods may be limited by the provider. RackNerd's acceptable use policy covers your site, and software licences still apply to your apps and themes.
- Figures here are examples. Memory sizes, worker counts and buffer pool values vary with your site. Measure your own server.
- This guide is about one site. Many sites, or an agency's whole cPanel account, need more planning. Reseller hosting for freelancers may be a better fit for client sites.
When to step up
If, after tuning, the server still runs short of memory or the slow query log keeps filling, step up a size. The usual order is to give the database more memory first, then add PHP workers. If the database alone needs most of the server, consider splitting it onto its own VPS so it does not compete with PHP for memory. For a very large database or steady heavy traffic, a dedicated server is the next step. VPS or dedicated server? helps you decide, and moving to a new VPS covers the next move.
Dedicated servers in stock with the most memory, for very large databases
- [UT] Dual Intel Xeon E5-2695 V3 - 512GB RAM - 10x 1.6TB SSD56 threads · 512 GB RAM · 16 TB disk · 50 TB bandwidth$6,948.00/yrabout $579.00/moView at RackNerd
- 10Gbps - Dual Xeon E5-2640 V3 Dedicated32 threads · 256 GB RAM · 5 TB disk · 100 TB bandwidth$3,588.00/yrabout $299.00/moView at RackNerd
- Dual Intel Xeon E5-2650 v2 | 256GB RAM | 4x 2TB SSD32 threads · 256 GB RAM · 8 TB disk · 100 TB bandwidth$4,188.00/yrabout $349.00/moView at RackNerd
- Dual Intel Xeon E5-2680 v4 | 256GB RAM | 4x 2TB SSD56 threads · 256 GB RAM · 8 TB disk · 100 TB bandwidth$4,788.00/yrabout $399.00/moView at RackNerd
In stock when this page loaded. See all 72 matching plans
What next
- Keep an eye on the server: My VPS is slow or down: what to check.
- Add a staging copy for testing updates: a VPS as a dev and staging server.
- For a WordPress-only site, see WordPress on a VPS for WordPress-specific settings.
To compare what is available now, start with the cheapest RackNerd VPS guide and check the sales page.