LinuxAugust 29, 2026 12 views

Migrate a website from shared hosting to a VPS (2026)

Migrate a website from shared hosting to a VPS (2026)

Why migrate to a VPS?

Shared hosting is a good fit for low-traffic sites and projects that do not need control over the server environment. Some needs go beyond that scope: a specific PHP version that is not available, root access required for a system dependency, performance affected by neighbors on the shared node, or the need to host several projects with isolated configurations. Migrating to a VPS answers those specific cases, without making shared hosting unsuitable for the uses it covers.

Step 1: Inventory before migrating

Complete the full inventory before touching anything. A failed migration almost always comes from an item forgotten at this stage.

Files and directories

  • Main web directory (often public_html/, www/ or htdocs/)
  • Configuration files outside the web root (.env files, application configs)
  • Files uploaded by users (often in an uploads/ or storage/ subfolder)
  • Cron jobs configured on the shared hosting (list them via your current host's control panel)

Database

  • Database name, username, password (available in the panel or in the application's config file)
  • MySQL/MariaDB version used on the shared hosting (check compatibility with the target version)

Email

  • Mailboxes hosted with the shared hosting provider do not migrate automatically to the VPS
  • List the active email addresses and decide: migrate them to the VPS (Postfix/Dovecot), move them to a third-party service, or leave them with the current provider with the MX records unchanged

DNS and TTL

Before any migration, lower the TTL of the domain's A record to 300 seconds (5 minutes). This reduces the propagation delay during the final switch.

Locate the A record in the domain's DNS zone (usually in the registrar's or current host's panel) and change the TTL value:

; Before migration: usual TTL (often 3600 or 86400)
example.com.  3600  IN  A  1.2.3.4

; Change to 300 at least 24h before the switch
example.com.  300   IN  A  1.2.3.4

Wait at least the duration of the old TTL after the change before proceeding with the switch, so propagation is effective.

Step 2: Prepare the VPS

Install and secure the VPS before transferring any data. Do not migrate to an unconfigured server.

  • Basic hardening (SSH key, UFW, updates): VPS security checklist
  • SSH key access: SSH key guide
  • Web stack installation (Nginx + PHP-FPM + MariaDB): Nginx + PHP-FPM guide
  • Create the destination directory: sudo mkdir -p /var/www/example.com
  • Check that the PHP version installed on the VPS is compatible with the application

Step 3: Transfer the files

With rsync (SSH access available on the shared hosting)

rsync is the recommended method: it only transfers modified files, supports resuming after an interruption and preserves permissions. Source: man rsync

From the VPS (pulling the files from the shared hosting):

rsync -avz --progress \
  [email protected]:/home/user/public_html/ \
  /var/www/example.com/

Or from a local machine (if you have SSH access to both servers):

rsync -avz --progress \
  [email protected]:/home/user/public_html/ \
  [email protected]:/var/www/example.com/

Options used:

  • -a: archive mode (preserves permissions, timestamps, symbolic links)
  • -v: verbose (shows transferred files)
  • -z: compression during transfer
  • --progress: shows progress

Without SSH access on the shared hosting (SFTP or manual download)

If the shared hosting does not offer SSH access, use an SFTP client (FileZilla, Cyberduck) to download the files locally, then send them to the VPS:

# From the local machine to the VPS
rsync -avz --progress /local/path/public_html/ \
  user@vps-ip:/var/www/example.com/

Adjust permissions

sudo chown -R www-data:www-data /var/www/example.com
sudo find /var/www/example.com -type d -exec chmod 755 {} \;
sudo find /var/www/example.com -type f -exec chmod 644 {} \;

Step 4: Export and import the database

Export from the shared hosting

If the shared hosting offers SSH access:

mysqldump -u db_user -p db_name > export-$(date +%F).sql

Without SSH access, use phpMyAdmin (available on most shared hosting plans): Export > SQL format > Download.

Source: dev.mysql.com: mysqldump

Transfer the export file to the VPS

scp export-$(date +%F).sql user@vps-ip:/home/user/

Create the database on the VPS

sudo mariadb -u root
CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'db_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT ALL PRIVILEGES ON db_name.* TO 'db_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Import the data

mariadb -u db_user -p db_name < /home/user/export-$(date +%F).sql

Step 5: Reconfigure the application on the VPS

Application configuration file

Update the database connection settings in the application's configuration file (.env, wp-config.php, config.php depending on the framework):

  • Database host: localhost (the database is on the same server)
  • Database name, user and password: the ones created in the previous step

Absolute paths

Check and update all absolute paths hardcoded in the configuration. On shared hosting, paths often start with /home/user/public_html/; on the VPS, they start with /var/www/example.com/.

URLs stored in the database (if applicable)

Some applications (notably WordPress) store the site URL in the database. If the URL changes during the migration, update those values. For WordPress, use WP-CLI:

sudo -u www-data wp search-replace 'http://old-domain.com' 'https://example.com' \
  --path=/var/www/example.com

For other applications, check the relevant framework's documentation.

Nginx vhost

Create the Nginx vhost for the site. For a generic PHP site:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

For WordPress: WordPress on VPS guide (specific vhost with permalink handling).

Step 6: Test the site before the DNS switch

Never switch the DNS without having verified that the site works on the VPS. Edit the local hosts file to force resolution to the new IP, without touching the public DNS.

Edit the local hosts file

On Linux or macOS:

sudo nano /etc/hosts

On Windows: C:\Windows\System32\drivers\etc\hosts (open as administrator)

Add the line:

vps-ip  example.com www.example.com

The browser now resolves example.com to the VPS, without affecting other users.

Points to check

  • Home page and internal pages display correctly
  • Contact forms work (email sending)
  • Login to the admin area
  • Uploaded files are accessible (images, documents)
  • Redirects are active (301, 302)
  • No errors in the Nginx logs: sudo tail -f /var/log/nginx/error.log

Remove the hosts entry after testing

# Remove the line added to /etc/hosts
sudo nano /etc/hosts

Step 7: Issue the SSL certificate

Issuing the Let's Encrypt certificate before the DNS switch is not possible (Certbot must resolve the domain to the VPS). Issue the certificate right after the switch, while the low TTL is still active.

sudo certbot --nginx -d example.com -d www.example.com

Complete guide: OuiHeberg Certbot guide.

Step 8: Switch the DNS

Once the tests are validated, change the domain's A record to point to the VPS IP:

; Before
example.com.  300  IN  A  1.2.3.4   (old shared hosting IP)

; After
example.com.  300  IN  A  5.6.7.8   (new VPS IP)

With the TTL at 300 seconds, propagation is effective within 5 to 10 minutes for most DNS resolvers. Watch the Nginx logs during this period to confirm that traffic is reaching the VPS:

sudo tail -f /var/log/nginx/access.log

Once propagation is confirmed, raise the TTL back to a standard value (3600 or 86400):

example.com.  3600  IN  A  5.6.7.8

Step 9: Overlap period

Keep the shared hosting active for 1 to 2 weeks after the switch. This period allows you to:

  • Recover files forgotten during the initial inventory
  • Check that emails hosted on the shared hosting keep arriving (if the MX records have not changed)
  • Have a quick rollback point in case of a critical problem

Do not delete the data on the shared hosting before confirming that everything works correctly on the VPS.

Common pitfalls

Emails stay with the old provider

If the domain had mailboxes on the shared hosting, the MX records still point to the old provider after switching the A record. That is often intentional (keeping email with the current provider during the transition), but check that it is deliberate. Decide explicitly on the email strategy before the DNS switch.

Cron jobs no longer run

Scheduled tasks configured in the shared hosting panel do not migrate automatically. Recreate them in the VPS crontab:

crontab -e

List the old host's cron jobs from its control panel before the migration (inventory step).

The SSL certificate is not issued immediately

Certbot cannot issue a certificate for a domain that does not yet point to the VPS. Issue the certificate right after the DNS switch, while the low TTL is still active. Between the switch and the certificate issuance, the site is accessible over HTTP only.

Absolute paths stored in the database

Some applications store absolute paths in the database (path to uploaded files, image URLs). These paths must be updated after the migration. Use the application's search/replace feature or an SQL script:

UPDATE wp_options SET option_value = REPLACE(option_value,
  '/home/old/public_html',
  '/var/www/example.com')
WHERE option_value LIKE '%/home/old/public_html%';

Frequently asked questions

How long does a migration take?

For a standard site (a few hundred MB, a reasonably sized database), the technical migration takes 1 to 3 hours. The preparation (inventory, lowering the TTL, configuring the VPS) represents most of the time. The DNS switch itself takes less than 10 minutes with a TTL of 300 seconds.

Can you migrate without downtime?

Yes, if the procedure is followed in order: the site stays live on the shared hosting throughout the preparation and testing. The only potential interruption is the window between the DNS switch and the SSL certificate issuance (a few minutes). With a TTL of 300 seconds, that window is short.

Should you notify your visitors?

For a low-traffic site, no. For an e-commerce site or an application with logged-in users (active sessions), schedule the migration outside peak hours and display a maintenance page during the switch.

What if the site does not work after the switch?

Set the A record back to the old shared hosting IP (the 300-second TTL allows a quick rollback). Diagnose the problem on the VPS without pressure, then run the switch again once the problem is solved. This is exactly why the shared hosting must stay active during the overlap period.

Not decided yet? Our article on VPS versus shared hosting covers the cases where migrating is worth it.

Migrating several sites to the same VPS? Set up the directory layout and isolation up front: multi-site setup with Nginx.