Back to tutorials
Tutorial

Hosting staging environment tutorial (2026): Clone your live WordPress site to a VPS with safe URLs, SSL, and push-to-live

Hosting staging environment tutorial for WordPress: clone to a VPS, fix URLs, lock access, add SSL, and push changes safely.

By Anurag Singh
Updated on Sep 19, 2026
Category: Tutorial
Share article
Hosting staging environment tutorial (2026): Clone your live WordPress site to a VPS with safe URLs, SSL, and push-to-live

Most WordPress outages don’t come from a “bad server.” They happen when changes go straight to production: a plugin update, a theme edit, or a PHP version bump. This hosting staging environment tutorial shows a repeatable way to clone your live WordPress site onto a VPS.

You’ll keep staging private, fix URLs the right way, add HTTPS, and push changes back to live. You’ll also keep a clear rollback path.

The workflow is intentionally boring. That’s the point.

You’ll get a staging URL you can test safely, a pre-release checklist you can reuse, and a cutover method that doesn’t gamble with revenue.

What you’ll build (and what you’ll avoid)

By the end, you’ll have:

  • A staging copy of your WordPress site running on an Ubuntu VPS
  • Staging access restricted (no indexing, no random visitors, no public logins)
  • Correct staging URLs (no mixed content, no accidental calls to production)
  • HTTPS on staging with Let’s Encrypt
  • A “push-to-live” process that minimizes downtime and reduces data loss risk

You’ll also avoid two staging mistakes that cause real-world damage:

  • Letting staging email real customers
  • Testing through cached assets that hide problems until launch day

Prerequisites

  • A live WordPress site (on shared hosting, a VPS, or cPanel) that you can access via SFTP/SSH and database credentials
  • A domain or subdomain for staging (recommended: staging.yourdomain.com)
  • A VPS with Ubuntu 24.04 LTS or Ubuntu 26.04 LTS

If you don’t have a VPS yet, start small. Scale up once the process works.

A HostMyCode VPS fits staging well because you control PHP versions, Nginx/Apache, and firewall rules without touching production.

Step 1 — Provision the staging VPS and lock down SSH

Log in as root (or your cloud user) and do the basics first. This is the difference between “temporary staging” and a “public incident.”

  1. Update packages:

    apt update && apt -y upgrade
  2. Create an admin user and add SSH keys:

    adduser deploy
    usermod -aG sudo deploy
    mkdir -p /home/deploy/.ssh
    nano /home/deploy/.ssh/authorized_keys
    chown -R deploy:deploy /home/deploy/.ssh
    chmod 700 /home/deploy/.ssh
    chmod 600 /home/deploy/.ssh/authorized_keys
  3. Harden SSH to disable password login. Do this only after verifying key access:

    sudo nano /etc/ssh/sshd_config

    Set:

    PasswordAuthentication no
    PermitRootLogin no
    sudo systemctl reload ssh

If you want a safer, rollback-friendly approach, use this guide. It helps you avoid lockouts while hardening SSH: SSH lockdown tutorial for a VPS.

Step 2 — Install Nginx, PHP-FPM, and MariaDB (staging stack)

Make staging match production as closely as possible. If live runs Nginx + PHP-FPM, use the same stack here.

If production is Apache/cPanel, Nginx still works for staging. Expect small differences in rewrites and headers.

sudo apt -y install nginx mariadb-server
sudo apt -y install php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-bcmath

Check versions:

nginx -v
php -v
mariadb --version

Enable services:

sudo systemctl enable --now nginx mariadb

Step 3 — Create a staging database and user

Don’t reuse production database credentials on staging. If staging leaks, production should not leak with it.

sudo mariadb
CREATE DATABASE wp_stage DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_stage_user'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_LONG_RANDOM_PASSWORD';
GRANT ALL PRIVILEGES ON wp_stage.* TO 'wp_stage_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Step 4 — Create the staging web root and Nginx server block

Pick a clean, predictable path. For single-site WordPress, this layout stays out of your way:

sudo mkdir -p /var/www/staging.yourdomain.com/public
sudo chown -R www-data:www-data /var/www/staging.yourdomain.com

Create an Nginx site file:

