IPv4 & IPv6 Leasing - Any RIR, Any LocationOrder Now
Hostperl

WordPress Staging on Ubuntu 24.04 with Safe Rollback

By Raman Kumar

Share:

Updated on Nov 13, 2026

WordPress Staging on Ubuntu 24.04 with Safe Rollback

Why this staging setup matters

WordPress staging on Ubuntu 24.04 gives you a separate copy of a live site, so you can test plugin updates, theme changes, and checkout flows before customers ever see them. This tutorial uses a fresh Ubuntu Server VPS, Nginx, PHP-FPM, and MariaDB, then adds a sync-and-rollback workflow you can rely on during a launch window or busy sales period.

If you manage WordPress sites for clients, this kind of change control helps you avoid preventable incidents. It also fits well with Hostperl VPS hosting for staging and launch work, especially when you need a dependable test environment before pushing changes to production: Hostperl VPS.

What you will build

You will create:

  • a non-root admin user with sudo access
  • an isolated staging domain on Ubuntu 24.04
  • Nginx, PHP-FPM, and MariaDB for WordPress
  • a staging database and file copy from production
  • basic HTTP authentication so search engines and customers do not find the staging site
  • a rollback plan that restores the previous staging snapshot if a test breaks

This is not a one-command install. The goal is a staging site that behaves like production and still gives you room to test safely. If you want a broader release workflow, pair this with Hostperl’s WordPress staging guidance and cutover planning, including WordPress staging and rollback for safer releases and WordPress staging and migration planning for safer releases.

Before you start

Run everything on a fresh Ubuntu Server 24.04 VPS. If you are using a different Ubuntu release, adjust package names only if your repository sources require it. The commands below assume the server IP example 203.0.113.10. Replace it with the real public IP assigned to your server.

On your local computer

ssh root@203.0.113.10

203.0.113.10 is a documentation address. Replace it with your server’s real IP before connecting.

On the VPS as root

cat /etc/os-release

You should see Ubuntu 24.04 details in the output. If you do not, stop and confirm the server image first.

Create the non-root admin user

Keep your root SSH session open until the new login works. That avoids lockout if you mistype a permission or firewall rule.

On the VPS as root

adduser deploy

Set a strong password when prompted. The new account will be your everyday admin user.

usermod -aG sudo deploy

This grants sudo access through Ubuntu’s sudo group.

mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

This copies your existing SSH key access to the new user. If you do not use key-based SSH yet, add your public key before moving on.

On your local computer

ssh deploy@203.0.113.10

Open a second terminal and log in as deploy. Replace the example IP with your real one. Keep the root session open until this works.

On the VPS as the non-root sudo user

sudo -l
pwd

You should see sudo rights and a normal home directory path. After this succeeds, you can stop using root for routine work.

Update the server and install WordPress packages

Start with a clean package state so you do not debug old repository metadata during a release window.

On the VPS as the non-root sudo user

sudo apt update
sudo apt -y upgrade
sudo apt -y install nginx mariadb-server php-fpm php-cli php-mysql php-curl php-gd php-xml php-mbstring php-zip php-imagick unzip wget rsync ufw

This installs the web stack, MariaDB, transfer tools, and the firewall. On Ubuntu 24.04, PHP-FPM usually ships as PHP 8.3.

php -v
nginx -v
mariadb --version

Confirm the packages installed correctly before you write config files.

Prepare the database for staging

Use a separate database and user for staging. Never point staging at the production database unless you are doing a very controlled read-only test.

On the VPS as the non-root sudo user

sudo mysql

You will enter the MariaDB shell.

CREATE DATABASE wp_staging CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wpstage'@'localhost' IDENTIFIED BY 'Use-A-Long-Random-Password-Here';
GRANT ALL PRIVILEGES ON wp_staging.* TO 'wpstage'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Replace the password with a unique secret. Do not reuse the production database password.

On the VPS as the non-root sudo user

sudo mysql -e "SHOW DATABASES LIKE 'wp_staging';"

You should see the new database listed.

Create the staging site directory and download WordPress

Use a dedicated document root so the staging site stays separate from production files.

On the VPS as the non-root sudo user

sudo mkdir -p /var/www/staging.example.com
sudo chown -R www-data:www-data /var/www/staging.example.com
cd /var/www/staging.example.com
wget https://wordpress.org/latest.tar.gz
sudo tar -xzf latest.tar.gz --strip-components=1
rm latest.tar.gz

