Development

How to Clone a Production WordPress Site Into Lando Safely

Cloning a production WordPress site into Lando can create an accurate debugging environment, but it also copies risk. The database may contain personal data, live payment configuration, real email addresses, API credentials, scheduled jobs, and URLs that still point to production.

A safe clone is a controlled data-handling workflow: verify a recoverable source backup, import only into a disposable local target, replace serialized URLs with WP-CLI, remove or override secrets, disable outbound side effects, and make the whole reset repeatable.

Confirm authority and minimize the data

Only copy a site you are authorized to administer. Check company privacy, security, and data-retention policies before downloading production data to a workstation.

Ask whether the problem can be reproduced with generated fixtures or a reduced dataset. A complete customer database should not be the default debugging tool. If production data is necessary, decide:

  • Who may create and access the clone
  • Which fields must be anonymized or removed
  • Where encrypted exports may be stored
  • How long the clone may exist
  • How local backups and logs will be deleted
  • Whether regulated or payment data may leave production at all

Payment card details should already be tokenized by the payment provider, but tokens, customer identifiers, webhook secrets, and API keys still require protection.

Build the empty Lando target first

Make the local target unambiguous before handling any production export:

name: client-site-local
recipe: wordpress
config:
  webroot: public
  php: '8.3'
  database: mysql:8.0
services:
  appserver:
    overrides:
      environment:
        WP_ENVIRONMENT_TYPE: local
        DISABLE_WP_CRON: 'true'
lando start
lando info
lando wp core download --path=/app/public
lando wp config create \
  --path=/app/public \
  --dbname=wordpress \
  --dbuser=wordpress \
  --dbpass=wordpress \
  --dbhost=database
lando wp db create --path=/app/public
lando wp core install \
  --path=/app/public \
  --url='https://client-site-local.lndo.site' \
  --title='Disposable local clone' \
  --admin_user='local-admin' \
  --admin_password='replace-this-local-password' \
  --admin_email='local-admin@example.test' \
  --skip-email
lando wp core version --path=/app/public
lando wp option get home --path=/app/public

An empty public/ directory does not contain WordPress merely because the recipe started. The commands above create a disposable core install, local wp-config.php, database, and administrator before any verification command expects WordPress to exist. If the repository already supplies those pieces, use its documented bootstrap instead and prove the same core-version and local-home checks.

Choose PHP and database versions that reproduce the issue or match the planned target. Confirm current recipe options in the Lando WordPress documentation.

Use a unique Lando app name. Before any import command, run pwd, lando info, and lando wp option get home so you know exactly which application and database you are about to replace.

Establish the network boundary before importing data

Bootstrap core while the target contains no production data, then deny appserver egress at the container, host firewall, or an equivalent infrastructure layer before transferring or importing the source export. Permit only required local services such as the database and a local mail-capture service.

Verify the boundary from the appserver with controlled requests: a production domain, live payment endpoint, webhook receiver, and external SMTP destination must be unreachable, while the local database and mail capture remain reachable. Record the result. Keep this boundary active through import, URL replacement, sanitization, secret removal, and all later testing.

DISABLE_WP_CRON prevents normal WordPress cron spawning, but it does not block a manual request, queue worker, activation callback, or plugin making an HTTP call. --skip-plugins is not a network control either, and WordPress still loads must-use plugins when that flag is present. Treat plugin settings and filters as defense in depth behind the verified egress rule.

Create and verify the source backup

Use your hosting platform's supported export or a production-side WP-CLI command. Write the database dump outside the public webroot with restrictive permissions. Export wp-content files separately if the issue requires them.

Verification should include:

  • The command completed successfully.
  • The archive is nonempty and can be listed or decompressed.
  • A checksum was recorded before transfer.
  • The database dump begins with expected SQL and contains expected tables.
  • A separate, known-good production backup exists before any maintenance work.

Transfer over an encrypted channel. Do not place a production dump in Git, a public object bucket, a chat attachment, or an unencrypted shared folder.

Keep production configuration out of the clone

Do not copy wp-config.php blindly. The local application should use Lando's database connection and local-only secrets.

Define environment-specific safeguards in a local configuration file that is not deployed to production:

<?php

declare(strict_types=1);

define('WP_ENVIRONMENT_TYPE', 'local');
define('DISABLE_WP_CRON', true);
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('AUTOMATIC_UPDATER_DISABLED', true);

Use a mail-capture service or disable transport at the infrastructure layer. Application-level filters alone can miss a plugin that calls an external API directly.

Replace payment gateways with sandbox mode and different sandbox credentials. A production secret should not remain present but “unused.” Remove it from the database and local environment.

Import into the disposable database