sudo nano /etc/nginx/sites-available/staging.yourdomain.com
server {
  listen 80;
  server_name staging.yourdomain.com;

  root /var/www/staging.yourdomain.com/public;
  index index.php index.html;

  access_log /var/log/nginx/staging.access.log;
  error_log  /var/log/nginx/staging.error.log;

  client_max_body_size 64m;

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

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php-fpm.sock;
  }

  location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|webp)$ {
    expires 7d;
    add_header Cache-Control "public, max-age=604800";
  }
}

Note: On Ubuntu, the PHP-FPM socket is often versioned (for example /run/php/php8.3-fpm.sock).

Verify what you have:

ls -1 /run/php/

Enable the site and test Nginx:

sudo ln -s /etc/nginx/sites-available/staging.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

If you need a refresher on multi-site layout and safe reload patterns, this is solid: Nginx server blocks tutorial.

Step 5 — Point DNS to the staging VPS (without risking production)

Create an A record for staging.yourdomain.com pointing to the staging VPS IP. Leave your production records alone.

Practical tip: set a low TTL (like 300 seconds) for the staging subdomain. It makes future IP changes much less painful.

If DNS behaves strangely—wrong IP, old values, or NXDOMAIN—use this diagnostic flow: DNS propagation troubleshooting tutorial.

Step 6 — Copy WordPress files to staging (rsync method)

You’ll usually be pulling from one of these environments:

  • From a VPS/dedicated server: rsync over SSH is fastest.
  • From shared hosting/cPanel: use SFTP, or generate a full backup and extract.

On the staging VPS, create a temp directory for imports:

sudo mkdir -p /root/import
sudo chmod 700 /root/import

Option A: rsync from a Linux server (recommended)

sudo rsync -aHAX --numeric-ids --delete \
  -e "ssh -p 22" \
  user@PRODUCTION_IP:/var/www/yourdomain.com/public/ \
  /var/www/staging.yourdomain.com/public/

Option B: upload a zip/tar from shared hosting

Upload your archive to /root/import, then extract:

sudo tar -xzf /root/import/public.tar.gz -C /var/www/staging.yourdomain.com/public

Fix ownership:

sudo chown -R www-data:www-data /var/www/staging.yourdomain.com/public

Step 7 — Export production database and import into staging

If you can run WP-CLI on production, exporting is simple:

wp db export /tmp/prod.sql --path=/path/to/wordpress

If you can’t, use mysqldump on production:

mysqldump --single-transaction --quick --routines --triggers \
  -u DBUSER -p DBNAME > /tmp/prod.sql

Transfer prod.sql to the staging VPS (SCP/SFTP), then import:

mysql -u wp_stage_user -p wp_stage < /root/import/prod.sql

Update wp-config.php on staging with the new DB name/user/password:

sudo nano /var/www/staging.yourdomain.com/public/wp-config.php

Step 8 — Fix site URL correctly (and stop mixed content)

In WordPress, the URL isn’t just two settings. URLs also land in post content, serialized options, widget configs, and some plugin tables.

You need a search/replace that understands serialization.

Install WP-CLI on staging:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

Run search-replace from the staging WordPress directory:

cd /var/www/staging.yourdomain.com/public
sudo -u www-data wp search-replace 'https://www.yourdomain.com' 'http://staging.yourdomain.com' --all-tables --precise

If production is http (rare in 2026), match the exact scheme used on production. Switch staging to HTTPS after you install the certificate.

Also set the WP home/siteurl explicitly. This prevents the classic “why am I being redirected to production?” loop:

sudo -u www-data wp option update home 'http://staging.yourdomain.com'
sudo -u www-data wp option update siteurl 'http://staging.yourdomain.com'

If WP-CLI fails with permissions or stuck maintenance flags, keep this nearby: WP-CLI troubleshooting tutorial.

Step 9 — Make staging private (basic auth + robots + WordPress hard stops)

Staging should not behave like a second public site. Give access to you, your team, and maybe a client.

That’s it.

9.1 Add HTTP Basic Auth at Nginx

sudo apt -y install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd-stage youruser

Edit your Nginx server block:

sudo nano /etc/nginx/sites-available/staging.yourdomain.com

Inside server { } add:

auth_basic "Staging";
auth_basic_user_file /etc/nginx/.htpasswd-stage;
sudo nginx -t && sudo systemctl reload nginx

