A staging site is a private copy of your live site where you test plugin updates, theme changes and new features before they reach visitors. On a VPS you can create one in minutes with standard tools — no paid staging feature required.

Plan

  • Live: https://example.com in /var/www/example.com, database wp_live.
  • Staging: https://staging.example.com in /var/www/staging.example.com, database wp_staging.

1. DNS and web server

Add an A record for staging.example.com pointing to the server. Copy the live Nginx server block, change server_name and root, then enable it:

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/staging.example.com
sudo sed -i 's/server_name .*/server_name staging.example.com;/; s#root /var/www/example.com#root /var/www/staging.example.com#' /etc/nginx/sites-available/staging.example.com
sudo ln -s /etc/nginx/sites-available/staging.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d staging.example.com

(Remove Certbot-generated lines for the live domain from the copied file first, if present.)

2. Keep staging private

Add HTTP basic authentication to the staging server block — search engines and visitors can’t reach it, and you avoid duplicate-content issues:

sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd-staging alice
auth_basic "Staging";
auth_basic_user_file /etc/nginx/.htpasswd-staging;

3. Copy the files

sudo rsync -a --delete /var/www/example.com/ /var/www/staging.example.com/ \
  --exclude 'wp-content/cache/' --exclude 'wp-content/uploads/backups/'
sudo chown -R www-data:www-data /var/www/staging.example.com

4. Copy the database

sudo mysql -e "CREATE DATABASE wp_staging CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'wp_staging'@'localhost' IDENTIFIED BY 'another-password';
GRANT ALL ON wp_staging.* TO 'wp_staging'@'localhost';"
sudo mysqldump --single-transaction wp_live | sudo mysql wp_staging

Use a separate database user so staging can never touch the live database. Update /var/www/staging.example.com/wp-config.php with the staging credentials.

5. Point staging at its own URL

cd /var/www/staging.example.com
sudo -u www-data wp search-replace 'https://example.com' 'https://staging.example.com' --all-tables --precise
sudo -u www-data wp cache flush

6. Make staging safe

Add to staging’s wp-config.php:

define( 'WP_ENVIRONMENT_TYPE', 'staging' );
define( 'DISABLE_WP_CRON', true );       // no scheduled jobs (emails, syncs) from staging

And with WP-CLI:

sudo -u www-data wp option update blog_public 0     # discourage search engines

Stop staging from contacting customers or external services:

  • Deactivate or reconfigure email/SMTP plugins (or route mail to a catcher like Mailpit).
  • WooCommerce: put payment gateways in test mode and disable webhooks; never process real orders on staging.
  • Disable integrations that sync to CRMs, newsletters or inventory systems.
  • Disable backup plugins that upload to the same remote storage as live.

WP_ENVIRONMENT_TYPE lets well-behaved plugins detect staging automatically.

7. Test your changes

Update plugins, try the new theme, run your checklist (homepage, forms, checkout in test mode, logged-in views). Watch the PHP error log for warnings.

8. Push changes to live

What to move depends on what changed:

  • Plugin/theme updates — simply repeat the same updates on live, now that you know they work. This is the safest approach.
  • Code changes (custom theme or plugin) — deploy the files (ideally from Git — see WordPress Git deployment).
  • Content and settings — avoid overwriting the live database: live has new orders, comments and users since you copied it. Re-create the settings on live, or use a plugin that merges selected tables.

Overwriting the live database with staging’s is only safe for sites where nothing changes on live in the meantime (brochure sites with a content freeze).

9. Refresh staging

Repeat steps 3–5 whenever staging drifts too far from live. Script it:

#!/bin/bash
set -euo pipefail
rsync -a --delete --exclude 'wp-config.php' /var/www/example.com/ /var/www/staging.example.com/
mysqldump --single-transaction wp_live | mysql wp_staging
cd /var/www/staging.example.com
sudo -u www-data wp search-replace 'https://example.com' 'https://staging.example.com' --all-tables --precise --quiet
sudo -u www-data wp option update blog_public 0
sudo -u www-data wp cache flush

Excluding wp-config.php keeps staging’s own database credentials.

Resources

A staging copy doubles disk usage and adds a little load while you test. For larger sites, a separate small VPS for staging keeps tests from affecting live performance.

Related: WP-CLI guide, updating WordPress safely and the WordPress VPS page.

  • WordPress
  • Staging
  • Development