Place the dump inside the Lando application directory because lando db-import operates on app-visible files. Confirm the target one more time, then import:

lando info
lando wp option get home --path=/app/public
lando wp db export /app/.lando/backups/before-production-import.sql --path=/app/public
lando db-import .lando/import/production.sql.gz

The Lando database import guide notes that the target database is wiped by default. That is appropriate for a disposable clone, but dangerous if the current app is not what you think it is.

The pre-import export is intentionally before the destructive db-import. It preserves the known disposable target for troubleshooting or rollback. Treat every backup created after production data enters the workflow with the same privacy controls as the imported data.

Replace URLs without corrupting serialized data

WordPress stores some values as serialized PHP. A raw SQL string replacement can change string lengths and corrupt those values. Use WP-CLI's serialization-aware search-replace command.

Preview the change first:

lando wp search-replace \
  'https://www.example.com' \
  'https://client-site-local.lndo.site' \
  --all-tables-with-prefix \
  --precise \
  --skip-columns=guid \
  --dry-run

Review the table and replacement counts, then run the same command without --dry-run.

Do not change post GUIDs during a normal site move. They are identifiers, not display URLs. If the production site uses several domains, HTTP variants, a CDN, or a historical staging host, handle each verified source separately.

After replacement, set canonical local values explicitly:

lando wp option update home 'https://client-site-local.lndo.site'
lando wp option update siteurl 'https://client-site-local.lndo.site'
lando wp cache flush
lando wp rewrite flush

Sanitize personal data

Use an approved, reviewed sanitization command tailored to the schema. WordPress core user fields are only part of the picture. Membership, ecommerce, form, analytics, CRM, support, and logging plugins may store personal data in custom tables or serialized option values.

At minimum, review:

  • Names, email addresses, phone numbers, and postal addresses
  • Usernames and password-reset keys
  • IP addresses and user-agent histories
  • Form submissions and support messages
  • Order and subscription metadata
  • Download, login, and access logs
  • API tokens, application passwords, and webhook secrets
  • SMTP, payment, analytics, and CRM configuration

Use deterministic fake values when relationships must remain testable. Preserve IDs and foreign keys while changing the personal content. Confirm unique constraints, such as unique email addresses, still hold after anonymization.

Do not publish a generic SQL anonymizer without inspecting plugin schemas. A query that works on one site can corrupt another or miss sensitive custom tables.

Verify every outbound side effect remains disabled

A local clone must not behave like production. Keep the infrastructure egress boundary in place and verify these application controls independently:

  • Email is captured locally or blocked at the network boundary.
  • Payment providers use sandbox endpoints and sandbox credentials.
  • Webhooks cannot call production consumers.
  • CRM, messaging, and analytics exports are disabled.
  • DISABLE_WP_CRON is active, and system cron does not invoke WordPress.
  • Queue workers are stopped unless a test explicitly starts them.
  • Search indexing is discouraged, and the site is not publicly reachable.
  • Object storage writes use a disposable bucket or are disabled.

Inspect scheduled events without running them:

lando wp cron event list

Do not assume changing home and siteurl disables integrations. Many plugins store independent endpoints.

Copy files selectively

Source-controlled plugins and themes should come from Git, not a production archive. Copy uploads only when they are needed for reproduction. Exclude caches, generated backups, session files, and old debug logs.

Media can contain private documents even when WordPress calls it an upload. Apply the same authorization and retention controls as the database.

Scan the resulting tree for known production domains and secret variable names. Do not print matching secret values to a shared terminal log.

Verify the clone before debugging

Run a short acceptance pass:

lando wp core verify-checksums
lando wp plugin list
lando wp option get home
lando wp option get siteurl
lando wp cron event list

Then confirm:

  • Front end and administration load locally.
  • Login uses a designated local account.
  • Production user credentials are invalidated where policy requires it.
  • A test email stays in the local capture service.
  • A test payment cannot reach a live gateway.
  • A test webhook cannot reach a production receiver.
  • No page source or network request references production assets unexpectedly.
  • Debug logs contain no newly exposed secrets.

Turn the process into a repeatable reset

Document a two-stage workflow:

  1. An authorized maintainer creates, transfers, and sanitizes the protected source export.
  2. Developers reset from the already sanitized fixture.

The everyday reset should never require broad production access. Version the sanitization logic, not the private result. Record the source date, schema version, checksum, sanitization version, and approved retention deadline in a secure inventory.

Destroy expired local clones and their derivative backups deliberately. Deleting the obvious SQL file is not enough if copies remain in shell history, editor caches, database volumes, or automated workstation backups.

A production-like environment is valuable because it preserves relevant behavior. A safe one preserves only the data necessary to reproduce that behavior and makes every external side effect impossible by default.