9.2 Block indexing

Create robots.txt in staging root:

sudo -u www-data tee /var/www/staging.yourdomain.com/public/robots.txt >/dev/null <<'EOF'
User-agent: *
Disallow: /
EOF

In WordPress, also set “Discourage search engines from indexing this site.” This is not security. It only reduces accidental exposure.

9.3 Stop staging from sending real emails

This is the failure mode that burns teams. Block outbound mail at the server level by denying TCP 25 (or route mail to a sink).

On Ubuntu with UFW:

sudo apt -y install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny out 25/tcp
sudo ufw enable

If you need to test email formatting, route mail to a test inbox or an SMTP sandbox plugin.

The rule is simple: never send from staging to real customers.

Step 10 — Add HTTPS on staging (Let’s Encrypt) and switch URLs

Once DNS resolves to the staging VPS, issue a certificate. With Nginx, Certbot is the cleanest route:

sudo apt -y install certbot python3-certbot-nginx
sudo certbot --nginx -d staging.yourdomain.com

After Certbot updates Nginx, switch WordPress URLs to HTTPS. Then rewrite content URLs:

cd /var/www/staging.yourdomain.com/public
sudo -u www-data wp option update home 'https://staging.yourdomain.com'
sudo -u www-data wp option update siteurl 'https://staging.yourdomain.com'
sudo -u www-data wp search-replace 'http://staging.yourdomain.com' 'https://staging.yourdomain.com' --all-tables --precise

If you want a deeper hardening baseline for HTTPS ciphers, HSTS, and safe headers, pair this with: TLS hardening for Nginx/Apache and security headers setup guide.

Step 11 — Fix caching and external integrations (the staging sanity pass)

Before you trust your staging tests, remove the things that make staging lie:

  • Disable page cache plugins (or purge everything) so you aren’t looking at old HTML.
  • Disable CDN integration (Cloudflare APO, Bunny, etc.) on staging.
  • Swap payment gateways to sandbox mode and stop webhook callbacks to production.
  • Check hardcoded API keys in wp-config.php and plugin settings.

Quick diagnostics that catch common leaks:

# Look for production domain references in files (themes/plugins)
cd /var/www/staging.yourdomain.com/public
grep -R "yourdomain.com" -n wp-content/themes wp-content/plugins | head

If you want to catch 500 errors and slow endpoints while you test, go to the logs instead of guessing: VPS log analysis tutorial.

Step 12 — Create a staging snapshot before risky work (your cheap rollback)

Staging is where you take risks. Snapshot it before big changes.

If your VPS uses LVM or Btrfs, local snapshots are fast. Provider snapshots work fine too.

For a hands-on snapshot + offsite sync flow (useful on VPS and dedicated servers), follow: snapshot backup tutorial.

Step 13 — Push-to-live options (choose your risk profile)

“Push to live” isn’t a single button unless you’re on a managed platform. On typical hosting, you choose the push method based on what changed.

Think in three buckets: code only, database settings, or an entire site move. Here are three practical options.

Option A (lowest risk): push only code changes

Use this for theme tweaks, custom plugin changes, or new plugin versions. It works best when you don’t need staging content.

  • Deploy updated theme/plugin files from staging to production using git, SFTP, or rsync.
  • Do not overwrite wp-content/uploads.
  • Do not replace the production database.

Example rsync (run from staging to production):

rsync -avz --delete \
  --exclude 'uploads/' \
  --exclude 'wp-config.php' \
  -e ssh \
  /var/www/staging.yourdomain.com/public/wp-content/ \
  user@PROD_IP:/var/www/yourdomain.com/public/wp-content/

Option B (balanced): push database changes, but freeze production briefly

Use this when your staging work changed options, menus, widgets, or plugin settings stored in the database.

  1. Put production into maintenance mode (short window).
  2. Export production DB as a safety backup.
  3. Import staging DB into production.
  4. Immediately run URL search-replace back to production domain.
  5. Exit maintenance mode.

Maintenance mode can be as simple as enabling a plugin. WP-CLI gives you a predictable on/off switch:

# Production server
cd /var/www/yourdomain.com/public
sudo -u www-data wp maintenance-mode activate

