keepcalculator

06 Numbers and Powers

VPS Staging & Production Setup Guide

Card No. 06·67938 · Updated · Standard formula, shown below

This guide walks you through setting up separate staging and production environments on a single VPS, so you can test changes safely before they go live. You’ll end up with two isolated web roots, two databases, and a simple way to promote code from staging to production.

What You’ll Need

  • A VPS running Ubuntu 22.04 or 24.04 (other distros work but commands may differ)
  • Root or sudo access to the server
  • A domain name (or subdomains) pointing to your VPS IP for each environment, e.g., staging.example.com and example.com
  • SSH client (Terminal on macOS/Linux, PuTTY on Windows)
  • Basic familiarity with the command line

Step-by-Step

  1. Connect to your VPS – SSH into your server as a user with sudo privileges. Update the package list and upgrade existing packages: sudo apt update && sudo apt upgrade -y
  2. Install a web server – We’ll use Nginx for its performance and ease of configuration. Install it with sudo apt install nginx -y. Start and enable it: sudo systemctl start nginx and sudo systemctl enable nginx.
  3. Install PHP (if needed) – For dynamic sites, install PHP-FPM: sudo apt install php-fpm php-mysql -y. The exact version may vary; check with php -v.
  4. Install MySQL/MariaDB – Install MariaDB (a MySQL drop-in): sudo apt install mariadb-server -y. Secure it with sudo mysql_secure_installation and follow the prompts.
  5. Create directory structure – Create separate roots for staging and production: sudo mkdir -p /var/www/staging and sudo mkdir -p /var/www/production. Set ownership to your user: sudo chown -R $USER:$USER /var/www.
  6. Set up databases – Log into MariaDB as root: sudo mysql. Create two databases and a user for each. For example:
    CREATE DATABASE staging_db;
    CREATE DATABASE production_db;
    CREATE USER 'staging_user'@'localhost' IDENTIFIED BY 'strongpassword1';
    CREATE USER 'prod_user'@'localhost' IDENTIFIED BY 'strongpassword2';
    GRANT ALL PRIVILEGES ON staging_db.* TO 'staging_user'@'localhost';
    GRANT ALL PRIVILEGES ON production_db.* TO 'prod_user'@'localhost';
    FLUSH PRIVILEGES;
    EXIT;
  7. Create Nginx server blocks – For each environment, create a config file in /etc/nginx/sites-available/. For staging, create staging with content like:
    server {
        listen 80;
        server_name staging.example.com;
        root /var/www/staging;
        index index.php index.html;
        location / {
            try_files $uri $uri/ =404;
        }
        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        }
    }

    Adjust the PHP socket path to match your installed version. Create a similar file for production, replacing the server_name and root.

  8. Enable the sites – Symlink the configs to sites-enabled: sudo ln -s /etc/nginx/sites-available/staging /etc/nginx/sites-enabled/ and same for production. Remove the default site: sudo rm /etc/nginx/sites-enabled/default.
  9. Test and reload Nginx – Check config syntax with sudo nginx -t. If OK, reload: sudo systemctl reload nginx.
  10. Deploy code – Upload your application code to /var/www/staging and /var/www/production. For a simple PHP app, copy files with scp or rsync. For a Git-based workflow, clone your repository into each directory.
  11. Configure environment-specific settings – In your app, use different configuration files for each environment (e.g., .env for staging and .env.production). Point the database host, name, user, and password to the correct ones.
  12. Set up SSL (optional but recommended) – Install Certbot: sudo apt install certbot python3-certbot-nginx -y. Then run sudo certbot --nginx -d staging.example.com -d example.com to obtain and install free SSL certificates.
  13. Test both environments – Visit http://staging.example.com and http://example.com in your browser. You should see your site. Check that the database connections work by viewing a page that queries the DB.

Common Mistakes

  • Using the same database for both – This can cause data corruption or unexpected behavior when staging writes test data to production.
  • Forgetting to update file permissions – If Nginx can’t read your files, you’ll get a 403 error. Ensure the web server user (www-data) has read access to the web roots.
  • Hardcoding database credentials – This makes it easy to accidentally connect to the wrong DB. Use environment variables or separate config files.
  • Not testing the Nginx config before reloading – A syntax error can take down your site. Always run nginx -t first.
  • Ignoring firewall rules – If you have a firewall (like UFW), make sure ports 80 and 443 are open: sudo ufw allow 'Nginx Full'.

Tips

  • Use Git branches – Keep a main branch for production and a develop branch for staging. Deploy by pulling the appropriate branch.
  • Automate deployments – Write a simple script that pulls latest code and runs migrations. This reduces human error.
  • Back up regularly – Schedule cron jobs to back up both databases and web roots. Test restores periodically.
  • Keep staging as close to production as possible – Use the same PHP version, extensions, and server config to catch issues early.
  • Use separate subdomains – This makes it clear which environment you’re on and avoids cookie conflicts.

Troubleshooting

  • 403 Forbidden – Check file permissions: sudo chmod -R 755 /var/www. Also verify the root path in the Nginx config is correct.
  • 404 Not Found – If your app uses routing (like Laravel), you need to configure Nginx to pass all requests to index.php. Add a try_files directive: try_files $uri $uri/ /index.php?$query_string;
  • 502 Bad Gateway – PHP-FPM is not running or the socket path is wrong. Check with sudo systemctl status php-fpm and verify the socket in your Nginx config.
  • Database connection refused – Ensure MariaDB is running and the user/password are correct. Also check that the database name matches.
  • Site not loading after SSL – Certbot usually updates the Nginx config automatically. If not, run sudo certbot --nginx again.
  • Changes not showing on staging – Clear your browser cache or use incognito mode. Also check that you’re editing the correct web root.

Frequently Asked Questions

Can I use the same domain for both staging and production? No, you need distinct subdomains or domains. Using the same domain would cause conflicts and confusion.

Should I use Docker instead? Docker can simplify dependency management, but it adds complexity. For a simple setup, separate directories work fine. If you expect many services, Docker may be worth it.

How do I promote code from staging to production? Typically, you push changes to your Git repository, then on the server pull those changes into the production directory. You can also use rsync to copy files directly.

Do I need a separate VPS for production? No, a single VPS can host both. However, for high-traffic sites, you may want to separate them for performance and security.

What if my app uses Node.js or Python? The same principle applies: create separate directories and configure Nginx as a reverse proxy to the appropriate application server (e.g., PM2 for Node, Gunicorn for Python).

How do I keep the databases in sync? You don’t want to sync automatically. Staging should have test data. If you need to copy production data to staging, use mysqldump to export and import.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →