Skip to content

Pull Script (Remote to Local)

The pull.sh script syncs a remote site’s database and files to your local WP Local environment.

  • Local by WP installed with the site set up
  • SSH access to the remote server
  • The script must be run from the Local Site Shell (Open Local → select site → “Open Site Shell”)
  1. Exports the remote database via SSH + WP-CLI
  2. Downloads the SQL file via rsync
  3. Cleans up the remote backup
  4. Detects the remote table prefix and updates wp-config.php to match (e.g. wp_PFzYaBYi_)
  5. Resets the local database to remove any leftover tables
  6. Imports the database as-is (no SQL file modification needed)
  7. Runs search-replace on the domain (catches both http:// and https://)
  8. Flushes cache, transients, and rewrite rules
  9. Syncs wp-content/ files via rsync with --delete (removes local files that don’t exist on remote)

Copy this to the site root (e.g. /Users/.../Local Sites/sitename/pull.sh) and update the config block.

#!/bin/bash
# ============================================================
# Pull Script — Production → Local
# Pulls database + files from remote server to local environment.
#
# NOTE: This must be run from the Local (WP Local) Site Shell.
# Open Local → select the site → click "Open Site Shell".
# This opens the container with access to wp-cli and MySQL.
#
# Usage: bash pull.sh
# ============================================================
# ── Configuration ─────────────────────────────────────
REMOTE_SSH_HOST="user@sftp.example.com"
REMOTE_SSH_PORT="2222"
REMOTE_PATH="/site-www"
REMOTE_TEMP_DIR=".tmp-exports" # safe dir for temp DB exports
LOCAL_WP_PATH="/Users/you/Local Sites/sitename/app/public"
REMOTE_SITE_DOMAIN="example.com"
LOCAL_SITE_DOMAIN="dev.sitename.local"
# Files/folders to exclude from sync
EXCLUDES=(
"cache/"
"uploads/cache/"
"upgrade/"
"debug.log"
"*.log"
"object-cache.php"
"advanced-cache.php"
".DS_Store"
)
# ── Preflight ─────────────────────────────────────────
echo "🔍 Checking requirements..."
command -v wp >/dev/null 2>&1 || { echo "❌ WP-CLI is required. Install: brew install wp-cli"; exit 1; }
echo "✓ Ready to pull from: $REMOTE_SITE_DOMAIN"
echo " → To local: $LOCAL_SITE_DOMAIN"
echo ""
read -p "Continue? (y/n) " -n 1 -r
echo ""
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 0
# ── Database Export ───────────────────────────────────
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DB_FILE="wp_backup_${TIMESTAMP}.sql"
echo ""
echo "📦 Exporting remote database..."
ssh -p $REMOTE_SSH_PORT $REMOTE_SSH_HOST "wp db export $REMOTE_TEMP_DIR/$DB_FILE --quiet"
if [ $? -ne 0 ]; then
echo "❌ Failed to export remote database."
exit 1
fi
echo "✓ Database exported (stored in $REMOTE_TEMP_DIR — not publicly accessible)"
# ── Download Database ─────────────────────────────────
echo "⬇️ Downloading database..."
rsync -az -e "ssh -p $REMOTE_SSH_PORT" "${REMOTE_SSH_HOST}:${REMOTE_PATH}/${REMOTE_TEMP_DIR}/${DB_FILE}" "${LOCAL_WP_PATH}/${DB_FILE}"
if [ ! -f "${LOCAL_WP_PATH}/${DB_FILE}" ]; then
echo "❌ Failed to download database file."
exit 1
fi
echo "✓ Database downloaded"
# ── Clean Up Remote ───────────────────────────────────
echo "🧹 Cleaning up remote backup..."
ssh -p $REMOTE_SSH_PORT $REMOTE_SSH_HOST "rm ${REMOTE_TEMP_DIR}/${DB_FILE}"
# ── Match Table Prefix ────────────────────────────────
cd "$LOCAL_WP_PATH"
LOCAL_PREFIX=$(grep "table_prefix" wp-config.php | sed "s/.*'\(.*\)'.*/\1/")
REMOTE_PREFIX=$(grep "CREATE TABLE" "$DB_FILE" | head -1 | sed "s/.*\`\([^_]*_\).*/\1/")
if [ "$REMOTE_PREFIX" != "$LOCAL_PREFIX" ] && [ -n "$REMOTE_PREFIX" ]; then
echo "🔧 Updating wp-config.php table prefix ($LOCAL_PREFIX$REMOTE_PREFIX)..."
sed -i '' "s/\\\$table_prefix = '${LOCAL_PREFIX}'/\\\$table_prefix = '${REMOTE_PREFIX}'/" wp-config.php
echo "✓ wp-config.php updated"
fi
# ── Reset & Import Database ──────────────────────────
echo "📥 Resetting local database and importing..."
wp db reset --yes --quiet
wp db import "$DB_FILE"
if [ $? -ne 0 ]; then
echo "❌ Failed to import database. File saved at: ${LOCAL_WP_PATH}/${DB_FILE}"
exit 1
fi
echo "✓ Database imported"
# ── Search & Replace ──────────────────────────────────
echo "🔄 Running search-replace..."
wp search-replace "$REMOTE_SITE_DOMAIN" "$LOCAL_SITE_DOMAIN" --all-tables --quiet
echo "✓ URLs updated"
# ── Flush Cache ───────────────────────────────────────
wp cache flush --quiet 2>/dev/null
wp transient delete --all --quiet 2>/dev/null
wp rewrite flush --quiet 2>/dev/null
# ── Sync Files ────────────────────────────────────────
echo "📂 Syncing wp-content files..."
# Build exclude flags
EXCLUDE_FLAGS=""
for exc in "${EXCLUDES[@]}"; do
EXCLUDE_FLAGS="$EXCLUDE_FLAGS --exclude=$exc"
done
rsync -avz --progress --delete \
$EXCLUDE_FLAGS \
-e "ssh -p $REMOTE_SSH_PORT" \
"${REMOTE_SSH_HOST}:${REMOTE_PATH}/wp-content/" \
"${LOCAL_WP_PATH}/wp-content/"
if [ $? -ne 0 ]; then
echo "⚠️ File sync had issues — check output above."
else
echo "✓ Files synced"
fi
# ── Cleanup ───────────────────────────────────────────
# Keeping database file for debugging: ${LOCAL_WP_PATH}/${DB_FILE}
# rm -f "${LOCAL_WP_PATH}/${DB_FILE}"
echo ""
echo "✅ Pull complete!"
echo " Database: imported and URLs replaced"
echo " Files: synced from remote"
echo " Local site: https://$LOCAL_SITE_DOMAIN"

Update these 6 values for each site:

VariableDescription
REMOTE_SSH_HOSTSSH connection string (e.g. user@sftp.host.com)
REMOTE_SSH_PORTSSH port (often 22 or 2222)
REMOTE_PATHRemote WordPress root directory
REMOTE_TEMP_DIRA non-public directory on the server for temp DB exports
LOCAL_WP_PATHFull path to the local WordPress public directory
REMOTE_SITE_DOMAIN / LOCAL_SITE_DOMAINDomain only, no protocol

The search-replace uses domain only (no https:// prefix). This ensures both http:// and https:// references are caught — remote databases sometimes store URLs with inconsistent protocols.

The rsync uses --delete so that files which exist locally but not on the remote server are removed. This ensures your local environment is an exact mirror of the remote wp-content/ directory. Without this flag, stale local files (e.g. a child theme template you deleted on production) would persist indefinitely.

These are excluded from the file sync:

cache/
uploads/cache/
upgrade/
debug.log
*.log
object-cache.php
advanced-cache.php
.DS_Store
  1. Copy pull.sh to the new site’s root (e.g. /Users/.../Local Sites/newsite/)
  2. Update the 6 config variables at the top
  3. Ensure you have SSH access to the remote server (ssh -p PORT USER@HOST)
  4. Open Local Site Shell and run bash pull.sh

Rather than rewriting every table prefix inside the SQL dump (which can fail on non-UTF8 binary data), the script updates wp-config.php to match whatever prefix the remote database uses. This means:

  • The SQL file is imported exactly as exported — no sed modifications
  • The local database is fully reset before import to prevent leftover tables from a previous pull
  • wp-config.php’s $table_prefix is kept in sync with the actual database
IssueFix
wp: command not foundRun from Local Site Shell, not regular terminal
wp db import failsCheck MySQL is running in Local
SSH connection refusedVerify SSH host, port, and key are correct
URLs still pointing to remoteCheck the remote DB uses the same domain you configured — try wp option get siteurl after import
Site redirects to live domainClear browser cache or use incognito — browsers cache 301 redirects aggressively
PHP memory exhaustionIncrease memory_limit in Local’s conf/php/php.ini.hbs and add WP_MEMORY_LIMIT to wp-config.php