Complete documentation for the site01.com WordPress multi-environment infrastructure on Hostinger VPS.
- Project Overview
- Server Information
- Folder Structure
- Database Configuration
- Environment Details
- Accessing Sites
- WordPress Admin Access
- Development Workflow
- DNS Configuration
- SSL/HTTPS Setup
- Common Tasks
- Troubleshooting
- Backup & Recovery
- Security Notes
This project implements a professional multi-environment WordPress setup with three isolated environments:
- Live (Production): Public-facing website
- Stage (Staging): Pre-production testing and client preview
- Dev (Development): Active development and experimentation
Purpose: Enable proper version control, testing workflows, and safe deployment practices for WordPress development.
Tech Stack:
- WordPress (Latest)
- Apache2 Web Server
- MySQL/MariaDB Database
- Let's Encrypt SSL Certificates
- Debian-based Linux OS
- Provider: Hostinger VPS
- Server Name: srv1250135
- IP Address: 72.62.125.163
- Operating System: Linux (Debian-based)
- Web Server: Apache2
- Database: MySQL/MariaDB
- PHP Version: (Check with
php -von server)
ssh -i ~/.ssh/id_ed25519_gaia root@72.62.125.163SSH Key Location: ~/.ssh/id_ed25519_gaia
Important: Keep this private key secure and never commit it to version control.
/var/www/site01.com/
│
├── live/ # Production Environment
│ ├── wp-admin/ # WordPress admin files
│ ├── wp-content/ # Themes, plugins, uploads
│ │ ├── themes/
│ │ ├── plugins/
│ │ └── uploads/
│ ├── wp-includes/ # WordPress core files
│ ├── wp-config.php # WordPress config (DB: s01_live)
│ ├── index.php
│ └── ... # Other WordPress files
│
├── staging/ # Staging Environment
│ ├── wp-admin/
│ ├── wp-content/
│ │ ├── themes/
│ │ ├── plugins/
│ │ └── uploads/
│ ├── wp-includes/
│ ├── wp-config.php # WordPress config (DB: s01_stage)
│ └── ...
│
├── dev/ # Development Environment
│ ├── wp-admin/
│ ├── wp-content/
│ │ ├── themes/
│ │ ├── plugins/
│ │ └── uploads/
│ ├── wp-includes/
│ ├── wp-config.php # WordPress config (DB: s01_dev)
│ └── ...
│
└── logs/ # Apache Log Files
├── live_error.log
├── live_access.log
├── stage_error.log
├── stage_access.log
├── dev_error.log
└── dev_access.log
- Directories: 755 (rwxr-xr-x)
- Files: 644 (rw-r--r--)
- Owner: www-data:www-data
- Database User:
wpuser - Database Password:
YourStrongPassword123! - Database Host:
localhost - Database Charset:
utf8mb4 - Database Collate:
utf8mb4_unicode_ci
| Environment | Database Name | Previous Name |
|---|---|---|
| Live | s01_live |
s01_prod |
| Stage | s01_stage |
s01_stg |
| Dev | s01_dev |
(new) |
- Original databases:
s01_prodands01_stgwere renamed tos01_liveands01_stagerespectively - Backup date: 2026-01-15 00:22:22
- Backup location:
/root/db_backups_20260115_082222/
# Connect to MySQL
mysql -u wpuser -p
# Show all site01 databases
SHOW DATABASES LIKE 's01_%';
# Use specific database
USE s01_live;
# Show tables
SHOW TABLES;Purpose: Public-facing production website
| Property | Value |
|---|---|
| URL | https://site01.com |
| Alternate URL | https://www.site01.com |
| Directory | /var/www/site01.com/live |
| Database | s01_live |
| Apache Config | /etc/apache2/sites-available/site01.com.conf |
| Error Log | /var/www/site01.com/logs/live_error.log |
| Access Log | /var/www/site01.com/logs/live_access.log |
| wp-config.php | /var/www/site01.com/live/wp-config.php |
wp-config.php database settings:
define( 'DB_NAME', 's01_live' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'YourStrongPassword123!' );
define( 'DB_HOST', 'localhost' );Purpose: Pre-production testing, client review, and QA
| Property | Value |
|---|---|
| URL | https://stage.site01.com |
| Directory | /var/www/site01.com/staging |
| Database | s01_stage |
| Apache Config | /etc/apache2/sites-available/stage.site01.com.conf |
| Error Log | /var/www/site01.com/logs/stage_error.log |
| Access Log | /var/www/site01.com/logs/stage_access.log |
| wp-config.php | /var/www/site01.com/staging/wp-config.php |
wp-config.php database settings:
define( 'DB_NAME', 's01_stage' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'YourStrongPassword123!' );
define( 'DB_HOST', 'localhost' );Purpose: Active development, testing new features, experimentation
| Property | Value |
|---|---|
| URL | https://dev.site01.com |
| Directory | /var/www/site01.com/dev |
| Database | s01_dev |
| Apache Config | /etc/apache2/sites-available/dev.site01.com.conf |
| Error Log | /var/www/site01.com/logs/dev_error.log |
| Access Log | /var/www/site01.com/logs/dev_access.log |
| wp-config.php | /var/www/site01.com/dev/wp-config.php |
wp-config.php database settings:
define( 'DB_NAME', 's01_dev' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'YourStrongPassword123!' );
define( 'DB_HOST', 'localhost' );All environments can be accessed via two methods:
- Domain Names (After DNS configuration)
- Direct IP Address (Available immediately)
Requirements: DNS records must be configured (see DNS Configuration)
| Environment | URL | Alternative URL |
|---|---|---|
| Live | https://site01.com | https://www.site01.com |
| Stage | https://stage.site01.com | - |
| Dev | https://dev.site01.com | - |
Available immediately without DNS configuration:
| Environment | HTTP URL | Site Title |
|---|---|---|
| Live | http://72.62.125.163 | Wordpress Hostinger Live Tester Site |
| Stage | http://72.62.125.163:8001 | Wordpress Hostinger Staging Site |
| Dev | http://72.62.125.163:8002 | Wordpress Hostinger Development Site |
Important Notes for Port-based Access:
-
Port Configuration:
- Live uses port 80 (default HTTP port, no port number needed in URL)
- Stage uses port 8001 (must include
:8001in URL) - Dev uses port 8002 (must include
:8002in URL)
-
Direct Access:
- All three environments are directly accessible via IP + port
- No DNS configuration or hosts file modification needed
- Perfect for immediate testing and development
-
Firewall Note:
- Ensure ports 80, 8001, and 8002 are open on your server firewall
- Most hosting providers (including Hostinger) allow these ports by default
-
SSL/HTTPS Consideration:
- Port-based access uses HTTP (not HTTPS)
- For HTTPS, you need domain names and SSL certificates
- HTTP is fine for development/testing, but use HTTPS for production
If you want to test with domain names before configuring DNS:
On Mac/Linux:
sudo nano /etc/hosts
# Add these lines:
72.62.125.163 site01.com
72.62.125.163 www.site01.com
72.62.125.163 stage.site01.com
72.62.125.163 dev.site01.comOn Windows:
Edit: C:\Windows\System32\drivers\etc\hosts
Add:
72.62.125.163 site01.com
72.62.125.163 www.site01.com
72.62.125.163 stage.site01.com
72.62.125.163 dev.site01.com
After adding these entries, you can access all environments using domain names.
Via IP (Available Now - Recommended for Development):
Live: http://72.62.125.163
Stage: http://72.62.125.163:8001
Dev: http://72.62.125.163:8002
Via Domain (After DNS + SSL Configuration):
Live: https://site01.com
Stage: https://stage.site01.com
Dev: https://dev.site01.com
Via Domain (Before SSL, After DNS):
Live: http://site01.com
Stage: http://stage.site01.com
Dev: http://dev.site01.com
Via IP Address (Available Now):
| Environment | Admin URL | Database |
|---|---|---|
| Live | http://72.62.125.163/wp-admin | s01_live |
| Stage | http://72.62.125.163:8001/wp-admin | s01_stage |
| Dev | http://72.62.125.163:8002/wp-admin | s01_dev |
Via Domain (After DNS Setup):
| Environment | Admin URL | Database |
|---|---|---|
| Live | https://site01.com/wp-admin | s01_live |
| Stage | https://stage.site01.com/wp-admin | s01_stage |
| Dev | https://dev.site01.com/wp-admin | s01_dev |
Note: The admin username and password are stored in the WordPress database. Stage and Dev environments were copied from the original staging environment, so they share the same admin credentials initially.
Important Security: Change admin passwords for each environment after setup to prevent cross-environment access issues.
┌─────────────┐
│ Dev │ ← Make changes here
│ │ ← Install plugins, modify themes
│ │ ← Test new features
└──────┬──────┘
│
│ (After testing)
▼
┌─────────────┐
│ Stage │ ← Deploy for review
│ │ ← Client preview
│ │ ← QA testing
└──────┬──────┘
│
│ (After approval)
▼
┌─────────────┐
│ Live │ ← Deploy to production
│ │ ← Public website
└─────────────┘
-
Development Phase
- Work in Dev environment (https://dev.site01.com)
- Make code changes, install plugins, update themes
- Test functionality thoroughly
-
Staging Phase
- Copy changes from Dev to Stage
- Review on Stage environment (https://stage.site01.com)
- Client preview and approval
- QA testing
-
Production Phase
- Deploy approved changes to Live
- Monitor Live environment (https://site01.com)
- Keep backup before deployment
Method 1: Manual Copy (Simple)
# Copy theme from dev to stage
rsync -av /var/www/site01.com/dev/wp-content/themes/your-theme/ \
/var/www/site01.com/staging/wp-content/themes/your-theme/
# Copy plugin from stage to live
rsync -av /var/www/site01.com/staging/wp-content/plugins/your-plugin/ \
/var/www/site01.com/live/wp-content/plugins/your-plugin/Method 2: Git Version Control (Advanced)
# Initialize git in dev environment
cd /var/www/site01.com/dev/wp-content/themes/your-theme
git init
git add .
git commit -m "Initial commit"
# Push to repository and pull in other environmentsCopy database from Dev to Stage:
mysqldump s01_dev > /tmp/dev_backup.sql
mysql s01_stage < /tmp/dev_backup.sql
# Update URLs in stage database
mysql s01_stage -e "UPDATE wp_options SET option_value = 'https://stage.site01.com' WHERE option_name IN ('siteurl', 'home');"Copy database from Stage to Live:
# CAUTION: This will overwrite live data!
mysqldump s01_stage > /tmp/stage_backup.sql
mysql s01_live < /tmp/stage_backup.sql
# Update URLs in live database
mysql s01_live -e "UPDATE wp_options SET option_value = 'https://site01.com' WHERE option_name IN ('siteurl', 'home');"Configure these A records in your domain registrar's DNS settings:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | site01.com | 72.62.125.163 | 3600 |
| A | www.site01.com | 72.62.125.163 | 3600 |
| A | stage.site01.com | 72.62.125.163 | 3600 |
| A | dev.site01.com | 72.62.125.163 | 3600 |
# Check DNS resolution
nslookup site01.com
nslookup stage.site01.com
nslookup dev.site01.com
# Or use dig
dig site01.com +short
dig stage.site01.com +short
dig dev.site01.com +shortOnline Tools:
Note: DNS propagation can take 5-30 minutes (sometimes up to 48 hours).
# Update package list
apt update
# Install certbot and Apache plugin
apt install certbot python3-certbot-apache -yFor Live Site:
certbot --apache -d site01.com -d www.site01.comFor Stage Site:
certbot --apache -d stage.site01.comFor Dev Site:
certbot --apache -d dev.site01.comCertbot automatically configures auto-renewal. Verify with:
# Test renewal process
certbot renew --dry-run
# Check renewal timer
systemctl status certbot.timer# Renew all certificates
certbot renew
# Renew specific certificate
certbot renew --cert-name site01.com/etc/letsencrypt/live/site01.com/
├── cert.pem
├── chain.pem
├── fullchain.pem
└── privkey.pem
# Reload configuration (graceful, no downtime)
systemctl reload apache2
# Full restart
systemctl restart apache2
# Check status
systemctl status apache2# Live environment
tail -f /var/www/site01.com/logs/live_error.log
tail -f /var/www/site01.com/logs/live_access.log
# Stage environment
tail -f /var/www/site01.com/logs/stage_error.log
tail -f /var/www/site01.com/logs/stage_access.log
# Dev environment
tail -f /var/www/site01.com/logs/dev_error.log
tail -f /var/www/site01.com/logs/dev_access.log
# Apache error log
tail -f /var/log/apache2/error.log# Test configuration syntax
apache2ctl configtest
# Show loaded virtual hosts
apache2ctl -S# Backup all environments
mysqldump s01_live > /root/backups/s01_live_$(date +%Y%m%d).sql
mysqldump s01_stage > /root/backups/s01_stage_$(date +%Y%m%d).sql
mysqldump s01_dev > /root/backups/s01_dev_$(date +%Y%m%d).sql
# Backup all databases (compressed)
mysqldump s01_live | gzip > /root/backups/s01_live_$(date +%Y%m%d).sql.gz# Restore from backup
mysql s01_live < /root/backups/s01_live_20260115.sql
# Restore from compressed backup
gunzip < /root/backups/s01_live_20260115.sql.gz | mysql s01_live# If URLs need to be changed in database
mysql s01_live -e "UPDATE wp_options SET option_value = 'https://site01.com' WHERE option_name IN ('siteurl', 'home');"# Reset ownership
chown -R www-data:www-data /var/www/site01.com/live
chown -R www-data:www-data /var/www/site01.com/staging
chown -R www-data:www-data /var/www/site01.com/dev
# Reset directory permissions
find /var/www/site01.com -type d -exec chmod 755 {} \;
# Reset file permissions
find /var/www/site01.com -type f -exec chmod 644 {} \;# Enable site
a2ensite site01.com.conf
# Disable site
a2dissite site01.com.conf
# Reload after changes
systemctl reload apache2# Delete cache files (example for W3 Total Cache)
rm -rf /var/www/site01.com/live/wp-content/cache/*
# Or use WP-CLI
wp cache flush --path=/var/www/site01.com/livePossible Causes:
- PHP error
- Plugin conflict
- Theme issue
Solutions:
# Check error logs
tail -50 /var/www/site01.com/logs/live_error.log
# Enable WordPress debugging
# Edit wp-config.php and add:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
# Disable all plugins (via database)
mysql s01_live -e "UPDATE wp_options SET option_value = '' WHERE option_name = 'active_plugins';"Error: "Error establishing a database connection"
Solutions:
# Verify database exists
mysql -e "SHOW DATABASES LIKE 's01_%';"
# Test database connection
mysql -u wpuser -p s01_live
# Check wp-config.php credentials
grep "define.*DB_" /var/www/site01.com/live/wp-config.php
# Restart MySQL
systemctl restart mysql# Fix ownership and permissions
chown -R www-data:www-data /var/www/site01.com/live
find /var/www/site01.com/live -type d -exec chmod 755 {} \;
find /var/www/site01.com/live -type f -exec chmod 644 {} \;
# Make uploads directory writable
chmod 755 /var/www/site01.com/live/wp-content/uploads# Check configuration
apache2ctl configtest
# Check what's using port 80
netstat -tlnp | grep :80
# View Apache error log
tail -50 /var/log/apache2/error.log# Test SSL certificate
openssl s_client -connect site01.com:443
# Renew certificate
certbot renew --force-renewal --cert-name site01.com
# Check certificate expiry
certbot certificatesProblem: Live site redirects to stage or dev
Solution:
# Update URLs in database
mysql s01_live -e "UPDATE wp_options SET option_value = 'https://site01.com' WHERE option_name IN ('siteurl', 'home');"
# Clear WordPress cache
wp cache flush --path=/var/www/site01.com/live
# Clear browser cache and test in incognito modeCurrent Backups:
/root/db_backups_20260115_082222/s01_prod_backup.sql(original production)/root/db_backups_20260115_082222/s01_stg_backup.sql(original staging)
Daily Backups (Recommended):
#!/bin/bash
# Save as /root/scripts/daily-backup.sh
BACKUP_DIR="/root/backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
# Backup databases
mysqldump s01_live | gzip > $BACKUP_DIR/s01_live.sql.gz
mysqldump s01_stage | gzip > $BACKUP_DIR/s01_stage.sql.gz
mysqldump s01_dev | gzip > $BACKUP_DIR/s01_dev.sql.gz
# Backup files (uploads)
tar -czf $BACKUP_DIR/live_uploads.tar.gz /var/www/site01.com/live/wp-content/uploads/
tar -czf $BACKUP_DIR/stage_uploads.tar.gz /var/www/site01.com/staging/wp-content/uploads/
tar -czf $BACKUP_DIR/dev_uploads.tar.gz /var/www/site01.com/dev/wp-content/uploads/
# Delete backups older than 30 days
find /root/backups -type d -mtime +30 -exec rm -rf {} \;Set up cron job:
# Edit crontab
crontab -e
# Add daily backup at 2 AM
0 2 * * * /root/scripts/daily-backup.shRestore Database:
# Stop using the site (optional but recommended)
a2dissite site01.com.conf
systemctl reload apache2
# Restore database
mysql s01_live < /root/backups/20260115/s01_live.sql
# Re-enable site
a2ensite site01.com.conf
systemctl reload apache2Restore Files:
# Restore uploads
tar -xzf /root/backups/20260115/live_uploads.tar.gz -C /
# Fix permissions
chown -R www-data:www-data /var/www/site01.com/live/wp-content/uploads/-
Keep WordPress Updated
# Use WP-CLI to update WordPress core wp core update --path=/var/www/site01.com/live # Update plugins wp plugin update --all --path=/var/www/site01.com/live # Update themes wp theme update --all --path=/var/www/site01.com/live
-
Strong Passwords
- Database password:
YourStrongPassword123!(consider changing to a stronger one) - WordPress admin passwords should be unique for each environment
- Use password manager to store credentials securely
- Database password:
-
File Permissions
- Directories: 755
- Files: 644
- wp-config.php: 600 (even more restrictive)
chmod 600 /var/www/site01.com/live/wp-config.php
-
Disable File Editing in WordPress Add to wp-config.php:
define('DISALLOW_FILE_EDIT', true);
-
Limit Login Attempts
- Install plugin like "Limit Login Attempts Reloaded"
- Configure fail2ban for WordPress
-
Hide WordPress Version Add to functions.php:
remove_action('wp_head', 'wp_generator');
-
Regular Backups
- Daily database backups
- Weekly full site backups
- Off-server backup storage
-
Firewall
# Install and configure UFW apt install ufw ufw allow 22/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable
-
SSH Security
- Keep SSH key secure
- Consider disabling password authentication
- Use SSH key authentication only
-
Monitor Logs
# Watch for suspicious activity tail -f /var/www/site01.com/logs/live_access.log | grep -i "POST"
- WordPress core up to date
- All plugins up to date
- All themes up to date
- Strong admin passwords set
- File permissions correct (755/644)
- wp-config.php permissions restrictive (600)
- File editing disabled in WordPress
- SSL certificates installed and auto-renewing
- Regular backups configured
- Firewall enabled
- Login attempt limiting enabled
- WordPress version hidden
- SSH key secure
- Database password strong
Once the multi-environment setup is stable, consider implementing Git version control:
-
Initialize Git Repository
cd /var/www/site01.com/dev git init -
Create .gitignore
wp-config.php wp-content/uploads/ *.log .htaccess -
Connect to Remote Repository
git remote add origin git@github.com:yourusername/site01.git git push -u origin main
-
Deployment Workflow
- Dev → commit and push changes
- Stage → pull from repository
- Live → pull from repository (after approval)
- SSH:
ssh -i ~/.ssh/id_ed25519_gaia root@72.62.125.163 - Root Access: Available
- Provider: Hostinger
- Support: https://www.hostinger.com/contact
- Official Docs: https://wordpress.org/support/
- WP-CLI Docs: https://wp-cli.org/
- Codex: https://codex.wordpress.org/
- ✅ Initial multi-environment setup completed
- ✅ Created three databases: s01_live, s01_stage, s01_dev
- ✅ Renamed s01_prod → s01_live
- ✅ Renamed s01_stg → s01_stage
- ✅ Installed WordPress in dev environment
- ✅ Configured Apache virtual hosts for all three environments
- ✅ Updated WordPress URLs in all databases
- ✅ Set proper file permissions
- ✅ Created database backups in /root/db_backups_20260115_082222/
- 📋 Next: DNS configuration and SSL setup
This documentation is proprietary to the site01.com project.
Last Updated: 2026-01-15 Documentation Version: 1.0 Author: Roger Woolie (with Claude Code assistance)