This creates the staging tree under /var/www/staging.example.com. Replace the hostname in the directory name with your own staging subdomain if needed.

Restore production content into staging safely

Use the production database dump and file copy only after you have a maintenance window or a recent backup. A staging clone is useful only if it mirrors real content.

On the production server or backup source

mysqldump --single-transaction --quick --routines --triggers wordpress_prod > wordpress_prod.sql
rsync -a --delete /var/www/example.com/wp-content/ /backup/wp-content-staging/

Adjust the paths and database name to match your live site. Store the dump and files securely until transfer.

On the VPS as the non-root sudo user

sudo cp /path/to/wordpress_prod.sql /var/www/staging.example.com/
sudo rsync -a /path/to/wp-content-staging/ /var/www/staging.example.com/wp-content/

Replace the paths with the actual transfer location you used. Do not expose the dump over the web.

On the VPS as the non-root sudo user

cd /var/www/staging.example.com
sudo mysql wp_staging < wordpress_prod.sql

This imports the live content into staging. If the database is large, expect this to take a few minutes.

Configure WordPress for staging

Edit wp-config.php so staging uses its own database and does not send traffic to the live site by mistake.

On the VPS as the non-root sudo user

cd /var/www/staging.example.com
sudo cp wp-config-sample.php wp-config.php
sudo nano wp-config.php

Add or replace the database values with the following block:

define( 'DB_NAME', 'wp_staging' );
define( 'DB_USER', 'wpstage' );
define( 'DB_PASSWORD', 'Use-A-Long-Random-Password-Here' );
define( 'DB_HOST', 'localhost' );
define( 'WP_ENVIRONMENT_TYPE', 'staging' );
define( 'WP_HOME', 'https://staging.example.com' );
define( 'WP_SITEURL', 'https://staging.example.com' );
define( 'DISALLOW_FILE_EDIT', true );

Save and exit nano with Ctrl+O, Enter, then Ctrl+X.

On the VPS as the non-root sudo user

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

This keeps ownership consistent and avoids overly permissive file modes.

Set up Nginx and PHP-FPM

Ubuntu 24.04 uses PHP-FPM sockets rather than a remote PHP daemon. That keeps the web server and runtime local, which makes WordPress staging easier to debug.

On the VPS as the non-root sudo user

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

Paste this full server block and replace staging.example.com with your staging hostname if different:

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

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

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

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

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

    location ~ /(\.ht|\.git|wp-config\.php) {
        deny all;
    }
}

Save and exit, then enable the site:

sudo ln -s /etc/nginx/sites-available/staging.example.com /etc/nginx/sites-enabled/
sudo nginx -t

The syntax test must pass before you reload Nginx. If it fails, fix the referenced line first.

sudo systemctl reload nginx
sudo systemctl enable --now php8.3-fpm mariadb nginx

After this, Nginx should serve the staging host and PHP-FPM should start at boot.

Restrict access before going public

A staging site should not be indexed or casually browsed. Add firewall rules and basic HTTP authentication before you open the site externally.

On the VPS as the non-root sudo user

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable
sudo ufw status verbose

Ubuntu’s UFW should show SSH and web traffic allowed, with the firewall active.

Now add password protection:

sudo apt -y install apache2-utils
sudo htpasswd -c /etc/nginx/.staging-htpasswd deploy

Use a strong password when prompted. This file protects the staging site from casual access.

Edit the Nginx site again and add these lines inside the server block, near the top:

auth_basic "Staging Only";
auth_basic_user_file /etc/nginx/.staging-htpasswd;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;

Then run:

sudo nginx -t
sudo systemctl reload nginx

This sequence locks down the staging site before anyone can reach it without credentials.

Finish the WordPress install in the browser

Point your staging DNS record at the VPS, then open the staging hostname in a browser. You should see the WordPress setup screen after HTTP auth. Complete the site title, admin account, and password.

If DNS is not ready yet, you can test locally by adding a temporary hosts entry on your computer that maps staging.example.com to 203.0.113.10. Remove that entry after DNS propagates.

Confirm the site is healthy

Run both server-side and client-side checks. A staging deployment is only useful if you can prove the server responds correctly and WordPress can load pages.

