Q1: What is wpstaging?
A1: wpstaging is a high-performance command-line tool to process WP Staging backup files (.wpstg). It allows you to extract, normalize, inspect, and restore backups without using WordPress itself.
Q2: Which operating systems are supported?
A2:
Windows, Linux, and macOS. Pre-built binaries are available for all major OSes. On Windows, the wpstaging binary requires Windows 10 or Server 2016 or later. The CMD installer one-liner (install.cmd) uses curl and additionally requires Windows 10 1803 (April 2018) or Server 2019 or later. The PowerShell one-liner (irm https://wp-staging.com/install.ps1 | iex) uses the built-in System.Net.WebClient and works on Windows 10 / Server 2016 without curl. Users on earlier builds can also download the binary manually.
Q3: Do I need a license to use this tool?
A3:
Yes. You need a valid WP Staging Agency or Developer license key to access backup files.
Q4: How fast is it?
A4:
Benchmarks show it can extract a 20GB backup in under 36 seconds on an AMD Ryzen 7 PRO 7840U with a fast SSD running Ubuntu 20.04.
Q5: How do I install wpstaging?
A5:
Use the quick install script (recommended):
Linux / macOS / WSL:
curl -fsSL https://wp-staging.com/install.sh | bashWindows (PowerShell):
irm https://wp-staging.com/install.ps1 | iexWindows (CMD):
curl -fsSL https://wp-staging.com/install.cmd -o install.cmd && install.cmd && del install.cmdThe installer will:
- Download the latest version for your platform
- Verify checksums for security
- Install to
~/.local/bin(Linux/macOS) or%LOCALAPPDATA%\Programs\wpstaging(Windows) - Add to your PATH automatically
- Install shell completion (Bash and Zsh on Linux/macOS)
Manual Installation:
- Download the latest release: GitHub Releases (main.zip)
- Extract and locate the binary in the
buildfolder for your platform - Make it accessible:
# Linux/macOS (user installation) mkdir -p ~/.local/bin mv wpstaging ~/.local/bin/ chmod +x ~/.local/bin/wpstaging # Or system-wide sudo mv wpstaging /usr/local/bin/ sudo chmod +x /usr/local/bin/wpstaging
For complete installation details, see the Installation section in README.
Q6: Can I install to a custom directory?
A6:
Yes. Use the --bin-dir (-d) flag to install the binary to a specific directory:
curl -fsSL https://wp-staging.com/install.sh | bash -s -- --bin-dir /opt/toolsThe installer still configures PATH, shell aliases, and completion scripts. On Windows (PowerShell): -d "C:\Tools". On Windows (CMD): --bin-dir C:\Tools.
Q6a: Can I download files without installing?
A6a:
Yes. Use the --extract (-e) flag to download all installable files to a directory without running the full installation:
curl -fsSL https://wp-staging.com/install.sh | bash -s -- --extract /tmp/wpstaging-filesNo PATH changes, aliases, or completion scripts are configured. The binary and completion files are copied to the specified directory.
Note: --bin-dir and --extract cannot be used together.
Q6a1: Can I pass extra arguments to the binary during installation?
A6a1:
Yes. Use the --cli-args (-a) flag to pass extra arguments to every wpstaging binary call the installer makes (version check and license registration):
curl -fsSL https://wp-staging.com/install.sh | bash -s -- -a "--debug"On Windows (PowerShell): -a "--debug". On Windows (CMD): -a "--debug".
Note: Only simple space-separated flags are supported. Arguments with embedded spaces are not supported.
Q6b: Can I use it without installing?
A6b:
Yes, you can run the binary directly from the extracted folder.
Q6c: How do I uninstall wpstaging?
A6c:
Use the built-in uninstall command (recommended):
Default uninstall (deactivates license, removes binary, shell completions, and PATH entries):
wpstaging uninstallFull uninstall (additionally removes cache, Docker sites, etc.):
wpstaging uninstall --fullAlternatively, download and run the uninstall script directly:
Linux / macOS / WSL:
curl -fsSL https://wp-staging.com/uninstall.sh | bashWindows (PowerShell):
irm https://wp-staging.com/uninstall.ps1 | iexWindows (CMD):
curl -fsSL https://wp-staging.com/uninstall.cmd -o uninstall.cmd && uninstall.cmd && del uninstall.cmdThe default uninstall command:
- Deactivates your license on the WP Staging server and deletes the local key
- Removes the wpstaging binary
- Removes shell completion scripts for Bash and Zsh (Linux/macOS)
- Removes PATH entries from shell RC files (Linux/macOS) or user PATH (Windows)
The --full flag additionally removes:
- Cache and working directories:
- Linux:
~/.config/wpstaging/ - macOS:
~/Library/Application Support/wpstaging/ - Windows:
%APPDATA%\wpstaging\
- Linux:
- Docker sites and data
Note: If you've used Docker features, run wpstaging remove first to remove Docker containers and data before uninstalling the CLI.
For complete uninstallation details, see the Uninstallation section in README.
Q7: How do I run wpstaging?
A7:
Use the following command:
wpstaging [commands] [flags] <backupfile.wpstg>Commands must come first. Flags and the backup file can appear in any order.
Q8: What are the main commands?
A8:
WP Staging CLI has four main command groups:
Site Commands:
add– Add a new WordPress sitelist [hostname...]– List all sites or show details for specific sitesdel [hostname...]– Delete one or more sites, or all sitesenable– Enable a WordPress sitedisable– Disable a WordPress sitereset– Reset a WordPress site
Backup Commands:
extract– Extract files, database, or metadata from a WP STAGING backuprestore– Restore a WordPress site from a WP STAGING backupdump-header– View backup header detailsdump-index– View backup index detailsdump-metadata– View metadata from a backup file
Docker Commands:
start [hostname]– Start Docker containers (all sites or specific site)stop [hostname]– Stop and remove containers (all sites or specific site)restart [hostname]– Restart containers (all sites or specific site)status [hostname...]– Display container status (all sites or specific sites)shell <hostname> [root]– Open an interactive shell in the PHP containerremove– Stop containers and remove all Docker dataupdate-hosts-file– Update the local hosts file with site entriesgenerate-compose-file [hostname]– Generate docker-compose.yml (one site or all sites)generate-docker-file [hostname]– Generate Docker config files (one site or all sites)
Other Commands:
register– Activate your WP Staging Pro licenseupdate– Update WP Staging CLI to the latest versionclean– Clean up cached data, license info, and temporary fileshelp– Help about any command
Use wpstaging [command] --help for detailed information about each command.
Q9: How do I register my license?
A9:
You can provide your license in three ways:
Option 1: Register Your License (Recommended)
# Interactive mode (prompts for license key)
wpstaging register
# Non-interactive mode (useful for scripts/automation)
wpstaging register -l=YOUR_LICENSE_KEY
wpstaging register --license=YOUR_LICENSE_KEYThis will validate your license with WP STAGING servers and store it encrypted locally for future use.
Option 2: Environment Variable
# Unix/Linux/macOS
export WPSTGPRO_LICENSE=YOUR_LICENSE_KEY
# Windows CMD
set WPSTGPRO_LICENSE=YOUR_LICENSE_KEY
# Windows PowerShell
$env:WPSTGPRO_LICENSE="YOUR_LICENSE_KEY"Option 3: Command-Line Flag
wpstaging extract --license=YOUR_LICENSE_KEY backup.wpstgUsing license registration (Option 1) is recommended as it keeps sensitive data out of command history and works seamlessly across all commands.
Q9a: How do I check my current license status?
A9a:
Run:
wpstaging register --statusThis shows your registered license details from local storage: license holder, email, plan name, and expiration date. No network call is made. If no license is registered, the command tells you and prints the registration command.
Q10: How do I deactivate my license?
A10:
To deactivate and remove your license from this machine:
wpstaging deactivateThis command will:
- Display your current license information
- Prompt for confirmation
- Deactivate the license on WP STAGING servers
- Remove the encrypted license file from local storage
You can also use the clean license command for the same purpose:
wpstaging clean licenseAfter deactivating, you'll need to re-enter your license key the next time you run a command.
Q11: How can I extract and normalize the database file?
A11:
wpstaging extract --normalizedb backupfile.wpstg
Q12: How can I restore to a different WordPress path?
A12:
Use the --path flag:
wpstaging restore --path=/var/www/site backupfile.wpstgIf running from the WP root, --path is optional.
Q13: Can I extract only specific parts of the backup?
A13:
Yes, using “Only-Filters”:
--only-wpcontent– Only extractwp-content.--only-plugins– Only extract plugins.--only-file=<string>– Extract only matching files.
Q14: Can I skip certain parts?
A14:
Yes, using "Skip-Filters":
--skip-wpcontent– Skipwp-content.--skip-uploads– Skip uploads.--skip-file=<string>– Skip files matching a string.
Q15: How do --skip-file and --only-file work? Do they support regex or wildcards?
A15:
These flags use simple substring matching, not regex or wildcards. The string you provide is matched anywhere in the file path.
How it works:
--only-file=<string>– Extract only files whose full path contains the string--skip-file=<string>– Skip files whose full path contains the string
Examples:
# Extract only SQL files (matches any path containing ".sql")
wpstaging extract --only-file=.sql backup.wpstg
# Extract only files from uploads directory
wpstaging extract --only-file=/uploads/ backup.wpstg
# Extract only images (matches .jpg, .jpeg, .png, .gif)
wpstaging extract --only-file=.jpg backup.wpstg
# Skip all log files
wpstaging extract --skip-file=.log backup.wpstg
# Skip cache directory
wpstaging extract --skip-file=/cache/ backup.wpstg
# Skip specific plugin
wpstaging extract --skip-file=/wp-content/plugins/problematic-plugin/ backup.wpstgImportant notes:
- ❌ Does not support wildcards like
*.sqlorfile?.txt - ❌ Does not support regex patterns like
^backup.*\.sql$ - ✅ Uses simple substring search:
"uploads"matches/wp-content/uploads/image.jpg - ✅ Case-sensitive matching:
".SQL"will not match".sql" - ✅ Matches anywhere in the full file path
Combining filters:
# Extract only images from uploads, but skip thumbnails
wpstaging extract --only-file=/uploads/ --skip-file=-150x150 backup.wpstg
Q16: How do I restore to an external database?
A16:
Use DB-related flags:
wpstaging restore --path=/var/www/site \
--db-name=dbname --db-user=user --db-pass=pass --db-host=host backupfile.wpstg
Q17: Can I overwrite existing files or DB tables?
A17:
Yes, use:
--overwrite=<yes|no>– Overwrite target directory.--overwrite-db=<yes|no>– Remove DB tables not in backup.--overwrite-wproot=<yes|no>– Remove WP root files not in backup or core.
Q18: What is the default configuration file used for?
A18:
The config file is used to store flags only, not commands. It allows you to avoid repeatedly typing commonly used flags, such as paths, database credentials, filters, or Docker flags.
Default config file location (OS-specific):
- Linux/Unix:
~/.config/wpstaging/wpstaging.conf - macOS:
~/Library/Application Support/wpstaging/wpstaging.conf - Windows:
%APPDATA%\wpstaging\wpstaging.conf
Q19: Can I skip reading the config file?
A19:
Yes, use the --skip-config flag when running any command. This ensures the CLI ignores the config file entirely and only uses flags provided on the command line.
Q20: Do CLI flags override the config file?
A20:
Yes. Any flag provided directly in the CLI command will override the corresponding value in the config file.
Q21: What kind of flags can I define in the config file?
A21:
You can define most CLI flags in the config file to avoid repeatedly typing them on the command line. Each flag should be on its own line with its value (e.g., --path=/var/www or --debug).
However, the following flags are ignored and cannot be defined in the config file:
-h, --help, -v, --version, --about, --yes, --options
You can define flags such as:
- WordPress Path:
--path - Database Credentials:
--db-name,--db-user,--db-pass,--db-host - File Overwrite Settings:
--overwrite,--overwrite-db,--overwrite-wproot - Filters:
--only-wpcontent,--skip-uploads,--only-plugins, etc. - Docker Defaults:
--php,--http-port,--https-port,--db-port,--env-path, etc. - General Flags:
--debug,--quiet,--verify
Q22: Can I use multiple config files?
A22:
WP-Staging-CLI only reads one config file at a time. By default, it uses the OS-specific config location (see Q18), but you can override it with a custom file using --config=file.conf. You can also temporarily bypass it using --skip-config and pass all flags directly on the CLI.
Q23: Where is the Docker environment setup?
A23:
By default, Docker-related files are stored in ~/wpstaging/. Each WordPress site has its own isolated directory in ~/wpstaging/sites/<sitename>/. You can change the parent location with the --env-path=<path> flag.
Note: The --env-path specifies a parent path. The CLI automatically appends wpstaging/ to it. For example:
--env-path=/tmp/test→ actual path:/tmp/test/wpstaging/- Site directory:
/tmp/test/wpstaging/sites/<hostname>/
Q24: How do I create a new WordPress site with Docker?
A24:
Use the add command to create a new WordPress site with its own isolated Docker environment. Here's what happens step by step:
-
Creates site-specific directory structure:
~/wpstaging/sites/<sitename>/(site directory)config/(PHP, Nginx, MariaDB configurations for this site)data/(persistent data for MariaDB, PHP, Mailpit for this site).env(site configuration and credentials)
-
Generates configuration files:
docker-compose.ymlwith site-specific containers- PHP configuration (php.ini, PHP-FPM pool settings)
- Nginx configuration (server block, SSL certificates)
- MariaDB configuration
- WP-CLI installation
-
Pulls required Docker images (if not already cached):
- PHP-FPM (version specified with
--php, default: 8.1) - Nginx (stable-alpine-slim)
- MariaDB (latest, unless
--external-db) - Mailpit (latest, unless
--disable-mailpit)
- PHP-FPM (version specified with
-
Starts site-specific Docker containers:
- Creates isolated containers with names like
wpstg-sitename-php,wpstg-sitename-nginx, etc. - Automatically assigns unique IP and ports
- Configures container communication
- Creates isolated containers with names like
-
Installs WordPress:
- Downloads WordPress core
- Creates database and user (uses default credentials: admin/123456, or secure random passwords with
--secure-credentials) - Installs WordPress with admin credentials
- Updates
/etc/hostsfile for local access
Example usage:
# Create a new WordPress site (basic)
wpstaging add mysite.local
# Create with custom configuration
wpstaging add mysite.local \
--php=8.3 \
--http-port=8080 \
--https-port=8443 \
--db-port=3307
# Create with specific WordPress version
wpstaging add mysite.local --wp=6.4After creating a site, use wpstaging list to see all your sites and their ports.
Q25: How can I assign a specific IP to my Docker site?
A25:
Use --container-ip=<ipv4> when creating a site. If you don't specify an IP, the CLI automatically assigns the next available IP from the range 127.3.2.1 - 127.3.2.254:
- Linux/Windows: Automatic IP allocation — loopback IPs are always available, no sudo required
- macOS: Automatic IP alias binding enabled by default — requires sudo (passwordless sudo recommended, see Q87). Use
--skip-macos-auto-ipto disable and bind IPs manually withifconfig lo0 alias
Important: When you explicitly specify --container-ip with an IP that's already used by another site, the CLI will show an error and suggest the next available IP:
Error: The IP address 127.3.2.1 is already in use by 'existingsite.local'.
You can either:
- Use the next available IP: --container-ip=127.3.2.2
- Remove the --container-ip flag to auto-assign an available IP
Without --container-ip, the CLI automatically finds the next available IP without errors.
Q26: How many sites can I create? What's the maximum limit?
A26:
The limit depends on how you allocate IPs:
With unique IPs per site (default behavior):
- Maximum 254 sites — limited by our loopback IP range 127.3.2.1 - 127.3.2.254
- Each site gets its own IP automatically:
- Linux/Windows: No sudo required (loopback IPs always available)
- macOS: Automatic IP binding with sudo (passwordless sudo recommended - see Q87)
- All sites can use the same ports (e.g., all on port 80/443) since they're on different IPs
With shared IPs (using --container-ip to reuse IPs):
- No IP-based limit — create as many sites as your system resources allow
- Limited by: CPU cores, available RAM, disk space, and available ports
- Each site on the same IP must use different ports (e.g., 8080, 8081, 8082, etc.)
- Practical limit typically 10-50 sites depending on hardware specs
Example with shared IP:
# All sites on same IP, different ports
wpstaging add site1.local --container-ip=127.3.2.1 --https-port=8443
wpstaging add site2.local --container-ip=127.3.2.1 --https-port=8444
wpstaging add site3.local --container-ip=127.3.2.1 --https-port=8445System resource considerations:
- CPU: 2-4 cores recommended per 10 sites
- RAM: ~500MB per site (PHP + MariaDB + Nginx)
- Disk: ~1GB per site (WordPress files + database)
Q27: How can I configure PHP version or ports?
A27:
PHP version and ports can be configured in several ways:
1. During site creation (using add command):
wpstaging add mysite.local --php=8.3 --http-port=8080 --https-port=84432. Switch PHP version (using switch-php command):
wpstaging switch-php mysite.local 8.4This updates the configuration, regenerates Docker files, and restarts the containers automatically. Supported PHP versions: 7.4, 8.1, 8.2, 8.3, 8.4.
3. Switch WordPress version (using switch-wp command):
wpstaging switch-wp mysite.local 6.5
wpstaging switch-wp mysite.local 6.7-beta1
wpstaging switch-wp mysite.local latest
wpstaging switch-wp mysite.local nightlySupported version formats: X.Y, X.Y.Z, X.Y-beta1, X.Y-RC1, latest, nightly.
This replaces only the WordPress core files while preserving the database, themes, plugins, and uploads. Containers must be running before switching.
4. Edit the .env file manually:
# Edit ~/wpstaging/sites/<hostname>/.env
PHP_VERSION=8.3
HTTP_PORT=8080
HTTPS_PORT=8443Then restart the site for changes to take effect:
wpstaging restart mysite.localAvailable flags for add command:
--php=<version>(default: 8.1)--http-port=<port>(default: 80)--https-port=<port>(default: 443)
Note: Port settings can only be set during initial creation or by editing .env manually. PHP version can be changed at any time using the switch-php command. WordPress version can be changed using the switch-wp command.
Q28: How do I configure MariaDB?
A28:
You can set --db-port=<port> (default 3306) and --db-root=<password> for root password (default 123456). Database credentials use default values (admin/123456) unless you specify --secure-credentials which generates cryptographically secure random passwords. All credentials are stored in the site's .env file. You can also use an external database with --external-db which disables the MariaDB container.
Q29: Can I modify or use a custom docker-compose.yml file?
A29:
The docker-compose.yml file is auto-generated and recreated when:
- Using the
addcommand to create a site - The
.envfile is modified - Any ports or IP configuration changes
Using a custom compose file location:
Use --compose-file to specify a different compose file path. The file will stay in sync with wpstaging's workflow as long as you consistently use the same path:
# Create site with custom compose file
wpstaging add mysite.local --compose-file=/path/to/custom-compose.yml
# Start/restart/stop must use the same path
wpstaging start mysite.local --compose-file=/path/to/custom-compose.yml
wpstaging restart mysite.local --compose-file=/path/to/custom-compose.ymlBetter approach - Add to config file:
To avoid specifying --compose-file every time, add it to your config file (e.g., ~/.config/wpstaging/wpstaging.conf on Linux — see Q18 for OS-specific paths):
--compose-file /path/to/custom-compose.ymlNow all commands will automatically use your custom compose file path, and it will stay synchronized with your configuration changes.
Note: If you manually edit the compose file outside of wpstaging, those changes will be overwritten when wpstaging regenerates it.
Q30: How do I configure WordPress settings?
A30:
The add command supports various WordPress configuration flags:
Database settings (for WordPress):
--db-host- Database host (default:localhost)--db-name- Database name (default: sanitized from hostname, e.g.,example_local)--db-user- Database user (default:user_<dbname>)--db-pass- Database password (default:admin, or secure random with--secure-credentials)--db-prefix- Table prefix (default:wp_)--db-ssl- Enable SSL connection to database
Admin settings (for WordPress):
--admin-user- Admin username (default:admin)--admin-pass- Admin password (default:admin, or secure random with--secure-credentials)--admin-email- Admin email (default:admin@<sitename>)
WordPress options:
--wp- WordPress version to install (default:latest). AcceptsX.Y,X.Y.Z,X.Y-beta1,X.Y-RC1,latest, ornightly. The actual installed version is stored in.env(e.g.6.7.2). Useswitch-wpto change it later without reinstalling.--multisite- Install as WordPress Multisite (subdirectory mode by default)--subdomains- Enable subdomain multisite mode (implies--multisite). Optional: pass comma-separated hostnames to pre-register, e.g.--subdomains=blog.mysite.local,shop.mysite.local
Note: When using --from with a multisite backup, --multisite is auto-detected from the backup. Subdomain mode is also auto-detected from the restored database. You don't need to pass these flags manually.
Example:
# Subdirectory multisite
wpstaging add mysite.local --multisite
# Subdomain multisite
wpstaging add mysite.local --subdomains
# Subdomain multisite with pre-registered hostnames
wpstaging add mysite.local --subdomains=blog.mysite.local,shop.mysite.local
# Auto-detect from backup (no flags needed)
wpstaging add mysite.local --from=backup.wpstg
Q31: How do I disable or re-enable the Mailpit container?
A31:
Pass --disable-mailpit when creating, resetting, or regenerating a site:
wpstaging add mysite.local --disable-mailpit
wpstaging reset mysite.local --disable-mailpit
wpstaging generate-docker-file mysite.local --disable-mailpit
wpstaging reconfigure mysite.local --disable-mailpitThe DISABLE_MAILPIT=true setting is saved in the site's .env file so future runs keep Mailpit off.
To re-enable Mailpit on a site where it was previously disabled, pass --disable-mailpit=false:
wpstaging reconfigure mysite.local --disable-mailpit=false
wpstaging reset mysite.local --disable-mailpit=falsereconfigure --disable-mailpit=false updates the configuration and recreates the containers, so the Mailpit container is added back without losing WordPress files or database data. reset --disable-mailpit=false does the same after reinstalling WordPress core.
Q32: How do I use secure random passwords?
A32:
By default, sites use simple default credentials (admin/admin, root password: 123456) for convenience during development. To generate cryptographically secure random passwords, use the --secure-credentials flag:
wpstaging add mysite.local --secure-credentialsThis will auto-generate:
- Database password (32 characters)
- Database root password (32 characters)
- WordPress admin password (24 characters)
All passwords are stored in the site's .env file. You can also manually specify individual passwords:
wpstaging add mysite.local --admin-pass=MySecurePass123 --db-pass=DbSecurePass456
Q33: How do I use an external database?
A33:
Use the --external-db flag along with database connection details to use an external database instead of the containerized MariaDB:
# Option 1: Specify port in host
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100:3306 \
--db-name=wordpress_db \
--db-user=dbuser \
--db-pass=dbpass
# Option 2: Specify port separately
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100 \
--db-port=3307 \
--db-name=wordpress_db \
--db-user=dbuser \
--db-pass=dbpassRequirements:
- Database server must be accessible from the Docker containers
- Database must already exist on the server
- User must have appropriate permissions
The tool will validate the connection during setup. If validation fails, EXTERNAL_DB will be removed from the .env file and you'll need to fix the connection details before trying again.
Note: When using --external-db:
- The MariaDB container is not created
--db-portcan be used to specify the external database port (default: 3306)- The
DB_HOSTvalue is saved to the.envfile
Q33b: What database permissions are required for external databases?
A33b:
When using an external database, the WordPress installation script uses smart detection to determine the best setup approach.
How It Works:
The script follows this logic:
- First, it checks if the provided user credentials already work:
- Can connect to the database server
- Database exists
- User has access to the database
- User has required privileges (CREATE TABLE, DROP TABLE)
- If user credentials work - proceeds directly with WordPress installation (no root needed)
- If user credentials don't work - uses root credentials (if provided) to create database/user
- If no root credentials - falls back to
wp db create(requires CREATE DATABASE privilege)
Option 1: Pre-configured Database (Recommended for External DB)
If your database and user are already set up with proper permissions, you don't need root credentials:
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100:3306 \
--db-name=wordpress_db \
--db-user=wpuser \
--db-pass=secure_passwordThe script will verify the credentials work and proceed directly.
Option 2: With Root Credentials
If the database or user doesn't exist, provide root credentials to create them:
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100:3306 \
--db-name=wordpress_db \
--db-user=wpuser \
--db-pass=userpass \
--db-root=root_passwordThe script will:
- Check if user credentials work first (skips root setup if they do)
- If not, wait for database connection with retry logic (up to 20 seconds)
- Create the database if it doesn't exist
- Create the database user and grant privileges
- Proceed with WordPress installation
Required Database User Permissions:
| Privilege | Required | Purpose |
|---|---|---|
| SELECT | Yes | Read data |
| INSERT | Yes | Add data |
| UPDATE | Yes | Modify data |
| DELETE | Yes | Remove data |
| CREATE | Yes | Create tables |
| DROP | Yes | Drop/reset tables |
| ALTER | Yes | Modify table structure |
| INDEX | Yes | Manage indexes |
Recommended Setup for External Databases:
-- Run on your external database server (as admin)
-- 1. Create the database
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 2. Create user with full privileges on that database
CREATE USER 'wpuser'@'%' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wpuser'@'%';
FLUSH PRIVILEGES;Then use:
wpstaging add mysite.local --external-db \
--db-host=your-db-server:3306 \
--db-name=wordpress_db \
--db-user=wpuser \
--db-pass=secure_passwordCommon Issues:
| Error | Cause | Solution |
|---|---|---|
Access denied |
Invalid credentials | Verify username/password |
Unknown database |
Database doesn't exist | Pre-create the database or use --db-root |
CREATE command denied |
Missing privilege | Grant CREATE privilege or pre-create DB |
Connection refused |
Network/firewall issue | Ensure DB is accessible from Docker network |
Q34: How do I connect to SSL-enabled external databases?
A34:
The CLI automatically handles SSL-enabled external databases with certificate verification disabled for development environments. If your external database has SSL enabled:
# SSL is handled automatically - no additional flags needed
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100:3306 \
--db-name=wordpress_db \
--db-user=dbuser \
--db-pass=dbpassOptional: Use --db-ssl flag to explicitly enable SSL in WordPress configuration:
wpstaging add mysite.local --external-db \
--db-host=192.168.1.100:3306 \
--db-name=wordpress_db \
--db-user=dbuser \
--db-pass=dbpass \
--db-sslWhat happens automatically:
- MySQL/MariaDB CLI commands use
--ssl-verify-server-cert=0to skip certificate verification - WP-CLI database commands work transparently through wrapper scripts
- WordPress database connections use
MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERTwhen--db-sslis specified
Technical Details:
The CLI creates wrapper scripts for mysql, mariadb, and mysqldump commands in ~/.wp-cli/bin/ that automatically add SSL flags. These wrappers:
- Skip certificate verification (appropriate for development/testing)
- Filter out unsupported
--no-defaultsflag - Work transparently with WP-CLI database operations
For production environments requiring proper certificate validation, configure the database server with trusted certificates and provide the CA certificate to WordPress.
Q35: Can I switch from external database to internal database?
A35:
Yes, but you must use the reset command to properly reconfigure the site. Simply changing EXTERNAL_DB=false in the .env file will not work.
Why the restriction? When you switch from external to internal database:
- The MariaDB container needs to be created in
docker-compose.yml - A new database needs to be initialized
- WordPress needs to be reinstalled with the new database
How to switch:
# 1. Stop the site
wpstaging stop mysite.local
# 2. Edit the .env file to remove or change EXTERNAL_DB
nano /path/to/sites/mysite.local/.env
# Remove the line: EXTERNAL_DB=true
# Or change it to: EXTERNAL_DB=false
# 3. Run reset to reconfigure and reinstall
wpstaging reset mysite.localNote: The reset command will reinstall WordPress and erase all site data. If the site was multisite, reset preserves the multisite setting from the .env file. If you have a backup, you can reset and restore in one step:
wpstaging reset mysite.local --from=backup.wpstgWhat happens if I try to start without reset? The CLI will detect the mismatch and show an error:
MariaDB container not found in docker-compose.yml for mysite.local.
This site was configured with --external-db previously.
To create the database and reinstall WordPress, run:
wpstaging reset mysite.local
This protection prevents you from starting a site with a broken database configuration.
Q36: How do I enable debug messages?
A36:
Use -d or --debug.
Q37: Can I suppress output?
A37:
Yes, use -q or --quiet.
Q38: How do I verify the integrity of extracted files?
A38:
Use the --verify flag.
Q39: How do I enable debug messages?
A39:
Use -d or --debug.
Q40: Can I suppress output?
A40:
Yes, use -q or --quiet.
Q41: How do I verify the integrity of extracted files?
A41:
Use the --verify flag.
Q42: How can I test what the CLI extracts from docker-compose.yml?
A42:
Use the compose-info command to display all data parsed from the compose file:
wpstaging compose-infoThis command is useful for debugging and verifying what configuration values the CLI tool extracts from your docker-compose.yml. It displays data in an alphabetically sorted, formatted output:
CONTAINER_IP : 172.20.0.1
EXTRA_HOST : aaa.local=127.5.6.8
EXTRA_HOST_2 : wp-staging.local=127.5.6.8
EXTRA_HOST_3 : xdebug.host=127.5.6.8
MARIADB_PORT : 3306
NGINX_PORT : 80
NGINX_PORT_2 : 443
PHP_VERSION : 8.2
The extracted data includes:
- PHP_VERSION - PHP version from the image tag
- CONTAINER_IP - Host IP for port mappings
- NGINX_PORT, NGINX_PORT_2 - HTTP and HTTPS ports
- MARIADB_PORT - Database port
- MAILPIT_PORT - Mailpit HTTP port
- EXTRA_HOST, EXTRA_HOST_2, etc. - Extra hosts entries
This is particularly helpful when troubleshooting port conflicts or verifying custom configurations.
Q43: I get "port already in use" error when using the Docker environment. What should I do?
A43:
The CLI now has automatic port conflict detection and resolution! The tool automatically detects port conflicts and tries alternative ports.
When you run add or start commands, the CLI automatically:
- Checks if ports are available — including ports claimed by other wpstg sites, ports used by non-wpstg Docker containers (see Q101c), and OS-level port checks
- Tries predefined fallback ports if the default is in use
- Generates random ports if all fallbacks are occupied
- Notifies you of the port changes
Default ports and their fallback ranges:
| Service | Default | Fallback Ports | Count | Random Range |
|---|---|---|---|---|
| HTTP (Nginx) | 80 | 8844, 8845, 8846, 8855, 8866, 8888, 8899, 8877, 8878, 8879, 8889 | 10 | 49152-65535 |
| HTTPS (Nginx) | 443 | 4444, 4445, 4446, 4455, 4466, 4488, 4499, 4456, 4467, 4468, 4477 | 10 | 49152-65535 |
| MariaDB | 3306 | 3344, 3345, 3346, 3355, 3366, 3388, 3399, 3356, 3357, 3358, 3359, 3360, 3370 | 10 | 49152-65535 |
| Mailpit | 8025 | 8044, 8045, 8046, 8055, 8066, 8088, 8099, 8056, 8067, 8077 | 10 | 49152-65535 |
Example output when port is in use:
HTTPS port 443 is already in use. Automatically switching to port 4444.
You can specify custom ports when creating a new site using the add command. Each site has its own configuration, so you can customize ports per site.
For NGINX (HTTP/HTTPS):
wpstaging add mysite.local --http-port=8080 --https-port=8443For MariaDB:
wpstaging add mysite.local --db-port=3307For Mailpit:
wpstaging add mysite.local --mailpit-http-port=8026Change container IP:
wpstaging add mysite.local --container-ip=127.3.2.5Configure all ports at once:
wpstaging add mysite.local \
--http-port=8080 --https-port=8443 \
--db-port=3307 --mailpit-http-port=8026Note: Each site maintains its own configuration in its .env file, so different sites can use different ports and settings.
Check which process is using a port:
- Linux/macOS:
sudo lsof -i :80orsudo netstat -tulpn | grep :80 - Windows:
netstat -ano | findstr :80
Alternative solution - Disable unused services:
# If you're using an external database (disables MariaDB container)
wpstaging add mysite.local --external-db --db-host=your-db-host --db-name=your-db --db-user=your-user --db-pass=your-pass
# If you don't need Mailpit
wpstaging add mysite.local --disable-mailpitWhen services are disabled, their port validation is automatically skipped.
Q44: Why can't I use --site-url with the root command?
A44:
Flags like --site-url, --db-prefix, --normalizedb, and --verify are command-specific. You must use them with their respective commands:
--site-urland--db-prefixwork with bothextractandrestore--normalizedbonly works withextract- Use:
wpstaging extract --site-url=https://example.com backup.wpstg - Not:
wpstaging --site-url=https://example.com extract backup.wpstg
Q45: What flags are available globally vs command-specific?
A45:
Global flags (work with all commands):
--working-dir,--debug,--quiet,--yes,--allow-root,--json,--page,--page-size,--verbose
Extract-specific flags:
--normalizedb,--overwrite,--site-url,--db-prefix,--verify
Restore-specific flags:
--path,--site-url,--db-prefix,--verify,--skip-extract,--overwrite,--overwrite-db,--overwrite-wproot, all--db-*flags
Docker-specific flags:
--env-path,--compose-file,--container-ip,--php,--http-port,--https-port,--wp-site-url, etc.
Q46: Are there short aliases for common flags?
A46:
Yes, several flags have convenient aliases:
Environment Path:
--env-path(or--dockerize-pathas hidden alias)
Container IP:
--container-ipor--ip(both work the same)
Output Directory (for dump commands):
- All dump commands (
dump-header,dump-metadata,dump-index) now support--outputdirflag
Example:
# These are equivalent
wpstaging add site.local --env-path=/custom/path --container-ip=127.3.2.5
wpstaging add site.local --env-path=/custom/path --ip=127.3.2.5
# Dump commands with output directory
wpstaging dump-header backup.wpstg --outputdir=/tmp/output
Q47: When does license validation occur?
A47:
License validation happens automatically when you run any backup-related or Docker command (extract, restore, dump-*, add, etc.). Commands like help, register, clean, status, and list skip license validation for faster access.
Q48: Do I need a license to view help messages?
A48:
No. Running wpstaging --help or wpstaging extract --help does not require license validation. You only need a valid license when executing actual operations.
Q49: How is my license stored and validated?
A49:
After you register your license using wpstaging register, the key is encrypted and stored locally. The CLI automatically validates your license when running backup-related or Docker commands, and caches the validation results for 4 hours to minimize API calls.
Q49a: What happens when my license expires?
A49a:
When your license expires, the CLI displays your license information (licensee name and expiration date) and exits with an error message. The license key is preserved (not deleted) so you can see who the license belongs to and when it expired.
Example output:
WP Staging CLI
Licensed to John Doe
Valid through 27.02.2026 | Expired - Extend License https://wp-staging.com/
Error: License expired - Extend License https://wp-staging.com/
To resolve: Extend your license at https://wp-staging.com/your-account. Once renewed, the CLI will work automatically on the next command.
Q50: I get "Error: Backup file does not exist" but the file is there. Why?
A50:
Make sure you're providing the correct path to the .wpstg file. Use absolute paths if running from a different directory:
wpstaging extract /full/path/to/backup.wpstg
Q51: Can I run multiple extraction/restore operations simultaneously?
A51:
No. The CLI uses file-based locking to prevent concurrent operations on the same backup file. If you need parallel operations, use different backup files.
Q52: What does "This application cannot be run as root" mean?
A52:
On Linux/macOS, the CLI blocks root execution by default as a security best practice.
Note: This check does not apply to Windows. Windows users can run elevated (Administrator) without issues because Windows uses a different permission model (ACLs vs Unix uid/gid).
Why running as root creates issues on Linux/macOS:
-
File Ownership:
- Extracted files will be owned by root (UID 0)
- Web server (www-data, nginx, apache) cannot read/write these files
- WordPress will not function properly
- Requires manual permission fixes:
chown -R www-data:www-data /path
-
System Protection:
- Reduces risk of accidental modifications to system directories
- Follows the principle of least privilege
- Standard approach for CLI tools performing file operations
If you must use --allow-root (Linux/macOS only):
# Only in Docker containers or isolated environments
sudo wpstaging extract --allow-root backup.wpstg
# Fix ownership immediately after
sudo chown -R www-data:www-data ./wpstaging-output
Q53: I get "Error: Failed to open the backup file" on Windows. Help?
A53:
Ensure:
- The file path doesn't contain special characters
- You have read permissions on the file
- The file isn't locked by another program
- Use quotes around paths with spaces:
wpstaging extract "C:\My Backups\backup.wpstg"
Q54: How can I speed up extraction?
A54:
- Use fast storage (SSD/NVMe) for both source and destination
- Skip unnecessary parts with
--skip-*flags - Disable verification (
--verifyadds overhead) - Run on systems with good I/O performance
Q55: Does the CLI support multi-threading?
A55:
The CLI is optimized for single-threaded sequential extraction with efficient streaming. Multi-threading isn't needed as disk I/O is typically the bottleneck.
Q56: How much memory does the CLI require?
A56:
Memory usage is minimal (typically <100MB) even for large backups because the CLI uses streaming extraction rather than loading entire files into memory.
Q57: Why does my browser show "Your connection is not private" or "Not Secure" warnings?
A57:
This warning appears when your browser doesn't trust the SSL certificate used by your local development site. There are two common scenarios:
Scenario 1: Using self-signed certificates (default without mkcert)
- Self-signed certificates are not trusted by browsers by default
- You'll see warnings like "NET::ERR_CERT_AUTHORITY_INVALID"
- You can click "Advanced" → "Proceed to site" to bypass (not recommended for production)
Scenario 2: mkcert CA not installed in system trust store
- The mkcert Certificate Authority (CA) needs to be installed to your system
- This happens automatically during first setup when you confirm the installation prompt
- If you skipped the installation, you'll still see browser warnings
How to fix this:
-
Recommended: Use mkcert (automatic when creating a site)
wpstaging add mysite.local
When creating your first site, you'll be prompted to install the security certificate. Choose "Yes" to install the CA to your system trust store. This is a one-time operation that works for all future sites.
-
If you skipped CA installation, create another site:
wpstaging add anothersite.local
The tool will detect that the CA isn't installed and prompt you again.
-
Manual bypass (not recommended): Click "Advanced" → "Proceed to site" in your browser. This only works for the current session and doesn't actually solve the trust issue.
Q58: What is mkcert and why does WP Staging CLI use it?
A58: mkcert is a trusted tool for creating locally-trusted SSL certificates for development environments. WP Staging CLI uses mkcert to provide a seamless HTTPS development experience.
What mkcert does:
- Creates a local Certificate Authority (CA) on your computer
- Generates SSL certificates signed by this CA
- Installs the CA to your system's trust store (browsers, OS)
- Makes your local sites trusted automatically - no more browser warnings
Why we use mkcert instead of self-signed certificates:
Self-signed certificates (old approach):
- ❌ Browser shows scary "Not Secure" warnings
- ❌ Requires manual bypass for every site
- ❌ Breaks some JavaScript features requiring HTTPS
- ❌ Service Workers and PWA features don't work
- ❌ Different behavior from production environments
- ❌ Chrome requires additional flags to bypass warnings
mkcert certificates (current approach):
- ✅ Automatically trusted by all major browsers (Chrome, Firefox, Safari, Edge)
- ✅ No browser warnings - green padlock icon
- ✅ Identical HTTPS behavior to production
- ✅ Service Workers, PWA, and secure features work properly
- ✅ Better testing environment - catches HTTPS-related issues early
- ✅ One-time setup, works for all local sites
Q59: Why not just use HTTP instead of HTTPS for local development?
A59:
While HTTP is simpler, using HTTPS for local development is strongly recommended for several important reasons:
1. Production Parity:
- Most production WordPress sites use HTTPS (required for SEO, security, trust)
- Developing with HTTP can hide HTTPS-related bugs that only appear in production
- Mixed content issues (HTTP resources on HTTPS pages) won't be caught during development
2. Modern Browser Features Require HTTPS:
- Service Workers (Progressive Web Apps, offline functionality)
- Web Push Notifications
- Geolocation API (in some browsers)
- Camera/Microphone access (getUserMedia API)
- Payment Request API
- Clipboard API
- HTTP/2 and HTTP/3 protocols
- Some third-party APIs only work over HTTPS
3. WordPress-Specific Issues:
- WordPress admin over HTTPS prevents cookie hijacking
- Many WordPress plugins require HTTPS for certain features
- WooCommerce and payment gateways require HTTPS
- WordPress recommends HTTPS for login pages to protect credentials
4. Cookie Security:
- Secure cookies (Secure flag) only work over HTTPS
- SameSite cookie attributes behave differently over HTTP
- Session hijacking is easier over HTTP
5. Developer Tools and Testing:
- Some browser DevTools features only work over HTTPS
- Can't properly test HTTPS redirects and headers over HTTP
- Performance testing differs (HTTP/2, TLS overhead)
Using mkcert solves all these issues while keeping local development simple and warning-free.
Q60: Why does mkcert installation require sudo (Linux/macOS) or administrator permission (Windows)?
A60:
Installing a Certificate Authority (CA) to the system trust store is a system-level operation that requires elevated privileges. Here's why:
What the installation does:
- Creates a root CA certificate in
~/wpstaging-dockerize/docker/nginx/ca/ - Installs the CA to system trust stores:
- Linux:
/usr/local/share/ca-certificates/(requires sudo) - macOS: System Keychain (requires admin password)
- Windows: Certificate Manager (requires administrator)
- Linux:
- Updates browser trust stores:
- Linux: Chrome/Chromium NSS database (no sudo needed)
- macOS: Explicitly sets trustRoot policy for Firefox compatibility
- Windows: Uses Windows certificate store (all browsers)
Why sudo/admin is required:
- System trust stores are protected locations
- Modifying them affects all users on the system
- Security measure to prevent malware from installing rogue CAs
- One-time operation - subsequent certificate generation doesn't need sudo
What if I skip the sudo installation?
- The CA files are still created locally
- SSL certificates are generated and used by Nginx
- BUT: Your browser won't trust them (shows warnings)
- You can still bypass warnings manually with "Proceed to site"
Security note: The CA created by mkcert is stored locally and never leaves your computer. It's only used to sign certificates for your local development sites.
Q61: Does the mkcert CA installation affect my system security?
A61:
The mkcert CA is designed specifically for local development and is safe to install. Here's what you should know:
Security design:
- ✅ CA private key is stored locally only (
~/wpstaging/stack/mkcert/ca/rootCA-key.pem) - ✅ Never transmitted over network
- ✅ Only used for local development sites
- ✅ Cannot be used to intercept real websites (different domains)
- ✅ Standard practice used by thousands of developers worldwide
Best practices:
- Keep the CA private key secure (don't share or commit to git)
- Only install CAs you created yourself
- Uninstall the CA when you stop using local development (optional)
- The CLI stores CA in project directory for isolation
To remove Docker environment (if needed):
-
Stop and remove Docker environment:
wpstaging remove
-
Remove the CA from your system trust store (optional). The simplest way is:
wpstaging sweep-ca-trust --include-legacy
This removes both current
WP Staging CLI development CAentries and any legacymkcert development CAentries left by older builds. See Q120.To remove manually, look for
WP Staging CLI development CAin:- Linux:
/usr/local/share/ca-certificates/(system) and~/.pki/nssdb/(Chrome) - macOS: Keychain Access (search "WP Staging CLI")
- Windows: Certificate Manager, under Trusted Root Certification Authorities (search "WP Staging CLI")
- Linux:
Q62: What happens when I'm asked about security certificate installation?
A62:
When you create your first WordPress site with the add command, you'll see a prompt like this:
════════════════════════════════════════════════════════════════════════════════
The next action will install a security certificate to your system.
This allows your browser to trust the SSL certificates created by this tool.
Sudo permission is required.
════════════════════════════════════════════════════════════════════════════════
Continue to install? [y/N]:
If you choose "Yes" (recommended):
- You'll be prompted for your sudo/admin password (one-time)
- The tool installs the mkcert CA to your system trust store (Safari/Chrome trust it immediately)
- Platform-specific browser support is configured automatically:
- Linux: Installs to Chrome's NSS database
- macOS: Sets trustRoot policy for Firefox (Safari/Chrome already covered by step 2)
- All current and future local sites will be automatically trusted
- You'll see green padlock in browser - no warnings
- This is a one-time operation - you won't be prompted again
If you choose "No" or skip:
- The tool creates local CA files anyway (for certificate generation)
- SSL certificates are generated and used by Nginx
- Your browser will show "Not Secure" warnings
- You'll need to manually bypass warnings with "Advanced → Proceed to site"
- You can create another site later and install the CA when prompted again
Important: This prompt only appears once. If the CA is already installed in your system trust store (detected via X.509 verification), the prompt is skipped automatically.
Q63: My browser still shows warnings after installing the CA. What's wrong?
A63:
If you're still seeing warnings after confirming CA installation, try these troubleshooting steps:
1. Verify CA is actually installed:
- Linux:
ls /usr/local/share/ca-certificates/ | grep rootCA(should show rootCA.pem) - macOS: Open Keychain Access, search for "mkcert" (should appear in System keychain)
- Windows: Open certmgr.msc, check "Trusted Root Certification Authorities"
2. For Chrome/Chromium on Linux specifically: The CA must be installed to both the system trust store AND the NSS database. The CLI does this automatically, but you can verify:
certutil -d sql:$HOME/.pki/nssdb -L | grep mkcertIf missing, the CLI should have installed it, but you can manually add:
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "mkcert CA" -i ~/wpstaging-dockerize/docker/nginx/ca/rootCA.pem3. Restart your browser: After CA installation, close and reopen your browser completely (not just the tab).
4. Check certificate details in browser:
- Click the "Not Secure" warning in address bar
- View certificate details
- Verify the certificate includes your site's hostname and the container IP
5. Verify certificate was generated:
ls ~/wpstaging/sites/yoursite.local/config/nginx/certs/Should show yoursite.local.crt and yoursite.local.key (not self-signed.crt).
6. Recreate the site to regenerate everything:
wpstaging del yoursite.local
wpstaging add yoursite.local7. Clear browser SSL cache:
- Chrome: Go to
chrome://settings/security→ "Manage certificates" → Clear SSL state - Firefox: Settings → Privacy & Security → Certificates → View Certificates → Servers → Delete cached certificates
Q64: Can I use my own SSL certificates instead of mkcert?
A64:
Yes, but it's not recommended for local development. If you still want to use custom certificates:
Option 1: Replace generated certificates After creating a site, replace the certificate files:
cp your-cert.crt ~/wpstaging/sites/yoursite.local/config/nginx/certs/yoursite.local.crt
cp your-key.key ~/wpstaging/sites/yoursite.local/config/nginx/certs/yoursite.local.key
wpstaging restart yoursite.localOption 2: Disable mkcert (use self-signed fallback) If mkcert download or CA installation fails, the CLI automatically falls back to self-signed certificates. You'll see browser warnings but the site will still work.
Why mkcert is better:
- No manual certificate generation or management
- Automatic trust - no browser warnings
- Per-site certificates with proper SANs (hostname + IPs)
- Works across all browsers without configuration
Q65: Can I use external databases with the Docker environment?
A65:
Yes, use --external-db flag to disable the MariaDB container and connect to your external database:
wpstaging add mysite.local --external-db \
--db-host=external-db.example.com \
--db-name=mydb --db-user=user --db-pass=passThis disables the local MariaDB container and configures WordPress to use your external database server.
Q66: How do I manage multiple WordPress sites in Docker?
A66:
Use the site management commands:
# Add new sites (one at a time)
wpstaging add site1.local
wpstaging add site2.local
# List sites
wpstaging list # List all sites
wpstaging list site1.local # Show details for one site
wpstaging list site1.local site2.local # Show details for multiple sites
# Check status
wpstaging status # Show all sites status
wpstaging status site1.local site2.local # Show status for specific sites
# Manage individual sites
wpstaging stop site1.local
wpstaging start site1.local
wpstaging shell site1.local
# Delete sites
wpstaging del site1.local # Delete one site
wpstaging del site1.local site2.local # Delete multiple sites
wpstaging del # Delete all sites (with confirmation)
# Manage all sites at once
wpstaging start # Start all sites
wpstaging stop # Stop all sites
wpstaging restart # Restart all sitesEach site runs in its own isolated set of containers with unique IPs and ports.
Q66b: Why can't I add multiple sites at once with the add command?
A66b:
The add command only accepts one site URL at a time because each site requires unique configuration:
- Different container IP (auto-assigned from 127.3.2.1-254 range)
- Different database credentials (especially with
--secure-credentials) - Different ports (if conflicts exist)
- Site-specific SSL certificates
To add multiple sites quickly, use a loop:
# Bash (Linux/macOS)
for site in site1.local site2.local site3.local; do
wpstaging add $site --yes
done
# PowerShell (Windows)
@("site1.local", "site2.local", "site3.local") | ForEach-Object {
wpstaging add $_ --yes
}Note: The --yes flag auto-confirms prompts for unattended operation.
Q67: How can I check the status of my sites?
A67:
There are two commands for checking site status:
1. The list command shows site configuration details:
# List all sites with status
wpstaging list
# Check specific site
wpstaging list mysite.localList Status Information:
- Enabled - Running: Site is configured and containers are running
- Enabled - Stopped: Site is configured but containers are stopped
- Disabled - Stopped: Site has been disabled (use
enableto re-enable) - Missing root path: Site directory is missing
- Missing compose file: docker-compose.yml file is missing
Example list Output:
Host : mysite.local
URL : https://mysite.local
Path : ~/wpstaging/sites/mysite.local/www/mysite.local
Status : Enabled - Running
Total: 3, Running: 2, Stopped: 1
2. The status command shows container-level details:
# Show all containers
wpstaging status
# Check specific site containers
wpstaging status mysite.localStatus Output Format: The output is organized into three sections with separators:
- Active - Currently running containers
- Stopped - Containers stopped with
stopcommand (can be started again) - Disabled - Containers disabled with
disablecommand (needenablefirst)
Example status Output:
CONTAINER STATUS PORTS
-----------------------------------------------------------------------------------
wpstg-site1-local-nginx Up 5 minutes 127.3.2.1:80->80/tcp, 127.3.2.1:443->443/tcp
wpstg-site1-local-php Up 5 minutes 9000/tcp
wpstg-site1-local-mariadb Up 5 minutes 127.3.2.1:3306->3306/tcp
wpstg-site1-local-mailpit Up 5 minutes (healthy) 1025/tcp, 1110/tcp, 127.3.2.1:8025->8025/tcp
-----------------------------------------------------------------------------------
wpstg-site2-local-nginx Stopped 127.3.2.2:80->80/tcp, 127.3.2.2:443->443/tcp
wpstg-site2-local-php Stopped 9000/tcp
wpstg-site2-local-mariadb Stopped 127.3.2.2:3306->3306/tcp
wpstg-site2-local-mailpit Stopped 1025/tcp, 1110/tcp, 127.3.2.2:8025->8025/tcp
-----------------------------------------------------------------------------------
wpstg-site3-local-nginx Disabled 127.3.2.3:80->80/tcp, 127.3.2.3:443->443/tcp
wpstg-site3-local-php Disabled 9000/tcp
wpstg-site3-local-mariadb Disabled 127.3.2.3:3306->3306/tcp
wpstg-site3-local-mailpit Disabled 1025/tcp, 1110/tcp, 127.3.2.3:8025->8025/tcp
Q68: Can I change a site's configuration after creation?
A68:
Configuration is stored in each site's .env file. To change configuration:
-
Stop the site:
wpstaging stop mysite.local
-
Edit the .env file:
nano ~/wpstaging/sites/mysite.local/.env -
Start the site to apply changes:
wpstaging start mysite.local
Alternatively, delete and recreate the site with new configuration:
wpstaging del mysite.local
wpstaging add mysite.local --http-port=8080 --https-port=8443
Q69: Where are Docker logs stored?
A69:
Docker container logs are accessible via docker logs. The CLI stores configuration files in ~/wpstaging/ by default (or your custom --env-path).
Q70: How does the per-site container architecture work?
A70:
The CLI uses a per-site container architecture where each WordPress site runs in its own isolated set of containers. This means:
Key Features:
- Each site gets its own containers:
wpstg-sitename-php,wpstg-sitename-nginx,wpstg-sitename-mariadb,wpstg-sitename-mailpit - Automatic IP allocation from reserved range (127.3.2.1 - 127.3.2.254) on Linux/Windows
- Automatic port assignment to avoid conflicts (all platforms)
- Per-site .env configuration file stores all settings
- No interference between sites - they run completely independently
Example:
# First site gets 127.3.2.1 with ports 8080, 8443, 3306, 8025
wpstaging add site1.local
# Second site gets 127.3.2.2 with ports 8081, 8444, 3307, 8026
wpstaging add site2.local
# List all running sites
wpstaging listBenefits:
- Run multiple sites simultaneously without conflicts
- Each site has isolated database, PHP version, and configuration
- Start/stop/delete sites independently
- Configuration is preserved in
.envfiles across restarts
Q71: How does automatic IP allocation work (Linux/Windows)?
A71:
On Linux and Windows, the CLI automatically manages IP addresses from a reserved loopback range:
How it works:
- When you add a site without specifying
--container-ip, the CLI automatically assigns the next available IP from range 127.3.2.1-254 - The assigned IP is saved in the site's
.envfile - On restart, the site reuses its saved IP
- If the saved IP is in use by another container, the CLI automatically finds the next available IP
Example:
# First site - automatically assigned 127.3.2.1
wpstaging add site1.local
# Saved in ~/wpstaging/sites/site1.local/.env
# Second site - automatically assigned 127.3.2.2
wpstaging add site2.local
# Restart site1 - reuses 127.3.2.1 from .env
wpstaging start site1.localWhy this matters:
- No manual IP configuration needed
- No IP conflicts between sites
- Each site can use same port numbers (e.g., both use port 8080) because they're on different IPs
- Configuration persists across restarts
Q72: How does IP allocation work on macOS?
A72:
On macOS, automatic IP alias binding is enabled by default. The CLI automatically assigns each site a unique IP from the loopback range 127.3.2.1 - 127.3.2.254 and binds it for you (requires sudo). This allows multiple sites to use the same port numbers (like 80/443) on different IPs without conflicts.
Default behavior (automatic IP binding enabled):
# First site - automatically assigned 127.3.2.1
wpstaging add site1.local
# Second site - automatically assigned 127.3.2.2
wpstaging add site2.local
# Both sites can use the same ports (e.g., 80/443) on different IPs
# Requires sudo for IP binding (prompted automatically)How it works:
- On first
add, the CLI installs a macOS LaunchDaemon that reads site configurations and creates loopback aliases only for existing sites at boot - Only one sudo prompt is needed for installation
- Aliases persist across reboots automatically
- The daemon is removed when you run
wpstaging remove
Fallback: If daemon installation fails, the CLI falls back to creating a single IP alias for the current site (as before).
Passwordless sudo (recommended for macOS): On macOS, the LaunchDaemon install requires one sudo prompt. To avoid the prompt, see Q87 for passwordless sudo setup.
Summary: On macOS, a LaunchDaemon reads site configurations and creates loopback aliases only for existing sites at boot. One-time sudo on first add, aliases survive reboots. Linux/Windows don't need this because loopback IPs are always available. Use --skip-macos-auto-ip on macOS to skip daemon installation.
Q73: How can I disable automatic IP alias binding on macOS?
A73:
Automatic IP alias binding from the loopback range 127.3.2.1 - 127.3.2.254 is enabled by default on macOS (Linux/Windows don't need this since loopback IPs are always available). If you prefer manual IP alias binding without sudo requirements, use the --skip-macos-auto-ip flag:
# Disable automatic IP alias binding on macOS (manual ifconfig lo0 alias required)
wpstaging add site1.local --skip-macos-auto-ip
# Then manually bind each IP as needed (one-time per boot, requires password)
sudo ifconfig lo0 alias 127.3.2.1 netmask 255.255.255.255
sudo ifconfig lo0 alias 127.3.2.2 netmask 255.255.255.255
# ... and so on for each siteNote: With automatic IP binding enabled (default), passwordless sudo is highly recommended for the best experience. See Q87 for setup instructions.
Q74: What does --normalizedb actually do?
A74:
It replaces WP Staging placeholders in the database SQL file with actual values:
{WPSTG_TMP_PREFIX}→ temporary table prefix{WPSTG_FINAL_PREFIX}→ final table prefix{WPSTG_NULL}→ SQL NULL- This is required before manually importing the database to MySQL.
Q74a: Can I restore a multisite backup to a single-site WordPress?
A74a:
A full multisite network backup (multi) cannot be restored to a single-site WordPress. The restore command will stop with an error. Network subsite and main-site backups can be restored to a single-site.
Q74b: How do I set up subdomain multisite?
A74b:
Use the --subdomains flag when creating a site. This configures WordPress for subdomain mode, sets up wildcard SSL certificates, and adds wildcard nginx routing.
# Basic subdomain multisite
wpstaging add mysite.local --subdomains
# With pre-registered subsite hostnames (both forms are equivalent)
wpstaging add mysite.local --subdomains=blog.mysite.local
wpstaging add mysite.local --subdomains blog.mysite.localThe --subdomains flag automatically enables --multisite. You don't need to pass both.
When hostnames are provided, the CLI auto-creates WordPress subsites:
- Subdomains (e.g.,
blog.mysite.local) are created viawp site create --slug=blog - Custom domains (e.g.,
custom.local) are created with native domain mapping on WordPress 4.5+. On older WordPress versions, custom domains are skipped.
Custom domains automatically install sunrise.php so that login and cookies work correctly on the custom domain.
You can also add more subsites later in the WordPress admin under Network Admin > Sites. Then run update-subdomains to sync the new hostnames:
wpstaging update-subdomains mysite.local
Q74c: What does update-subdomains do?
A74c:
The update-subdomains command (alias: usub) queries WordPress for all subsites and updates the local environment:
- Runs
wp site listinside the container to discover all subsite domains - Regenerates the nginx config with the discovered hostnames in
server_name - Regenerates the SSL certificate to cover all discovered hostnames
- Updates the docker-compose file with DNS aliases for cross-site communication
- Restarts containers to apply the changes
- Updates
/etc/hostswith entries for each subsite
wpstaging update-subdomains mysite.localRun this command after creating, removing, or modifying subsites in WordPress admin.
Q74d: Is subdomain multisite auto-detected from backups?
A74d:
Yes. When restoring a multisite backup with --from, the CLI auto-detects both multisite mode and subdomain mode from the restored database. It then discovers all subsites and configures nginx, SSL, and /etc/hosts automatically. No flags needed:
wpstaging add mysite.local --from=backup.wpstg
Q74e: Can I use custom domain names for subsites?
A74e:
Yes, but only with local development TLDs (.local, .test, .dev, .home, etc.). Custom domains can be set up in two ways:
Option 1: During site creation (WordPress 4.5+ required):
wpstaging add mysite.local --subdomains=custom.localThe CLI auto-creates the subsite and maps the custom domain using native WordPress domain mapping.
Option 2: After site creation:
Map a custom domain in WordPress admin (Network Admin > Sites > Edit), then run update-subdomains to update nginx, SSL, and hosts:
wpstaging update-subdomains mysite.localPublic TLDs (.com, .org, etc.) are not supported. The tool is designed for local development only.
Q74f: Does reset preserve subdomain multisite settings?
A74f:
Yes. The reset command reads IS_SUBDOMAIN_MULTISITE and SUBDOMAIN_HOSTNAMES from the site's .env file and reinstalls WordPress with the same subdomain multisite configuration.
Q75: Can I restore only the database without files?
A75:
Yes, combine filters:
wpstaging restore --only-dbfile --path=/var/www/site backup.wpstg
Q76: How do I change the database prefix during restore?
A76:
Use --db-prefix:
wpstaging restore --path=/var/www/site --db-prefix=newwp_ backup.wpstg
Q77: Does restore support database SSL connections?
A77:
Yes, use SSL-related flags:
wpstaging restore --path=/var/www/site \
--db-ssl-ca-cert=/path/to/ca.pem \
--db-ssl-cert=/path/to/client-cert.pem \
--db-ssl-key=/path/to/client-key.pem \
--db-ssl-mode=preferred \
backup.wpstg
Q78: Is my license key stored securely?
A78:
The license key is stored encrypted in .dataIndex* files within the working directory (~/.config/wpstaging/ on Linux). The encrypted data is protected and requires proper file permissions.
If you store the license key in the config file, ensure it has proper permissions:
# Linux/macOS - Protect config file
chmod 600 ~/.config/wpstaging/wpstaging.conf
# macOS (alternative location)
chmod 600 ~/Library/Application\ Support/wpstaging/wpstaging.conf
# Also protect the entire working directory
chmod 700 ~/.config/wpstaging/Windows: The working directory is %APPDATA%\wpstaging\ with default Windows ACL permissions.
Q79: Can I use the CLI in CI/CD pipelines?
A79:
Yes, use environment variables and --yes flag for non-interactive operation:
export WPSTGPRO_LICENSE=YOUR_KEY
wpstaging extract --yes backup.wpstg
Q80: Does the CLI validate SSL certificates for license checks?
A80:
Yes, the CLI uses HTTPS for license validation and enforces SSL certificate verification for security.
Q81: What backup versions are supported?
A81:
The CLI supports WP Staging backup format versions 1 and 2. Version detection happens automatically when parsing the backup header.
Q82: Can I inspect backup contents without extracting?
A82:
Yes, use dump commands:
wpstaging dump-header backup.wpstg
wpstaging dump-metadata backup.wpstg
wpstaging dump-index backup.wpstg
wpstaging dump-index --data backup.wpstg # detailed file list
# All dump commands support --json for structured output
wpstaging dump-header --json backup.wpstg
wpstaging dump-metadata --json backup.wpstg
wpstaging dump-index --json backup.wpstg
Q83: Are compressed backups supported?
A83:
Yes, the CLI automatically handles compressed chunks within the .wpstg backup format. No additional decompression needed.
Q84: Can I extract to a custom directory?
A84:
Yes, use --output-dir:
wpstaging extract --output-dir=/custom/path backup.wpstgFiles land in a wpstaging-output folder under the path you pass (/custom/path/wpstaging-output/) so the command does not overwrite unrelated files in the path you supply. The older --outputdir name still works as a hidden alias.
Q84a: Can I choose where remote backups download to?
A84a:
Yes, use --download-dir on commands that take a remote backup URL (extract, restore, add, reset):
wpstaging extract --download-dir=/custom/path --from=https://example.com/backup.wpstgFiles land in a wpstaging-download folder under the path you pass (/custom/path/wpstaging-download/) so downloaded backups stay separate from other files in the path you supply.
Q85: How do I update the CLI to the latest version?
A85:
Use the built-in update command:
# Update to latest binary
wpstaging update
# Only check for available updates
wpstaging update --check
# Update using install script (also updates current binary location)
wpstaging update --full
# Update or downgrade to a specific version
wpstaging update --version 1.5.0
wpstaging update --version v1.5.0
# Target a pre-release version
wpstaging update --version 1.6.0-beta.1
# Check whether a specific version exists
wpstaging update --version 1.5.0 --check
# Downgrade using install script
wpstaging update --version 1.5.0 --fullThe CLI also checks for updates automatically once per day and shows a notice if a new version is available.
Version comparison results:
- If your version matches the latest release:
Already up to date (vX.Y.Z). - If your version is ahead of the latest release (e.g., a pre-release build):
Current version (vX.Y.Z) is ahead of latest release (vA.B.C). - If a newer version is available: shows an update banner with instructions
How update works (default):
- Asks for confirmation before downloading
- Downloads the new binary in chunks with progress display and resume support (up to 3 retries)
- Verifies the SHA256 checksum, then replaces the current binary
- If the binary is in a user-writable location (e.g.,
~/.local/bin/), replacement is done directly - If the binary is in a system directory (e.g.,
/usr/local/bin/), sudo is used automatically - On Windows, a helper script replaces the binary after the process exits (Windows locks running executables)
How update --version works:
- Accepts both
1.5.0andv1.5.0formats, including pre-release versions like1.5.0-beta.1 - Skips fetching the latest stable tag and targets the specified version directly
- Allows both upgrading and downgrading, except v1.10.0 and v1.11.0 which are refused (those releases have a bug that breaks the built-in update; see Q85a)
- Shows a warning when downgrading to a version before v1.7.0 (where the
updatecommand was introduced), with instructions to upgrade again using the install script - Validates the version by fetching its manifest from GitHub
- With
--check, only checks whether the version exists without installing - Works with both
--full(installer script) and default (binary replacement) modes
How update --full works:
- Downloads and runs the installer script from wp-staging.com (sets up aliases, shell completions, PATH)
- After the installer finishes, if the current binary is at a different location than the installed one (e.g., running from
/tmp/wpstaging), copies the installed binary to replace it - When combined with
--version, passes-v <version>(withvprefix) to the installer script. The installer scripts also normalise the prefix independently, so direct usage likeinstall.sh -v 1.7.0works. - Preserves original file permissions and uses sudo if needed
- On Windows, a detached wrapper script handles the copy after the installer completes
Environment variable overrides (for development/testing):
WPSTGCLI_UPDATE_TAGS_URL— Override the GitHub tags API URLWPSTGCLI_UPDATE_MANIFEST_URL— Override the manifest URL template (%s= version)WPSTGCLI_UPDATE_BINARY_URL— Override the binary download URL template (%s= version,%s= binary name)
Q85a: wpstaging update says "Update check skipped for development version" on a real release. What do I do?
A85a:
This affects users currently on v1.10.0 or v1.11.0. Those two releases have a bug where the built-in update command always reports them as a development build and refuses to check for new versions. The automatic daily update check is also affected, so no upgrade banner appears either.
To recover, reinstall from the official installer one time:
curl -fsSL https://wp-staging.com/install.sh | bashThis overwrites the binary in place and brings you to v1.11.1 or later, where update works as expected. Your sites, license, and configuration are not touched. From v1.11.1 onwards the daily update check and wpstaging update work normally again.
Q86: Where can I report bugs or request features?
A86:
Visit the official WP Staging support at https://wp-staging.com/support/ or check the CLI documentation for the issue tracker URL.
Q87: How do I set up passwordless sudo for the wpstaging binary?
A87:
The wpstaging binary uses sudo for these operations:
- Updating /etc/hosts file - Adding hostname entries for local sites (all platforms)
- LaunchDaemon install/remove - Installing persistent loopback IP daemon (macOS only, one-time)
- IP alias binding - Creating loopback IP aliases if daemon install fails (macOS only, fallback)
To set up passwordless sudo:
-
Find the wpstaging binary path:
which wpstaging # Or if installed manually: /path/to/wpstaging -
Create a sudo configuration file:
sudo visudo -f /etc/sudoers.d/wpstaging
-
Add the following lines (replace
usernameand/path/to/wpstaging):# Hosts file update (all platforms) username ALL=(ALL) NOPASSWD: /path/to/wpstaging update-hosts-file* # LaunchDaemon and loopback IP alias (macOS only - skip these lines on Linux) username ALL=(ALL) NOPASSWD: /bin/cp /tmp/com.wp-staging.cli-loopback-*.plist /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /usr/sbin/chown root\:wheel /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/chmod 644 /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/launchctl bootstrap system /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/launchctl bootout system/com.wp-staging.cli-loopback username ALL=(ALL) NOPASSWD: /bin/launchctl load -w /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/launchctl unload /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/rm -f /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist username ALL=(ALL) NOPASSWD: /bin/bash -c *ifconfig lo0* username ALL=(ALL) NOPASSWD: /sbin/ifconfig lo0 alias 127.3.2.* netmask 255.255.255.255For example:
bob ALL=(ALL) NOPASSWD: /usr/local/bin/wpstaging update-hosts-file* bob ALL=(ALL) NOPASSWD: /bin/cp /tmp/com.wp-staging.cli-loopback-*.plist /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /usr/sbin/chown root\:wheel /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/chmod 644 /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/launchctl bootstrap system /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/launchctl bootout system/com.wp-staging.cli-loopback bob ALL=(ALL) NOPASSWD: /bin/launchctl load -w /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/launchctl unload /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/rm -f /Library/LaunchDaemons/com.wp-staging.cli-loopback.plist bob ALL=(ALL) NOPASSWD: /bin/bash -c *ifconfig lo0* bob ALL=(ALL) NOPASSWD: /sbin/ifconfig lo0 alias 127.3.2.* netmask 255.255.255.255 -
Save and exit the editor
Alternative: If you cannot set up passwordless sudo or prefer not to, you can use the --skip-update-hosts-file flag:
wpstaging add https://mysite.local --skip-update-hosts-fileNote: When using --skip-update-hosts-file, you will need to manually add entries to your /etc/hosts file for local development.
Q88: I get "Error response from daemon: could not find an available, non-overlapping IPv4 address pool" when creating sites. How do I fix this?
A88:
This error occurs when Docker runs out of available IP address pools for creating new networks. This typically happens when you have many Docker networks created (e.g., from running multiple tests or creating many sites).
Solution 1: Clean up unused Docker networks (Recommended)
# Remove all unused networks
docker network prune -f
# Or remove specific test networks
docker network ls --filter "name=wpstg-" --format "{{.Name}}" | xargs docker network rmSolution 2: Stop and remove containers first, then clean up networks
# Stop all test site containers
docker ps -a --filter "name=wpstg-" --format "{{.Names}}" | xargs docker rm -f
# Then prune networks
docker network prune -fSolution 3: Restart Docker daemon (if the above doesn't work)
# On Linux with systemd
sudo systemctl restart docker
# On macOS/Windows
# Restart Docker Desktop from the application menuPrevention: After running tests or deleting sites, run docker network prune -f to clean up unused networks and prevent this issue from occurring.
Note: The test suite automatically runs docker network prune -f in the teardown function to prevent this issue during testing.
Q89: I get "docker is running in Windows container mode" error on Windows. How do I fix this?
A89:
WP Staging CLI requires Docker to run in Linux container mode because all the container images (PHP, Nginx, MariaDB, Mailpit) are Linux-based.
Solution: Switch to Linux containers
Option 1: Automatic switch (recommended)
When WP Staging CLI detects Windows container mode, it will prompt you:
Docker requirement check failed:
──────────────────────────────────────────────────────────────────────────
`docker` is running in Windows container mode
WP Staging CLI requires Linux containers to work properly.
The next action will switch to Linux containers automatically.
Continue? [y/N]: y
Switching to Linux containers...
Successfully switched to Linux containers.
Please run your command again.
──────────────────────────────────────────────────────────────────────────
Simply press y to let the CLI switch Docker to Linux containers automatically.
Option 2: Using Docker Desktop UI
- Right-click the Docker Desktop icon in the system tray (bottom-right corner)
- Select "Switch to Linux containers..."
- Wait for Docker to restart
- Run your wpstaging command again
Option 3: Using command line manually
# PowerShell
& "C:\Program Files\Docker\Docker\DockerCli.exe" -SwitchLinuxEngine# CMD
"C:\Program Files\Docker\Docker\DockerCli.exe" -SwitchLinuxEngineTo verify current mode:
docker version --format "{{.Server.Os}}"- Output
linux= Correct mode ✅ - Output
windows= Wrong mode, switch to Linux containers
Note: Docker Desktop defaults to Linux container mode. If you previously switched to Windows containers for .NET or Windows-based development, you'll need to switch back for WP Staging CLI.
Q89b: I get "Host 'x.x.x.x' is not allowed to connect to this MariaDB server" error. What's wrong?
A89b:
This error occurs on Windows and macOS when the CLI tries to connect to MariaDB during database setup. The error looks like:
Failed to set up database: mysite_local: failed to connect to database after 10 attempts:
Error 1130: Host '172.25.0.1' is not allowed to connect to this MariaDB server
Cause:
Docker Desktop routes host-to-container connections through the bridge network. The connection appears to come from the bridge gateway IP (e.g., 172.25.0.1 or 172.18.0.1) instead of localhost. MariaDB by default only allows root connections from localhost.
Solution: This issue was fixed by:
- Using MariaDB 11.8 image (
mariadb:11.8) instead oflatest - Adding
MARIADB_ROOT_HOST=%environment variable to allow root connections from any host
If you encounter this error with an older version:
- Update to the latest version of WP Staging CLI
- Delete the affected site and recreate it:
wpstaging del mysite.local wpstaging add mysite.local
The new site will use MariaDB 11.8 with the correct configuration.
Q89c: I get "Access denied for user 'root'@'localhost'" on Docker Desktop. What's wrong?
A89c:
This error can occur on Docker Desktop (Windows, macOS, or Linux with Docker Desktop) when MariaDB fails to set the root password from the MARIADB_ROOT_PASSWORD environment variable during first initialization.
Cause: Docker Desktop has a known issue where MariaDB may not properly initialize the root password from environment variables if the data directory is not completely empty at startup.
How WP Staging CLI handles this: The CLI automatically detects Docker Desktop and applies two fixes:
- Clears MariaDB data directory before first WordPress installation to ensure fresh initialization
- Uses init SQL script in
/docker-entrypoint-initdb.d/as a backup mechanism to set the root password
If you still encounter this error:
- Delete the affected site:
wpstaging del mysite.local
- Recreate the site:
wpstaging add mysite.local
The CLI will automatically clear the MariaDB data directory and set up the password correctly.
Manual fix (if needed): Delete the MariaDB data directory manually before creating the site:
rm -rf ~/wpstaging/sites/<hostname>/data/mariadb/*
wpstaging add <hostname>
Q89d: Database connection fails after running remove then add. What's wrong?
A89d:
This can happen when MariaDB hasn't fully initialized before the CLI attempts to connect.
Cause:
After remove, all container data is deleted. When add runs, MariaDB starts fresh and needs time to initialize (especially on slower systems or Docker Desktop).
How WP Staging CLI handles this: The CLI uses two mechanisms to ensure database readiness:
- MariaDB healthcheck - The container reports healthy only when MariaDB accepts connections and InnoDB is initialized
- Docker Compose --wait flag - CLI waits for the healthcheck to pass before proceeding
If you still encounter this error: The issue is usually transient. Simply retry:
wpstaging del mysite.local
wpstaging add mysite.localTechnical details: The MariaDB service includes a healthcheck configuration with platform-specific timings:
Docker Engine:
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 30s
interval: 10s
timeout: 5s
retries: 3Docker Desktop uses longer timings because bind-mounted volumes are slower:
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 90s
interval: 10s
timeout: 5s
retries: 5This ensures the database is fully ready before WordPress installation begins.
Q89e: MariaDB fails with "InnoDB: Cannot open './ibdata1'" on macOS. What's wrong?
A89e:
This error occurs on Docker Desktop for Mac (especially macOS 26+) when InnoDB's native I/O operations are incompatible with the virtualized filesystem.
Error message:
InnoDB: Operating system error number 20 in a file operation.
InnoDB: Error number 20 means 'Not a directory'
InnoDB: Cannot open './ibdata1'.
InnoDB: Database creation was aborted with error Generic error.
Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
Cause: Docker Desktop for Mac uses VirtioFS or gRPC-FUSE for filesystem virtualization. InnoDB's Native Asynchronous I/O and default flush methods don't work correctly with this virtualized filesystem layer, particularly on newer macOS versions (26+) and Docker Desktop versions.
How WP Staging CLI handles this: The CLI includes MariaDB configuration that disables problematic I/O features:
innodb_use_native_aio = 0
innodb_flush_method = fsyncThese settings are safe for all platforms with minimal performance impact for development environments.
If you encounter this error:
- Update to the latest version of WP Staging CLI
- Reset the site (optionally restore from backup):
wpstaging reset mysite.local # Or reset and restore in one step: wpstaging reset mysite.local --from=backup.wpstg
Manual fix for existing sites: Update the MariaDB config file:
# Edit the config file
echo "innodb_use_native_aio = 0" >> ~/wpstaging/sites/<hostname>/docker/mariadb/config/mysqld.cnf
echo "innodb_flush_method = fsync" >> ~/wpstaging/sites/<hostname>/docker/mariadb/config/mysqld.cnf
# Reset the site
wpstaging reset <hostname>
Q90: Browser shows "Your connection is not private" or certificate not trusted. How do I fix this?
A90:
This happens when the mkcert Certificate Authority (CA) is not installed in your system trust store. This can occur if you declined the CA installation prompt during site setup.
Symptoms:
- Browser shows "Your connection is not private"
- ERR_CERT_AUTHORITY_INVALID error
- Certificate appears invalid
Solution 1: Use reinstall-ca (recommended)
# Rotate the certificate authority and re-sign every site in one pass.
# This is the right command whenever browsers stop trusting your sites.
wpstaging reinstall-ca
# Equivalent alias on reinstall-cert (same effect, with or without a hostname):
wpstaging reinstall-cert --reinstall-ca
wpstaging reinstall-cert <hostname> --reinstall-ca
# Running sites are restarted automatically after the rotation.Solution 2: Delete CA and re-add site
# Delete CA to trigger re-prompt
rm -rf ~/wpstaging/stack/mkcert/ca/
# Add a new site and accept the CA installation prompt
wpstaging add newsite.localSolution 3: Manually install existing CA
Linux (Chrome/Chromium):
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "mkcert CA" \
-i ~/wpstaging/stack/mkcert/ca/rootCA.pem
# Restart Chrome
killall chromeLinux (Firefox):
PROFILE=$(find ~/.mozilla/firefox -name "*.default*" | head -1)
certutil -d sql:$PROFILE -A -t "C,," -n "mkcert CA" \
-i ~/wpstaging/stack/mkcert/ca/rootCA.pemmacOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
~/wpstaging/stack/mkcert/ca/rootCA.pemWindows (PowerShell as Administrator):
certutil -addstore -f "ROOT" $env:USERPROFILE\wpstaging\stack\mkcert\ca\rootCA.pem
Q91: How do I check if the mkcert CA is installed correctly?
A91:
Use these commands to verify CA installation:
Linux (Chrome NSS database):
certutil -d sql:$HOME/.pki/nssdb -L | grep mkcertmacOS:
security find-certificate -c "mkcert" -aWindows:
certutil -store -user root | findstr mkcertVerify certificate is signed by CA:
openssl verify -CAfile ~/wpstaging/stack/mkcert/ca/rootCA.pem \
~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.crt
# Should output: <hostname>.crt: OK
Q92: I get "mkcert binary not found" error. How do I fix it?
A92:
This happens if the mkcert binary wasn't downloaded or was deleted.
Solution 1: Add a new site (auto-downloads mkcert)
wpstaging add site.local
# Will copy from system or download from GitHub automaticallySolution 2: Install mkcert system-wide first
Linux (Homebrew):
brew install mkcertLinux (manual):
curl -LO https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
sudo mv mkcert-v1.4.4-linux-amd64 /usr/local/bin/mkcert
sudo chmod +x /usr/local/bin/mkcertmacOS:
brew install mkcertWindows (Chocolatey):
choco install mkcertAfter installing system-wide, WP Staging CLI will copy it automatically when you add a new site.
Q93: I get NET::ERR_CERT_DATE_INVALID or certificate expired error. How do I fix it?
A93:
This is usually caused by incorrect system clock or corrupted certificate files.
Check system time:
dateRegenerate certificates:
# Delete certificate files
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.crt
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.key
# Restart to regenerate
wpstaging restart <hostname>Note: Mkcert certificates are valid for 10 years, so expiration is rare unless system clock is wrong.
Q94: How do I regenerate SSL certificates for a site?
A94:
Use the reinstall-cert command (requires --show-all flag to see in help):
# Regenerate certificate for a single site
wpstaging reinstall-cert <hostname>
# Or regenerate certificates for every site in one command
wpstaging reinstall-cert
# Restart to apply changes
wpstaging restart <hostname>To also rotate the certificate authority (wipe the CA, install a fresh one into the system trust store, and re-sign every site's leaf):
# Dedicated command for CA rotation
wpstaging reinstall-ca
# Equivalent alias on reinstall-cert (same effect with or without a hostname)
wpstaging reinstall-cert --reinstall-ca
wpstaging reinstall-cert <hostname> --reinstall-caThe previous CA is also removed from the system trust store automatically. If you have older orphan entries from before this change, see Q121.
Alternative manual method:
# Delete certificates manually
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.crt
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.key
# Restart to regenerate
wpstaging restart <hostname>
Q95: Certificate works for hostname but not for IP access. Why?
A95:
The certificate must include the container IP in its Subject Alternative Names (SAN).
Check certificate SANs:
openssl x509 -in ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.crt \
-noout -text | grep -A5 "Subject Alternative Name"
# Should show: IP Address:127.3.2.x (your container IP)If container IP is missing, regenerate:
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.crt
rm ~/wpstaging/sites/<hostname>/docker/nginx/certs/<hostname>.key
wpstaging restart <hostname>
Q96: I get "docker is not installed" error. How do I install Docker?
A96:
The CLI displays OS-specific installation instructions when Docker is not found. Here's a summary:
Linux:
# Quick install via official script
curl -fsSL https://get.docker.com | sh
# Add your user to docker group (to run without sudo)
sudo usermod -aG docker $USER
# Log out and back in for group change to take effectmacOS:
- Download Docker Desktop from: https://docs.docker.com/desktop/setup/install/mac-install/
- Or install via Homebrew:
brew install --cask docker
Windows:
- Download Docker Desktop from: https://docs.docker.com/desktop/setup/install/windows-install/
Q97: I get "docker version too old" or "docker compose version too old" error. How do I update?
A97:
WP Staging CLI requires:
- Docker >= 20.10.0
- Docker Compose >= 2.19.0
To update Docker:
-
Linux:
curl -fsSL https://get.docker.com | sh -
macOS:
- Open Docker Desktop and check for updates
- Or reinstall from: https://docs.docker.com/desktop/setup/install/mac-install/
-
Windows:
- Open Docker Desktop and check for updates
- Or reinstall from: https://docs.docker.com/desktop/setup/install/windows-install/
To update Docker Compose:
-
Linux:
sudo apt-get update && sudo apt-get install docker-compose-plugin # Debian/Ubuntu sudo dnf install docker-compose-plugin # Fedora/RHEL
-
macOS/Windows:
- Update Docker Desktop to get the latest Docker Compose
Verify versions:
docker version
docker compose version
Q98: How do I restore a remote backup to a dockerized site?
A98:
The simplest way is to use add --from which creates a new site and restores from a backup in one step. For existing sites, database credentials are auto-detected from the site's .env file.
Single command (recommended for new sites):
# From local backup file
wpstaging add mysite.local --from=backup.wpstg
# From remote URL
wpstaging add mysite.local --from=https://example.com/backup.wpstgThis creates the site, installs WordPress, and then restores from the backup file using the database credentials from the site's .env file automatically.
After restoration, you'll see helpful information:
Login credentials:
* Site : https://mysite.local
* User : Login with same username as in https://original-site.com
* Password : Login with same password as in https://original-site.com
Paths:
* Site data : ~/wpstaging/sites/mysite.local/www
* Docker Compose : ~/wpstaging/sites/mysite.local/docker-compose.yml
* Env File : ~/wpstaging/sites/mysite.local/.env
Reset existing site with backup (recommended for existing sites):
If the site already exists and you want to reset it with a backup:
# From local backup file
wpstaging reset mysite.local --from=backup.wpstg
# From remote URL
wpstaging reset mysite.local --from=https://example.com/backup.wpstg
# Reset with a specific WordPress version
wpstaging reset mysite.local --wp=6.5
# Combine --wp and --from
wpstaging reset mysite.local --wp=6.5 --from=backup.wpstgThis reinstalls WordPress and restores from the backup, using the existing site's database credentials from .env. The --wp flag updates the WP_VERSION in the .env file with the actual installed version.
Tip: To change the WordPress version without reinstalling, use switch-wp instead:
wpstaging switch-wp mysite.local 6.5This replaces only the WordPress core files while preserving your database, themes, plugins, and uploads.
Alternative: Manual restore to existing site
If the site already exists and you want to restore manually without reset:
# Make sure site is running
wpstaging start mysite.local
# Restore the backup (simple hostname format)
wpstaging restore mysite.local backup.wpstg
# Or with remote URL
wpstaging restore mysite.local --from=https://example.com/backup.wpstgThe CLI automatically detects the site path and reads database credentials from the .env file. You'll see:
Restoring to dockerized site: mysite.local
Using database credentials from dockerized site: mysite.local
Alternative: Using --path flag (legacy format)
You can also specify the full path explicitly:
wpstaging restore --path=$HOME/wpstaging/sites/mysite.local/www backup.wpstg
wpstaging restore --path=$HOME/wpstaging/sites/mysite.local/www --from=https://example.com/backup.wpstgComplete workflow in one script:
# Set your site hostname and backup URL
SITE="mysite.local"
BACKUP_URL="https://mysite.com/wp-content/uploads/wp-staging/backups/backup.wpstg"
# Create site (skip if exists)
wpstaging add $SITE
# Make sure site is running (database must be accessible)
wpstaging start $SITE
# Restore - credentials auto-detected from .env
wpstaging restore $SITE --from="$BACKUP_URL"Note: CLI flags always take priority over auto-detected values. If you need to override a specific credential, just pass the flag:
wpstaging restore mysite.local --db-prefix=custom_ backup.wpstgFor dockerized sites, if you specify --site-url, the hostname must match the SITE_URL in the site's .env file. The CLI will show an error if there's a mismatch:
error: Site URL hostname 'different.local' does not match the dockerize site URL hostname 'mysite.local' from .env file.
When restoring to a dockerize site, the --site-url hostname must match the site's configured URL.
Either use --site-url=https://mysite.local or remove the --site-url flag to auto-detect.
Database credentials are stored in ~/wpstaging/sites/<hostname>/.env (auto-generated by wpstaging add).
Q99: What happens if an external service is using the same IP range as wpstaging?
A99:
The CLI uses the IP range 127.3.2.1 - 127.3.2.254 for Docker containers. If an external service (like Apache, nginx, or MySQL) is using an IP in this range:
For new sites (wpstaging add):
The CLI automatically detects the conflict and switches to the next available IP:
Container IP 127.3.2.1 is in use by external service, switching to 127.3.2.2
For existing sites (wpstaging start or wpstaging restart):
The CLI cannot auto-switch (the IP is already configured in .env). Instead, it shows an error with details:
Cannot start site. External services are using IP 127.3.2.1:
- HTTP (port 80): external service
- HTTPS (port 443): external service
Please stop these services or delete the site and recreate it to assign a new IP.
Solutions:
- Stop the conflicting external service
- Delete and recreate the site:
wpstaging del mysite.local && wpstaging add mysite.local - Use a specific IP:
wpstaging add mysite.local --container-ip=127.3.2.50
Q100: Why does my site fail to start on macOS with "can't assign requested address"?
A100:
On macOS, loopback IP addresses other than 127.0.0.1 require explicit aliases. If you see an error like:
bind: can't assign requested address
The CLI now provides a clear message:
Container IP address 127.3.2.1 is not configured on macOS.
macOS requires explicit loopback IP aliases. Please run:
sudo ifconfig lo0 alias 127.3.2.1 netmask 255.255.255.255
Or use --skip-macos-auto-ip flag to use port-based separation instead.
Solutions:
- Run
wpstaging start <site>— the CLI automatically creates the missing IP alias - Run
wpstaging add <site>again — the CLI installs a LaunchDaemon for persistent aliases - Run the suggested
sudo ifconfigcommand for a quick manual fix - Use
--skip-macos-auto-ipflag when adding sites (uses port-based separation instead of IP-based)
Note: The CLI installs a LaunchDaemon that creates loopback aliases for existing sites at boot. Aliases persist across reboots automatically. The start and restart commands also auto-create missing aliases.
Q101: What happens when I start or restart all sites and some have conflicts?
A101:
When running wpstaging start or wpstaging restart without specifying a hostname (to affect all sites), the CLI:
- Pre-scans all sites for external service conflicts
- Skips sites with conflicts and continues starting others
- Shows a summary at the end with diagnostic commands
Example output:
All containers started successfully
--------------------------------------------------------------------------------
The following sites could not start due to conflict with external services:
- site1.local: HTTP (port 80) used by external service
Please stop these services or delete the affected sites and recreate them
to assign new IPs.
To check which service is using a port:
sudo lsof -i :80
or: sudo ss -tlnp | grep 80
--------------------------------------------------------------------------------
This ensures that one conflicting site doesn't prevent other sites from starting.
Q101b: What happens if an external service is bound to all interfaces (wildcard binding)?
A101b:
When an external service (like nginx or Apache) binds to 0.0.0.0:80 or *:443 (wildcard binding), it listens on ALL IP addresses. This means Docker cannot bind to any specific IP on that port, even in the 127.3.2.x range.
How the CLI detects wildcard bindings:
- Tests if the port responds on both
127.0.0.1and another IP (like127.3.2.1) - If both respond, it's a wildcard binding
Behavior when wildcard is detected:
- Port rotation is triggered (not IP rotation, since IP rotation won't help)
- Enhanced message indicates the wildcard binding:
HTTP port 80 is in use by external service bound to all interfaces (*:80).
Switching to port 8844.
Common causes:
- System nginx configured with
listen 80;(defaults to all interfaces) - Apache with
Listen 80in httpd.conf - Other web servers or proxies
Solutions:
- Stop the conflicting service:
sudo systemctl stop nginx - Reconfigure the service to bind to a specific IP:
listen 127.0.0.1:80; - Let the CLI use alternate ports (automatic)
Note: On macOS with --skip-macos-auto-ip, external service checks for the IP range are skipped since port-based separation is used instead.
Q101c: A non-wpstg Docker container (e.g., standalone MySQL) is using a port. Will the CLI detect this?
A101c:
Yes. The CLI detects port conflicts from any running Docker container, not just wpstg containers. This is important because Docker's internal port allocator may use iptables-based forwarding that doesn't create host-level sockets, so standard OS-level port checks (TCP connect and bind) can miss these conflicts.
How it works:
- The CLI queries all running Docker containers via
docker psto get their published port mappings - It checks Docker's own conflict rules:
0.0.0.0:PORT(wildcard) conflicts with any IP on that port:::PORT(IPv6 wildcard) conflicts with any IP on that port- Exact
IP:PORTmatch conflicts
- Containers managed by wpstg (
wpstg-*) are excluded (handled separately)
Example scenario:
A standalone MySQL container is running with docker run -p 3306:3306 mysql. When you create a wpstg site:
wpstaging add mysite.localThe CLI detects the Docker port conflict and auto-switches:
MariaDB port 3306 on IP 127.3.2.1 conflicts with Docker container 'mysql'.
Automatically switching to port 3344.
What ports are checked: All 4 port types are covered: HTTP, HTTPS, MariaDB, and Mailpit.
Fallback behavior: The same fallback mechanism as Q43 applies — the CLI tries predefined fallback ports first, then random ports in the 49152-65535 range.
Why OS-level checks are not enough:
When Docker routes traffic using iptables (e.g., a container bound to 0.0.0.0:3306), no host-level socket may exist on the specific loopback IP (like 127.3.2.1). A TCP connect test to 127.3.2.1:3306 returns "connection refused" (port appears free), but Docker will reject binding 127.3.2.1:3306 because 0.0.0.0:3306 already claims all IPs. The Docker-level check catches this.
Q102: What environment variables does wpstaging CLI support?
A102:
The CLI supports several environment variables for configuration:
| Variable | Purpose | Example |
|---|---|---|
WPSTGPRO_LICENSE |
Provide license key | export WPSTGPRO_LICENSE=abc123... |
WPSTGCLI_JSON_OUTPUT |
Enable structured JSON output | export WPSTGCLI_JSON_OUTPUT=1 |
WPSTGCLI_JSON_PAGE |
Page number for paginated JSON output | export WPSTGCLI_JSON_PAGE=2 |
WPSTGCLI_JSON_PAGE_SIZE |
Items per page for paginated JSON output | export WPSTGCLI_JSON_PAGE_SIZE=50 |
WPSTGCLI_DEBUG |
Enable debug output | export WPSTGCLI_DEBUG=1 |
WPSTGCLI_VERBOSE |
Show detailed per-file output | export WPSTGCLI_VERBOSE=1 |
WPSTGCLI_QUIET |
Suppress informational output | export WPSTGCLI_QUIET=1 |
WPSTGCLI_ALLOW_ROOT |
Allow running as root user | export WPSTGCLI_ALLOW_ROOT=1 |
Boolean environment variables accept truthy values: 1, true, yes, on (case-insensitive).
Q103: How do I enable debug output?
A103:
Use either the --debug flag or the environment variable:
# Using flag
wpstaging extract --debug backup.wpstg
# Using environment variable
export WPSTGCLI_DEBUG=1
wpstaging extract backup.wpstgDebug output shows detailed execution traces to stderr prefixed with [DEBUG].
Q104: How do I suppress all informational output?
A104:
Use the --quiet flag or environment variable for silent operation:
# Using flag
wpstaging extract --quiet backup.wpstg
# Using environment variable
export WPSTGCLI_QUIET=1
wpstaging extract backup.wpstgThis is useful for scripting and CI/CD pipelines.
Q105: How do I run wpstaging as root user?
A105:
By default, running as root is blocked for security. To allow it:
# Using flag
sudo wpstaging extract --allow-root backup.wpstg
# Using environment variable
export WPSTGCLI_ALLOW_ROOT=1
sudo wpstaging extract backup.wpstgImportant: Don't use this on your regular system. Only use it when you're already in a sandboxed/isolated environment where running as root is expected and safe (e.g., Docker containers, CI/CD pipelines, virtual machines).
Q106: Can I extract or restore a backup directly from a URL?
A106:
Yes, you can extract or restore backups directly from HTTP/HTTPS URLs without downloading them manually first.
Using --from flag:
# Extract from remote URL
wpstaging extract --from=https://example.com/backups/backup.wpstg
# Restore from remote URL
wpstaging restore --path=/var/www/html --from=https://example.com/backups/backup.wpstgOr pass URL directly as argument:
wpstaging extract https://example.com/backups/backup.wpstgWhat happens:
- The CLI validates the URL (must end with
.wpstg) - Performs a preflight check (file size and backup format validation)
- Displays backup information (filename, size, format type)
- Asks for confirmation before downloading
- Downloads with progress indicator (supports resume for interrupted downloads)
- Caches downloaded files for reuse
Supported backup formats:
- WP Staging Backup v1
- WP Staging Backup v2
- WP Staging SQL Dump
Q107: Can one dockerized site send HTTPS requests to another site?
A107:
Yes. All sites share a Docker network called wpstg-site-shared-network. Each site's Nginx registers its hostname as a DNS alias on this network. When site1's PHP sends a request to https://site2.local, Docker DNS resolves the hostname to site2's Nginx container.
This works for:
- Cross-site requests (site1 to site2)
- Self-referral (site1 to itself)
- WordPress features like wp-cron, health checks, and WP Staging Remote Sync
The shared network is created automatically when you run wpstaging start. No extra setup is needed.
Q108: Why does curl inside the container fail with "unable to get local issuer certificate"?
A108:
This happens when the container does not trust the mkcert CA certificate. The fix is to regenerate the site's config files:
wpstaging generate-docker-file <hostname> # single site
wpstaging generate-docker-file # all sites
wpstaging start <hostname>This creates a combined CA bundle that includes both system CAs (for public websites) and the mkcert CA (for local sites). The bundle is mounted at /etc/ssl/certs/ca-certificates.crt inside the container, so both PHP and shell curl trust it.
If the issue still happens on a fresh site, make sure you are running the latest binary.
Q109: Does the shell prompt show the site hostname?
A109:
Yes. When you open a shell with wpstaging shell <hostname>, the prompt shows the site hostname:
[www-data@example.local ~]$
For root shell (wpstaging shell <hostname> root):
[root@example.local ~]#
Q110: What is the wpstg-site-shared-network network?
A110:
It is a Docker bridge network that exists only inside Docker. Containers use it to talk to each other. The host machine (your computer) cannot access containers through this network.
| From | To | How |
|---|---|---|
| Host machine (browser) | Container | Loopback IP port binding (127.3.2.x) |
| Container | Container | Shared network Docker DNS (wpstg-site-shared-network) |
The network is created when you run wpstaging start. It is removed when you run wpstaging stop (without a hostname).
Q111: How do I set up the Docker development environment on macOS?
A111:
macOS with Docker Desktop requires extra setup because containers cannot reach the Docker gateway IP (172.201.0.1) directly, unlike Linux where the gateway forwards traffic by port.
Initial setup:
# 1. Copy the macOS environment overrides
cp .env.local.example .env.local
# Edit HOST_UID/HOST_GID if needed (defaults: 501/20 for first macOS user)
# 2. Build and start (Makefile handles macOS automatically)
make build
make docker-up # Adds loopback alias and uses macOS overlay
make docker-site-setupWhat the Makefile does automatically on macOS:
- Adds a loopback alias (
sudo ifconfig lo0 alias 172.201.0.1) for host-side port binding - Uses
docker-compose.macos.ymloverlay alongsidedocker-compose.yml - Builds the correct binary architecture (ARM64 or AMD64) for integration tests
How the macOS overlay works:
- Removes
extra_hostsfrom the PHP container (gateway IP is unreachable) - Adds nginx network aliases so container hostnames resolve directly to nginx
- Loads a custom
nginx-macos.confwith a TCP stream proxy that forwards MySQL traffic (port 3306) from nginx to the database container
Running tests:
make tests-backup # Backup/extract tests
make tests-docker-integration # Docker integration tests (external DB tests auto-skip on macOS)Note: The loopback alias does not persist after reboot. Run make docker-up again after each restart, or manually run sudo ifconfig lo0 alias 172.201.0.1.
Q112: I use OrbStack instead of Docker Desktop. Is it supported?
A112:
No. OrbStack is not supported. OrbStack's networking does not support binding to loopback IP aliases (127.3.2.x) that the CLI uses. Containers start and appear healthy, but sites are not reachable in the browser.
The CLI auto-detects OrbStack's Docker context and switches to Docker Desktop (desktop-linux) or Docker Engine (default). You will see a notice:
OrbStack Docker context detected. OrbStack is not supported at the moment.
Switched Docker context to "desktop-linux".
If no alternative context is found, the CLI shows an error. Switch manually:
# List available contexts
docker context ls
# Switch to Docker Desktop
docker context use desktop-linux
# Or switch to Docker Engine
docker context use defaultNote: The OrbStack Docker context stays active even after you quit OrbStack. The Docker CLI silently uses OrbStack's socket until you switch contexts.
Q113: I get "is a directory" error when running wpstaging add or wpstaging reset. How do I fix it?
A113:
This happens when Docker creates bind mount source paths as directories instead of files. Older Docker Compose versions may ignore the create_host_path: false setting and create directories at paths where config files are expected (e.g., php.ini, ext-redis.ini).
Since v1.6.3, the CLI automatically detects and removes these stale directories before generating config files. You should see a message like:
Found directories where config files are expected:
/path/to/docker/php/config/php.ini
/path/to/docker/php/config/ext-redis.ini
Removing and recreating as files...
If you're on an older version, run:
wpstaging remove example.local
wpstaging add example.localThis clears the site directory and recreates it cleanly.
Q114: How do I get JSON output from the CLI?
A114:
Use the --json global flag or set the WPSTGCLI_JSON_OUTPUT=1 environment variable. All output switches to structured JSON on stdout. Each JSON response is pretty-printed with 2-space indentation and may span multiple lines. Consumers should parse complete JSON objects rather than assume one object per line.
Examples:
# List all sites as JSON
wpstaging list --json
# Paginated JSON output (default: 100 items per page)
wpstaging list --json --page=1 --page-size=50
# Container status as JSON
wpstaging status --json
# Backup file index as JSON (raw lines)
wpstaging dump-index backup.wpstg --json
# Backup file index as structured entries
wpstaging dump-index --data backup.wpstg --json
# Using environment variable
WPSTGCLI_JSON_OUTPUT=1 wpstaging listEach JSON object has a command field: message (progress), prompt (waiting for input), list, status, dump_header (backup header), dump_metadata (backup metadata), dump_index (backup file index), wp_installed (installation summary), site_delete_confirm (deletion list), port_conflict (port conflict errors), restore_confirm (restore confirmation), remote_backup_info (remote backup details), or license (license operations).
Commands that return lists (list, status, dump-index) support --page and --page-size flags. Default is 100 items per page. Set --page-size=0 to return all items.
Errors are written to stderr with "success": false. See GUI Integration Guide for the full JSON protocol documentation.
When a confirmation prompt appears, the CLI outputs a prompt object and waits for y or n on stdin:
{
"success": true,
"command": "prompt",
"data": {
"message": "Continue?",
"type": "confirm",
"accept": ["y", "n"]
}
}When the CLI needs sudo, it outputs a sudo prompt and waits for the password on stdin (followed by a newline):
{
"success": true,
"command": "prompt",
"data": {
"message": "Sudo password is required to update /etc/hosts for local domain resolution.",
"type": "sudo"
}
}The GUI wrapper shows a password dialog with the message text and writes the password to stdin. If sudo credentials are already cached, the prompt is skipped.
Q115: How do I change the PHP version for an existing site?
A115:
Use the switch-php command to change the PHP version for an existing dockerized site:
wpstaging switch-php mysite.local 8.4
wpstaging switch-php mysite.local 8.1Supported PHP versions: 7.4, 8.1, 8.2, 8.3, 8.4 (default: 8.1)
What happens:
- Validates the PHP version is supported
- Updates the
PHP_VERSIONsetting in the site's.envfile - Pulls the Docker image if not available locally
- Regenerates Docker Compose and PHP configuration files
- Restarts containers automatically if they are running
Notes:
- If containers are not running, configuration is updated but containers are not started
- All other site settings (database, ports, WordPress) remain unchanged
- The command requires exactly two arguments: hostname and PHP version
Q116: How do I change the WordPress version for an existing site?
A116:
Use the switch-wp command to change the WordPress version for an existing dockerized site:
wpstaging switch-wp mysite.local 6.5
wpstaging switch-wp mysite.local latest
wpstaging switch-wp mysite.local nightly
wpstaging switch-wp mysite.local 6.7-beta1Accepted version formats: X.Y, X.Y.Z, X.Y-beta1, X.Y-RC1, latest, nightly
What happens:
- Validates the WordPress version format
- Regenerates PHP files to ensure the switch script is available
- Executes the version switch inside the running container
- Downloads WordPress core files for the target version
- Updates the database schema if needed
- Stores the actual resolved version in the site's
.envfile
Notes:
- Containers must be running (the switch runs inside the container via
docker exec) - Database, themes, plugins, and uploads are preserved
- Symbolic versions like
latestare resolved to concrete versions (e.g.6.7.2) in.env
Q117: Why should I use VirtioFS on macOS?
A117: On macOS, Docker shares files between your Mac and the running containers whenever WordPress inside a container reads or writes a file. The default sharing backend is slow, so sites start up slowly and any workflow that touches many files inside the container takes longer than it should. VirtioFS is a faster backend that removes most of this overhead, so sites feel more responsive and file-heavy container work finishes sooner.
VirtioFS does not speed up host-side archive extraction itself, but it improves the parts of add, reset, and restore that run inside the container once the site is up.
To enable VirtioFS:
- Open Docker Desktop.
- Go to Settings → General → Virtual file sharing.
- Select VirtioFS.
- Click Apply & Restart.
The CLI shows a tip during add, reset, extract, and restore if VirtioFS is not the active file-sharing mechanism. It fires once per switch away: once you enable VirtioFS the tip goes silent, and if you later switch back to a different mechanism the tip is shown again on the next run. This applies to macOS only.
Q118: How do I access the database GUI for a dockerized site?
A118:
By default, dockerized sites ship with a bundled Adminer database UI. Open https://adminer.<site> in your browser. The /adminer/ subpath on the main site is not routed -- the dedicated subdomain is the only Adminer URL.
The login form is pre-filled with the database server, username, and database name. Enter the password shown when the site was created (also available in the site's .env file as DB_PASSWORD).
To disable Adminer, add --disable-adminer when creating, resetting, or regenerating a site:
wpstaging add https://mysite.local --disable-adminer
wpstaging reset mysite.local --disable-adminer
wpstaging generate-docker-file mysite.local --disable-adminer
wpstaging reconfigure mysite.local --disable-adminerreconfigure --disable-adminer turns Adminer off on a live site without losing data: it updates the site's configuration and relaunches it, while WordPress files and the database are preserved.
The DISABLE_ADMINER=true setting is persisted in the site's .env file so future regenerations keep Adminer off.
If you upgraded from a CLI version that did not include Adminer, start and restart create the missing Adminer files automatically the first time -- no manual step is required.
Q119: How do I roll an older site forward after a CLI upgrade?
A119:
Run:
wpstaging reconfigure <site>reconfigure updates the site's Docker setup (compose, nginx, PHP, SSL certificate) and relaunches the site. WordPress files and the database are preserved.
Use this to apply new defaults introduced by a CLI release (for example, to refresh the SSL certificate after the hostname list changed, or to pick up updated PHP-FPM or Nginx config after a CLI upgrade). For Adminer specifically, you do not need reconfigure -- start and restart regenerate the missing Adminer files automatically on sites created before Adminer support.
If you omit the hostname, all sites are reconfigured:
wpstaging reconfigure
Q120: What does the sweep-ca-trust command do?
A120:
sweep-ca-trust scans your system trust stores and removes stale WP Staging CLI certificate authority entries. Stale means the entry's fingerprint does not match the current active CA on disk. It checks the macOS keychain, Linux NSS database, Firefox profiles, and the Windows CurrentUser Root store. Other CAs (third-party, system, browser bundles) are not touched.
The command is hidden by default. Use --show-all to see it in help. It accepts two flags:
--dry-run-- shows what would be removed without changing anything.--include-legacy-- also removes legacymkcert development CAentries left by older builds. Asks for confirmation before running.
Run it when you want to clean up trust store bloat:
wpstaging sweep-ca-trust --dry-run # preview only
wpstaging sweep-ca-trust # remove stale WP Staging CLI entries
wpstaging sweep-ca-trust --include-legacy # also remove old mkcert-branded entriesThis command does not need Docker to be running.
Q121: Why does my system trust store have so many mkcert development CA entries?
A121:
Older WP Staging CLI builds left a trust store entry behind every time you ran reinstall-ca (or its reinstall-cert --reinstall-ca alias) or remove. Each cycle generated a new CA but never deleted the old one. Over time these entries pile up.
The entries do not break anything. They are harmless trust orphans.
To clean them up, run:
wpstaging sweep-ca-trust --include-legacyThe command lists how many entries it found and asks you to confirm before removing them. It keeps the current active CA intact.
Going forward, this problem stops growing. Newer builds brand the CA as WP Staging CLI development CA and clean up stale entries automatically every time you run reinstall-ca.
Q122: What happens when my local site's SSL certificate expires?
A122:
WP Staging CLI rotates local site SSL certificates automatically before they expire. Each leaf certificate is valid for 2 years and 3 months. When a certificate is within 30 days of its expiry date, the next start, restart, add, or reinstall-cert command for that site re-issues the certificate using the same CA. You do not need to take any action.
This means active sites never show a "certificate expired" warning in the browser. If you have a site you have not run for more than two years, the next site command rotates its certificate before serving traffic.
The CA itself is valid for 10 years and stays in your system trust store. Only the per-site leaf certificate rotates.
If you want to force a rotation early (for example, after changing the hostname), run:
wpstaging reinstall-cert <hostname>The site is restarted automatically when the certificate is re-issued. Pass --no-restart if you want to defer the restart and apply it yourself later.
Q123: How do I check if my SSL certificate is trusted across browsers?
A123:
Run wpstaging verify-cert. The command audits the certificate authority across every browser trust store and inspects each site's leaf certificate. It reports the trust state for every store and per site. The command does not modify anything.
The trust stores it checks differ by platform:
- Linux: the user NSS database at
~/.pki/nssdb(shared by Chrome, Edge, Brave, and other Chromium-family browsers) and each Firefox profile. - macOS: the System keychain (used by Safari, Chrome, Edge, and Brave).
- Windows: the CurrentUser Root store (used by Edge, Chrome, and Internet Explorer).
The Linux system trust store at /etc/ssl/certs is not part of this audit. That store backs CLI tools like curl and openssl, not browsers. To inspect it, use wpstaging sweep-ca-trust --dry-run.
Pass a hostname to scope the per-site section to one site:
wpstaging verify-cert mysite.localPass --live to open a TLS connection to each site and compare the served certificate against the on-disk file. This helps you spot a stopped site or a container serving an older certificate:
wpstaging verify-cert --livePass --json for a machine-readable report that scripts and integrations can parse.
The command exits with code 0 when everything is trusted and non-zero otherwise, so you can use it in shell scripts to detect when action is needed.
Q124: How do I skip the auto-restart after reinstall-cert or reinstall-ca?
A124:
Pass the --no-restart flag. Both commands accept it:
wpstaging reinstall-cert <hostname> --no-restart
wpstaging reinstall-ca --no-restartThe certificate files on disk are still regenerated as usual. The command prints a hint that tells you the exact wpstaging restart command to run later when you are ready.
Use --no-restart when you want to schedule the restart yourself. For example, when you do not want to interrupt a running test, or when you plan to apply other configuration changes before bringing the containers back up.
Q125: Why does wpstaging only ask for my sudo password once now?
A125:
On Linux and macOS, the first command that needs sudo prompts for your password as before. After that, wpstaging keeps the credentials warm with a small background helper. While the same terminal stays open, you will not be asked again, even if a later command also needs sudo (for example, to update /etc/hosts, install an SSL certificate, or set up a loopback IP).
The helper:
- Runs as a detached background process tied to your terminal.
- Refreshes the sudo timestamp every 4 minutes (sudo's own cache lasts 5 minutes by default).
- Stops automatically when you close the terminal.
- Is skipped on Windows (Windows uses UAC dialogs instead) and when you are already running as root.
To turn the helper off for a single command, set the environment variable before the command:
WPSTGCLI_DISABLE_SUDO_KEEPALIVE=1 wpstaging add example.localTo turn it off for the whole shell session:
export WPSTGCLI_DISABLE_SUDO_KEEPALIVE=1This is useful in CI scripts or other automated runs where you want sudo to behave the way it did before.
Q125a: I have Touch ID enabled for sudo on macOS. Will the helper trigger Touch ID prompts in the background?
A125a:
No. The background helper runs sudo -n -v in non-interactive mode. The macOS Touch ID PAM module (pam_tid.so) respects the non-interactive flag and does not show a dialog. You see Touch ID one time at the start of the terminal session, when sudo first asks for authentication. After that, the helper refreshes the cache silently in the background and Touch ID stays quiet for the rest of the session.
Q125b: I see a wpstaging sudo-keepalive process in ps. Should I be worried?
A125b:
No. That is the small background helper described in Q125. One copy runs per terminal session that has used wpstaging with a sudo command. It refreshes your sudo timestamp every 4 minutes so you do not get asked for the password again. It uses very little memory and does almost nothing between refreshes.
The full command line looks like:
wpstaging sudo-keepalive --pid-file <TMPDIR>/wpstaging-sudo-keepalive-<uid>-<sid>.pid --leader-sid <pid>
<uid>is your Linux or macOS user ID.<sid>and the--leader-sidvalue are the same number: the PID of your shell (the session leader of your terminal).<TMPDIR>is your system's temporary directory. On Linux it is usually/tmp. On macOS it is usually under/var/folders/...; runecho $TMPDIRto see your exact path.- The PID file is a small marker that stops a second helper from starting in the same terminal.
The helper stops on its own when:
-
You close the terminal, or your SSH session ends.
-
You remove its PID file. The helper notices within about 30 seconds and exits:
rm "${TMPDIR:-/tmp}"/wpstaging-sudo-keepalive-*.pid
-
You stop it directly:
pkill -f "wpstaging sudo-keepalive"
If you do not want the helper to start at all, see Q125 for the WPSTGCLI_DISABLE_SUDO_KEEPALIVE=1 environment variable.
Q125: How do I clear the WP-CLI download cache?
A125:
Run wpstaging clean wpcli. The command removes three directories shared across all your dockerized sites:
<env-path>/wpstaging/stack/wp-cli/cache/plugin/<env-path>/wpstaging/stack/wp-cli/cache/core/<env-path>/wpstaging/stack/wp-cli/wp-staging-pro/
By default <env-path> is ~/wpstaging. Pass --env-path to target a custom location.
The cache holds downloaded plugin ZIP files, WordPress core archives, and the WP Staging Pro plugin used during site setup. Deleting it forces the next add or reset to download fresh copies. The directories are recreated on demand, so this is safe to run at any time.
wpstaging clean all also clears these directories, on top of the general cache and the stored license key.
Q126: What happens if I press Ctrl-C while wpstaging add is running?
A126: The CLI catches the signal and runs a rollback before it exits:
- It stops and removes the per-site Docker containers.
- It deactivates the WP Staging Pro license for the site URL.
- It removes the half-created site directory only when the directory did not exist before this
addstarted. If you ranaddover an existing partial site, the directory is kept so you do not lose your earlier data.
You will see two lines on stdout that confirm the rollback ran. The first line is always the same; the second line depends on whether the site directory was kept:
Cancelled, rolling back partial site: <hostname>
Rollback complete: <hostname>
If add ran over a site directory that already existed, the second line is instead:
Rollback complete (kept existing site directory): <hostname>
The same handler works for SIGINT (Ctrl-C) and SIGTERM. SIGKILL cannot be caught by any program, so it bypasses the rollback.
You can safely re-run wpstaging add <hostname> afterwards. The CLI cleans up any leftover container from the prior cancelled run, so the next attempt starts clean.
Q127: How does wp-admin auto-login work, and how do I turn it off?
A127:
Sites you create with wpstaging add print a one-time magic-link URL on success. Open the URL in any browser and the site sets the auth cookie for the lowest-ID administrator and redirects you to the WordPress dashboard. You do not need to type a username or password.
The magic-link is single-use: clicking it consumes the token. Run wpstaging list <site> to see the current URL -- the command auto-refreshes the URL when the previous one has expired, so the printed link is always usable. Run wpstaging magic-link <site> to force-issue a new URL on demand. The regular /wp-login.php form is always available alongside the magic-link if you want to sign in as a different user.
The auto-login script lives outside the WordPress webroot, in the site's Docker configuration directory at <site>/docker/php/magiclink/. It is never copied into .wpstg backups and never pushed to production.
To turn the magic-link feature off, add --disable-magic-link when creating, resetting, or regenerating a site:
wpstaging add https://mysite.local --disable-magic-link
wpstaging reset mysite.local --disable-magic-link
wpstaging generate-docker-file mysite.local --disable-magic-link
wpstaging reconfigure mysite.local --disable-magic-linkreconfigure --disable-magic-link turns auto-login off on a live site without losing data. It updates the site's configuration and relaunches it, while WordPress files and the database are preserved.
The DISABLE_MAGIC_LINK=true setting is persisted in the site's .env file so future regenerations keep auto-login off. Pass --disable-magic-link=false to a later reconfigure to turn it back on.
To reach the regular WordPress login form, open /wp-login.php directly. The auto-login URL is single-use and only triggers on its own dedicated path, so the standard WordPress login form is always available alongside it.
Last Updated: 2026-05-13 04:40:36 UTC