"Shared hosting vs VPS?" is not a question with one answer for all time. The answer changes on the day your site starts getting real traffic, a 503 error shows up every day at noon, and support only tells you "your CPU usage is high". This article lists the signs that you have outgrown shared hosting, shows which quota has filled up and where to see it, and gives a practical migration checklist for PHP and WordPress sites, including the two traps, SSL and email, that ruin most migrations.
Signs that you have outgrown shared hosting
Shared hosting is like an apartment in a crowded building: cheap and hassle-free, as long as the neighbors stay quiet. Dozens (sometimes hundreds) of sites sit side by side on one server, and the host uses tools such as CloudLinux to put a usage ceiling on each of them. These are the signs that you are hitting that ceiling:
- 503 errors or 508 (Resource Limit Is Reached) errors at busy hours: requests exceed your quota, and visitors are left standing at the door.
- Unexplained, intermittent slowdowns: the site has not changed, yet some days it is sluggish. This is the "neighbor effect": a resource-hungry site next door swallows the shared disk and CPU.
- A wall of restrictions: a fixed PHP version, a short
max_execution_time, modules that cannot be installed, and on some hosts, cron jobs that cannot run more often than every 15 minutes. - No root access: Redis for WordPress caching? Node.js alongside PHP? The answer is always the same: "not available on your plan".
- A shared IP address: if one of your neighbors sends spam, the IP gets blacklisted and your emails start landing in spam too.
- An inode ceiling: besides disk space, the number of files (inodes) is counted as well; a site with tens of thousands of thumbnail images hits this ceiling long before the gigabytes run out.
If more than two of these sound familiar, the problem is not your site; it is the container it lives in. But before you swap the container, find out which wall you are pushing against: that one number decides the size of your next server.
Where do 508 and 503 errors come from? Which quota is full?
On hosts managed with CloudLinux, whether the panel is cPanel or DirectAdmin, each account lives inside its own isolated container called an LVE (Lightweight Virtual Environment), with several independent limits; these are the six that matter. So "your usage is high" means practically nothing: each limit has its own symptom and its own fix. The table below is based on CloudLinux's documentation on limits:
| Quota | What it limits | Symptom when it is full | What to do next |
|---|---|---|---|
| SPEED | CPU share, as a percentage of one core | Slowness, with no error | Find the heavy query or plugin |
| EP | Number of concurrent dynamic requests (entry processes) | 508 errors at busy hours | Enable page caching or get a higher EP limit |
| PMEM | Physical RAM used by the account's processes | A process gets killed; 500 or 503 error | Cut the memory each PHP request uses |
| NPROC | Total number of processes and threads | New processes cannot start; 500 or 503 | Run fewer cron jobs at the same time |
| IO | Disk read and write speed (KB/s) | Slowness during uploads and backups | Move heavy jobs to quiet hours |
| IOPS | Number of disk operations per second | Short pauses with no pattern | Fix queries that have no index |
The biggest misunderstanding concerns the first two rows: a 508 error almost always comes from EP, not from CPU. Even with an idle processor, twenty visitors loading an uncached page at the same time fill the ceiling, which is why "upgrading to a bigger plan" sometimes makes no difference at all.
So that you do not have to guess, open the resource usage report in your control panel: next to the usage of each quota, the number of faults tells you how many times you have hit that ceiling. A column with non-zero faults is your wall; the rest is noise. CloudLinux's documentation suggests these values for a typical account: 100 percent of one core, 1 GB of memory, 20 concurrent requests and 100 processes.
Shared hosting vs VPS at a glance
The difference between shared hosting and a VPS fits in one sentence: with shared hosting you rent "space on someone else's server"; with a VPS you rent "a server of your own". A VPS is built with KVM virtualization: the RAM and CPU cores really are yours, you choose the operating system yourself and you have root access.
| Criterion | Shared hosting | Virtual server (VPS) |
|---|---|---|
| Resources (CPU/RAM) | Shared with dozens of sites | Dedicated and isolated (KVM) |
| Access | Control panel and FTP | Full root + SSH |
| Software | The host's fixed list | Anything you like: Redis, Node.js, Docker |
| IP address | Shared | Dedicated |
| Linux kernel | Shared; sysctl tuning and modules not allowed | Your own kernel; any module and any tuning |
| Server management and security | The host's job | Your job |
| Scalability | Change plans, within limits | Add resources in a few clicks |
| Billing unit | Usually monthly or yearly | Can be hourly |
So the answer to "hosting or VPS?" comes down to two things: how many resources you need, and how much responsibility you are willing to take on. The sixth row is the main price you pay for that freedom.
What is KVM, and why is not every "virtual server" the same?
The term "virtual server" is used for two different technologies. With container virtualization (OpenVZ and LXC), all the machines run on one shared Linux kernel: kernel modules cannot be loaded, some sysctl settings are out of reach, there is no real swap, and the figures that free shows are sometimes the host's RAM rather than your share of it. With KVM, each machine boots its own kernel and, as far as the operating system is concerned, is a complete server. Two commands are enough to be sure:
systemd-detect-virt
kvm
lscpu | grep -iE 'hypervisor|virtualization'
Hypervisor vendor: KVM
Virtualization type: full
An output of kvm means a real, separate machine; lxc or openvz means that what you bought is not what you thought it was. Check the disk while you are at it:
lsblk -d -o NAME,ROTA,SIZE
NAME ROTA SIZE
vda 0 50G
nproc
2
free -m | head -2
total used free shared buff/cache available
Mem: 3936 512 2870 12 554 3180
A ROTA value of 0 means SSD or NVMe, and 1 means a mechanical hard drive. Check these three numbers in the first minute after the server is delivered, not after you have moved the site.
What you gain on a VPS, and what responsibility you take on
Freedoms
On a VPS you assemble the software stack yourself: Nginx with PHP-FPM, object caching with Redis, whatever PHP version you want, and a cron job every minute if you need one. More important than all that, though, is the freedom to "measure": on shared hosting you are at the mercy of an invisible ceiling, while on a VPS you can see what is eating your RAM and which query is slow, and then change exactly that.
Responsibilities
In return, the jobs that until yesterday were "the host's responsibility" are now yours; our guide to Linux server security walks through all of them step by step. The day-one minimum is four tasks, about half an hour of work in total: logging in with an SSH key instead of a password, a firewall with only three open ports (22, 80 and 443), automatic security updates, and a nightly backup cron job whose output is stored off the server:
ufw default deny incoming
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Measure these two numbers before you decide
Migrating without numbers turns a well-defined problem into a vague one. First, get the real size of your files and database; that determines both the disk size you need and how long the transfer will take:
du -sh public_html
3.4G public_html
mysql -u DB_USER -p -e "SELECT table_schema AS db,
ROUND(SUM(data_length+index_length)/1024/1024,1) AS mb
FROM information_schema.tables GROUP BY table_schema;"
+-------+-------+
| db | mb |
+-------+-------+
| wp_db | 412.6 |
+-------+-------+
Second, the RAM each PHP request consumes. The rule for tuning PHP-FPM is simple: the number of concurrent processes equals the RAM you can allocate, divided by the average RAM per process. The average is something you can measure:
ps -ylC php-fpm8.3 --sort:rss | awk 'NR>1 {s+=$8; n++} END {printf "%.0f MB avg over %d procs\n", s/n/1024, n}'
61 MB avg over 8 procs
A back-of-the-envelope calculation: on a server with 4 GB of RAM, set aside 1 to 1.5 GB for the operating system, Nginx, MariaDB and a safety margin; at an average of 60 MB, the rest gives you about 40 concurrent processes. Compare that number with the EP faults on your current host, and you will know what size of server solves the problem and what size is just extra money.
Checklist: moving a PHP or WordPress site from shared hosting to a VPS
Migration sounds scary, but it comes down to seven well-defined steps, and if you keep to the order, you do not need a single minute of downtime:
- Take inventory. Write down the PHP version and the active extensions (from phpinfo), the cron jobs and the
.htaccesssettings. Get the same list on the new server withphp -mand compare the two; a missing module is the most common cause of a 500 error in the first hour. - Take a full backup. Do it from the control panel or, if your host gives you SSH:
# on the old shared host mysqldump --single-transaction --default-character-set=utf8mb4 \ -u DB_USER -p DB_NAME > site-db.sql tar -czf site-files.tar.gz public_html/--single-transactiontakes an InnoDB dump without locking the site, and utf8mb4 keeps non-Latin text, such as Persian, from being corrupted. - Prepare the new server. Create an Ubuntu VPS and set up the Nginx + PHP-FPM + MariaDB stack; our guide to setting up a website with Nginx on Ubuntu follows exactly this path.
- Move the files and the database. Copy the backup across and restore it:
Then update the connection details in# on the new VPS mysql -u wp_user -p wp_db < site-db.sql tar -xzf site-files.tar.gz -C /var/www/example.com/ chown -R www-data:www-data /var/www/example.com/wp-config.php. For sites of several gigabytes,rsyncis the better tool: the first run copies everything and later runs copy only the differences, so move the initial copy a week ahead and sync only the changes on migration night:rsync -az --delete -e ssh public_html/ root@203.0.113.10:/var/www/example.com/ - Test before changing DNS. Edit the hosts file on your own computer so that the domain temporarily points to the new server, without the rest of the world noticing:
Now go through the whole site: pages, forms, logging in to the admin dashboard, uploading an image. In the terminal, curl's# local hosts file (Linux/Mac: /etc/hosts) # Windows: C:\Windows\System32\drivers\etc\hosts 203.0.113.10 example.com www.example.com--resolveoption applies the same mapping to a single request:curl -sI --resolve example.com:80:203.0.113.10 http://example.com/ \ | grep -Ei 'HTTP/|^server|^x-powered-by' HTTP/1.1 200 OK Server: nginx/1.24.0 X-Powered-By: PHP/8.3.14 - Switch DNS. A day or two ahead, set the TTL of the A record to something like 300 seconds; then change the IP address and enable SSL. The rest of the zone matters more than the A record: if you are rebuilding the zone from scratch, carry over the MX and TXT records (SPF, DKIM and DMARC) and every ownership-verification record exactly as they are. Our guide to DNS records covers them in detail.
- Do not rush to close the old hosting account. Keep both running for a few days; going back is then just one DNS change. But as long as both are live, some visitors will still reach the old server, so if the site takes orders, stop writes to the old copy after the switch so that your data does not end up split in two.
.htaccess rules do not run on Nginx, and they do not raise an error either; they are silently ignored. That means rewrites, old redirects and security headers stop working without any warning. Before switching DNS, translate every rule into its equivalent inside the server block; on Apache, move the file over as it is.SSL and email: two traps that ruin migrations
You cannot get the SSL certificate before switching DNS
Many people want everything ready before the DNS switch, HTTPS included, and run into a dead end. Let's Encrypt's default challenge, HTTP-01, places a file under /.well-known/acme-challenge/ on port 80 and fetches it from outside, so the name being validated must reach that very server at that moment. According to Let's Encrypt's documentation on challenge types, this challenge follows up to 10 redirects, but its starting point is whatever the DNS record says right now.
You have two options: accept the order (DNS first, then the certificate), or use the DNS-01 challenge, which asks for a TXT record at _acme-challenge.example.com instead of a file and does not care about the IP address. The steps for both are in our guide to installing free SSL with Certbot.
certbot in a loop while you practice. Let's Encrypt's rate limits are explicit: 5 certificates for the same set of names every 7 days, 50 certificates per registered domain every 7 days, and 5 failed validations per name, per account, per hour. For testing, the dry-run mode does not use up your quota:
certbot certonly --nginx -d example.com -d www.example.com --dry-run
Simulating a certificate request for example.com and www.example.com
The dry run was successful.A VPS does not send email by default
Shared hosting comes with a ready-made mail server; a bare VPS does not. If your contact form and order emails stop arriving after the migration, nothing broke: something was simply never installed. But a missing mail server is not the only issue: many cloud providers block outbound port 25 on new accounts. Hetzner's cloud documentation says ports 25 and 465 are blocked by default on all cloud servers and can be unblocked once the account is a month old and its first invoice has been paid; port 587 stays open in the meantime:
timeout 5 bash -c '</dev/tcp/gmail-smtp-in.l.google.com/25' && echo open || echo blocked
blocked
The low-hassle approach: leave the MX records alone, and for the site's outgoing email (password resets, invoices, notifications) use an authenticated sending service on port 587. That way the reputation of a brand-new IP never becomes an issue, and the domain's SPF and DKIM do not get scrambled.
What breaks after a migration, and how to fix it
The list of things that break after a migration is short and predictable; with one review before the DNS switch, most of them never happen at all:
| Symptom | Likely cause | What to do next |
|---|---|---|
| 500 error on every page | A PHP extension is not installed | Compare php -m with the old host's list |
| Home page opens, everything else is 404 | The .htaccess rewrite was not translated for Nginx | Add the try_files block |
| Images and uploads return 403 | File ownership changed after tar | chown -R www-data:www-data on the site folder |
| Database connection error | The values in wp-config.php are the old ones | Update the host, user and password |
| Email to the domain no longer arrives | The zone was rebuilt and MX was not carried over | Recreate the previous MX and TXT records exactly |
| Scheduled tasks do not run | The old cron jobs were not moved | Define them with crontab -e under the right user |
One WordPress-specific note: if you bring the site up on a temporary domain for practice, do not change the URLs inside the database by hand, because some of them sit inside serialized PHP data. The official wp search-replace tool understands this case, and with --dry-run it reports what it would change without saving anything:
wp search-replace 'https://example.com' 'https://staging.example.com' --all-tables --dry-run
Success: 412 replacements to be made.
With hourly billing, rehearse the migration first
The biggest fear about migrating is paying for a month of server time and finding out halfway through that you are not ready. The hourly model removes that fear: you create the server, rehearse the whole checklist above on a copy of the site, and if it does not work out, you delete it. Because the billing unit is the hour, a rehearsal of a few hours costs a small fraction of that server's monthly price. The details of this model are in what is an hourly cloud server.
Two points turn the rehearsal into real work. First, rehearse on the same size of server you plan to buy; what you are measuring is how fast the site runs under load, not whether it starts at all. Second, powering the server off does not stop the billing; deleting it does, and deletion takes the disk and all its data with it. So before you delete the rehearsal server, copy out the configuration you arrived at (the server block, the PHP-FPM settings, the cron jobs).
Shared hosting or VPS at Mehran Host?
On the shared hosting side, plans run on cPanel or DirectAdmin and are offered in both locations, Iran and abroad. Day-to-day work is not done inside the cPanel or DirectAdmin interface itself: in Mehran Host's own panel, each hosting service has separate tabs for Files, Database, FTP, Domains, DNS, Cron jobs, Backups, SSL certificate (for requesting a free certificate) and Email. The Email tab does what its name says and nothing more: creating and deleting mailboxes, changing passwords, quotas and forwarding. Creating SPF, DKIM and DMARC is not this tab's job; you enter those in the DNS tab as TXT records. The plans are listed on our Iran web hosting page.
On the VPS side, machines inside Iran are built on KVM with NVMe disks and are charged by the hour from your wallet; servers abroad are in Hetzner datacenters and are paid from the same wallet in Iranian currency; being Hetzner servers, they are subject to the port 25 restriction described above. What comes to your rescue on a bad day is the VNC console inside the panel: even if you have cut off the server's network with a wrong firewall rule, you can see the server's screen and fix it. Alongside it you get an OS reinstall, a root password change, adding IP addresses, usage graphs and the automatic backup add-on. One limitation, stated plainly: resources can only be increased; reducing them, especially the disk, is not possible.
Two services make the migration simpler whichever way you choose. DNS management covers A, AAAA, CNAME, MX, TXT, NS, SRV and CAA records, and the minimum TTL is 60 seconds, so lowering the TTL before the switch works without restrictions. And next to every A record there is a CDN switch: turning it on hides the origin IP and changes the public answer to the nearest edge node, including nodes inside Iran, with 1 TB of free traffic on a domain whose DNS is managed here. Measure where your site stands today with the free website speed test; just bear in mind that it runs from the service's own server, not from inside Iran. The bottom line: shared hosting is great for getting started, but once 503 errors and the wall of quotas appear, a VPS is the natural next step. Create an account and rehearse the migration without risk.
Frequently asked questions
Should I choose shared hosting or a VPS?
If you have a new site and do not want to spend time managing a server, shared hosting is the right choice, because the provider maintains and secures the operating system. A VPS becomes worthwhile when you have hit the resource ceiling, need software outside the host's list, or want a dedicated IP address; the price of that freedom is taking responsibility for updates, the firewall and backups.
What are the signs that it is time to move off shared hosting?
The most important sign is repeated 508 or 503 errors at peak hours, which means you are hitting the account's ceiling on concurrent requests or memory. Next come intermittent slowdowns with no change to the site, requests to install software such as Redis being refused, and the shared IP getting blacklisted. Before deciding, open the resource usage report in your control panel and see which quota has recorded faults.
Does moving a site from shared hosting to a VPS cause downtime?
It does not have to. In the right order, downtime is effectively zero: first bring the site fully up on the new server, then test it for yourself only, by editing your computer's hosts file or with curl's resolve option, then lower the TTL of the A record, and finally change the IP address. Keep the old hosting for a few days so that going back is just one DNS change.
How much RAM does a WordPress VPS need?
There is no fixed number, but you can calculate it: the number of concurrent PHP requests times the average RAM per process, plus 1 to 1.5 GB for the operating system, the database and the web server. Each WordPress process takes about 50 to 80 MB, so a site with ten to fifteen concurrent requests is comfortable on 2 to 4 GB of RAM, and with page caching it can need even less.
What happens to my domain's email after moving to a VPS?
Nothing, as long as you leave the MX records alone: a VPS has no mail server by default, so if you change only the A record, your domain's email keeps working where it always has. Problems start when you rebuild the DNS zone from scratch and do not carry over MX and TXT. For the site's outgoing email, use an authenticated sending service on port 587 instead of installing a mail server, because port 25 is blocked on many cloud servers.