On the VPS as the non-root sudo user

sudo systemctl status nginx --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo ss -tulpn | grep -E ':80|:443'

You should see active services and listeners on web ports. If Nginx is not listening, review the site file and the syntax test output.

On your local computer

curl -I http://staging.example.com
curl -I -u deploy https://staging.example.com

The first command should show a response, usually a redirect or auth challenge. The second should return headers from the protected staging site. Replace the domain with your real staging hostname.

On the VPS as the non-root sudo user

tail -n 50 /var/log/nginx/staging.example.com.error.log
journalctl -u php8.3-fpm -n 50 --no-pager

Use these logs if the page loads slowly, returns 502 errors, or fails to reach PHP-FPM.

Safe rollback when a test breaks

Staging should make recovery easier, not harder. Before risky plugin or theme tests, take a file and database snapshot.

On the VPS as the non-root sudo user

mkdir -p /home/deploy/staging-backups/$(date +%F)
mysqldump --single-transaction wp_staging > /home/deploy/staging-backups/$(date +%F)/wp_staging.sql
rsync -a /var/www/staging.example.com/ /home/deploy/staging-backups/$(date +%F)/files/

This creates a dated backup you can restore quickly if a plugin update breaks the site.

To roll back:

sudo rsync -a --delete /home/deploy/staging-backups/2026-01-01/files/ /var/www/staging.example.com/
sudo mysql wp_staging < /home/deploy/staging-backups/2026-01-01/wp_staging.sql
sudo systemctl reload nginx

Replace the dated directory with the snapshot you actually created. Restore the database first if the failure is data-related.

Troubleshooting the most likely failures

502 Bad Gateway from Nginx

Run:

sudo tail -n 50 /var/log/nginx/staging.example.com.error.log
sudo systemctl status php8.3-fpm --no-pager

If the log mentions connect() to unix:/run/php/php8.3-fpm.sock failed, PHP-FPM is down or the socket name is wrong. Fix the socket path in the Nginx config, then reload Nginx.

Permission denied on uploads

Run:

sudo find /var/www/staging.example.com/wp-content -maxdepth 2 -type d -ls
sudo chown -R www-data:www-data /var/www/staging.example.com/wp-content

WordPress needs write access to wp-content for uploads, cache files, and some plugin operations.

White screen after plugin activation

Run:

wp plugin deactivate --all --path=/var/www/staging.example.com

If you use WP-CLI, this quickly isolates the problem plugin. If WP-CLI is not installed, rename the plugin directory in wp-content/plugins and refresh the site.

Staging site is indexed

Run:

curl -I https://staging.example.com

You should see the X-Robots-Tag header. If it is missing, confirm the header line is inside the Nginx server block and reload Nginx after a syntax test.

Reboot and persistence check

One reboot test catches a lot of missed service issues. It is better to find them now than during a client review.

On the VPS as the non-root sudo user

sudo reboot

Reconnect after the server comes back.

On your local computer

ssh deploy@203.0.113.10

Then check service persistence again:

sudo systemctl status nginx --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo systemctl status mariadb --no-pager

All three services should start automatically after reboot.

If you are preparing a client launch, staging on a clean Ubuntu VPS gives you a safer rehearsal space for plugin updates, checkout tests, and rollback drills. Hostperl VPS hosting fits that workflow well, and you can pair it with managed VPS hosting when you want a support-backed environment for release windows.

If you need a wider cutover process for a team handoff, Hostperl’s managed hosting cutover checklist for agency clients is a useful next step.

FAQ

Should staging use the same plugins as production?

Usually yes, because you want to test the same stack. Remove anything that sends external email, payment webhooks, or live API calls unless you have safe test credentials.

Can I use staging for checkout tests?

Yes, but only with sandbox payment settings and test email delivery. Never let staging place live orders or trigger real invoices.

Do I need a separate domain for staging?

Yes. A separate subdomain like staging.example.com keeps DNS, SSL, and access control cleaner than a path-based setup.

What is the safest way to refresh staging from production?

Take a database dump and wp-content backup, restore them into the staging database and directory, then re-run the site URL checks before opening it to testers.

How do I stop search engines from indexing staging?

Use HTTP basic auth, add the X-Robots-Tag: noindex header, and keep the staging hostname out of public links and sitemaps.