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.comin/var/www/example.com, databasewp_live. - Staging:
https://staging.example.comin/var/www/staging.example.com, databasewp_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.