Export production DB (safety net):

sudo -u www-data wp db export /root/prod-before-push.sql

Import staging DB file into production DB, then fix URLs:

sudo -u www-data wp db import /root/stage-to-prod.sql
sudo -u www-data wp search-replace 'https://staging.yourdomain.com' 'https://www.yourdomain.com' --all-tables --precise

Turn off maintenance mode:

sudo -u www-data wp maintenance-mode deactivate

For a more controlled “change window + rollback” pattern, pair this with a restore rehearsal: VPS restore drill tutorial.

Option C (highest risk, most complete): full site swap with DNS cutover

Use this only when you’re migrating hosting or changing infrastructure in a big way. It’s not a weekly workflow.

For that scenario, follow a dedicated migration runbook instead: server migration tutorial from shared hosting to a VPS.

Release checklist (print this)

  • Staging matches production versions: PHP, major plugin stack, and caching layer.
  • Backups exist: you can restore production and staging.
  • No real emails from staging: outbound SMTP blocked or redirected.
  • Critical flows tested: checkout, contact forms, password reset, search, and admin login.
  • Error logs checked: staging Nginx/PHP logs are clean after testing.
  • Push plan chosen: code-only vs DB push, with a time window.
  • Rollback plan written: “restore DB backup” and “revert files” steps are ready.

Troubleshooting: common staging failures and quick fixes

Redirects keep sending you to production

  • Check home and siteurl options with WP-CLI.
  • Check for a hardcoded domain in wp-config.php (look for WP_HOME and WP_SITEURL).
  • Disable caching plugins and purge server cache.
sudo -u www-data wp option get home
sudo -u www-data wp option get siteurl

Mixed content after enabling HTTPS

  • Run a DB search-replace from http to https for staging.
  • Make sure your Nginx config sets the correct scheme after SSL (Certbot usually handles this).

Uploads missing or broken images

  • Verify wp-content/uploads copied correctly.
  • Check file permissions. Directories should be 755, files 644 in most setups.
sudo find /var/www/staging.yourdomain.com/public/wp-content/uploads -type d -exec chmod 755 {} \;
sudo find /var/www/staging.yourdomain.com/public/wp-content/uploads -type f -exec chmod 644 {} \;
sudo chown -R www-data:www-data /var/www/staging.yourdomain.com/public/wp-content/uploads

Staging is slow even though production is fast

  • Disable debug plugins, query monitors, and heavy logging.
  • Confirm OPcache is enabled and PHP-FPM is running.
  • Check CPU/RAM on the VPS; staging often runs on tiny instances.

For real visibility (CPU, RAM, disk IO, and uptime), set up lightweight monitoring: VPS monitoring setup tutorial.

Summary: a staging environment that won’t surprise you

A staging copy only helps if it behaves predictably. Clone files and the database, fix URLs with serialization-safe tools, lock the site down, and block outbound email.

Then choose a push-to-live method that matches what you changed.

If you’re making staging a permanent part of your workflow, run it on infrastructure you control. A managed VPS hosting plan can handle patching and baseline hardening, while you keep the access you need for clean releases.

If you want staging to behave like production, put it on a VPS with predictable resources and full SSH access. Start with a HostMyCode VPS for staging, then move critical sites to dedicated servers as traffic and complexity grow.

FAQ

Should staging be on the same server as production?

Usually no. If staging spikes CPU or fills disk, production takes the hit. A small separate VPS is cheap insurance and keeps your tests honest.

Do I need SSL on staging?

Yes. Many plugins and payment flows behave differently without HTTPS. Testing cookies and redirects under HTTPS also catches problems before release.

How do I prevent staging from being indexed by Google?

Use HTTP Basic Auth first. Then add robots.txt with Disallow: /. Basic Auth is the real protection.

Can I push staging changes to live without downtime?

Code-only pushes can be close to zero downtime. Database pushes usually require a short maintenance window unless you build a more complex content sync approach.

What’s the simplest rollback if a push goes wrong?

Restore the pre-push production database dump and revert the changed files. If you snapshot before pushing, rollback is faster and far less stressful.

Hosting staging environment tutorial (2026): Clone your live WordPress site to a VPS with safe URLs, SSL, and push-to-live | HostMyCode