ddev pull WordPress-Generator
This interactive tutorial enables you to pull an existing WordPress site into a local DDEV project. Local development enables stress-free local testing and development - without breaking your live site.
Resources:
- ▶️ SSH connection demo video
- ▶️ Backup file demo - coming soon
- Source code (DDEV scripts): ddev-pull-wp-scripts
- Source code (generator): ddev-pull-wp-generator
1. Select your configuration
2. Setup project folder and copy these files
Create a new empty directory for your project:
mkdir my-wp-site
cd my-wp-site/
The followings files were generated based on your selected configuration. Copy and paste these files into your newly created project folder:
2.1 DDEV project configuration
.ddev/config.yaml
name: my-wp-site
type: wordpress
docroot: ""
nfs_mount_enabled: false
mutagen_enabled: false
# WebServer settings
php_version: "7.4"
mariadb_version: "10.3"
webserver_type: nginx-fpm
Why is this file needed?
This is the DDEV configuration of your project folder. The web_environment-values are our custom variables. These will then be used in the provider pull script, see below. The awesome thing is that this config can be shared via git: This means it can be used in team projects as well and everyone uses the same webserver configuration. Check out the DDEV documentation for more information: config.yaml-docs.
2.2 DDEV provider script
.ddev/providers/ssh.yaml
# Pull a live site WordPress site into DDEV
# Commands:
# 'ddev pull ssh' - pulls a live wordpress site via SSH/mysqldump and rsync into DDEV.
# 'ddev push ssh' - pushes the child theme folder to the remote server
# Project repository: https://github.com/mandrasch/ddev-pull-wp-scripts
# Requires DDEV version >= 1.18.2 !
# https://github.com/drud/ddev/releases/tag/v1.18.2
# https://ddev.readthedocs.io/en/stable/users/providers/provider-introduction/
# ------------------------------ configuration ---------------------------------------------
environment_variables:
sshUser: user123
sshHost: ssh.example.org
sshWpPath: /sites/my-website/wordpress
# path to wordpress on ssh server without(!) trailing slash,
# e.g.: /var/www/html/my-website.eu
childThemeFolderName: twentytwentyone-child
# just the folder name in wp-content/themes/, no slashes
# if you don't use a child theme currently, just leave 'twentytwentyone-child'
# ----------------------------- eo configuration -------------------------------------------
# --------- provider script ---------
# (Update the following code if
# ddev-pull-wp-scripts gets an update)
# -------------------------------------
# 1. Add ssh keys to the user agent
auth_command:
command: |
ssh-add -l >/dev/null || ( echo "Please 'ddev auth ssh' before running this command." && exit 1 )
# 2. Pull a fresh database dump via SSH
#
# Get db connection credentials via bash from wp-config.php on live server,
# dump database via mysqldump to local .sql.gz file in .ddev/downloads
#
# (Possible alternative: 'wp db export' command, not implemented yet because
# detecting availability on remote server was not that easy)
#
db_pull_command:
command: |
# set -x # You can enable bash debugging output by uncommenting
set -eu -o pipefail
pushd "/var/www/html/${DDEV_DOCROOT}" >/dev/null
# run shell commands on remote server for db dump (via heredoc structure)
# TODO: add support for mixed quotes / double quotes in wp-config.php?
(ssh ${sshUser}@${sshHost} "cd ${sshWpPath} && bash -s" <<'ENDSSH'
#set -x # uncomment for debug
# Only works with single quotes currently (no mixed quotes / double quotes)
DB_USER=$(cat wp-config.php | grep "'DB_USER'" | cut -d \' -f 4)
DB_PASSWORD=$(cat wp-config.php | grep "'DB_PASSWORD'" | cut -d \' -f 4)
DB_NAME=$(cat wp-config.php | grep "'DB_NAME'" | cut -d \' -f 4)
DB_HOST=$(cat wp-config.php | grep "'DB_HOST'" | cut -d \' -f 4)
DB_CHARSET=$(cat wp-config.php | grep "'DB_CHARSET'" | cut -d \' -f 4)
# special case, some hosters such as WPEngine have <host>:<port>,
DB_HOST=$(echo $DB_HOST| cut -d':' -f 1) # remove port
mysqldump \
--user $DB_USER \
--host $DB_HOST \
--default-character-set $DB_CHARSET \
--no-tablespaces \
--password="$DB_PASSWORD" $DB_NAME
ENDSSH
) > .ddev/.downloads/db.sql
# DDEV will import the db.sql.gz file automatically, compress it:
gzip -9 .ddev/.downloads/db.sql
# Thx to https://stackoverflow.com/a/45927977/809939
# Thx to https://www.cloudsavvyit.com/14216/how-to-run-a-local-shell-script-on-a-remote-ssh-server/
service: web
# 3. Rsync all the files (except excludes)
files_pull_command:
command: |
# set -x # You can enable bash debugging output by uncommenting
set -eu -o pipefail
ls /var/www/html/.ddev >/dev/null
pushd /var/www/html/${DDEV_DOCROOT} >/dev/null
# Add trailing slash for sshWpPath here in a safe way
# (maybe user mistakenly provided trailing slash, thx
# https://gist.github.com/luciomartinez/c322327605d40f86ee0c)
[[ "${sshWpPath}" != */ ]] && sshWpPathTrailingSlash="${sshWpPath}/"
echo "Downloading files from remote site to /var/www/html/${DDEV_DOCROOT}"
# exclude child theme + some default locations of wordpress backup plugins
# (exclude pattern is glob based, not path based)
# add -v for output of files transferred, -vv for full debug
rsync -azh --stats \
--exclude=".git/" \
--exclude=".gitignore" \
--exclude=".ddev/" \
--exclude="README.md" \
--exclude="LICENSE" \
--exclude="wp-content/themes/${childThemeFolderName}" \
--exclude="wp-content/updraft" \
${sshUser}@${sshHost}:${sshWpPathTrailingSlash} .
# TODO: Should we use -a (archive mode) or is there a better combination of flags?
service: web
# 4. Set database connection + migrate URLs in DB
#
# We use this step to run some important WP-CLI commands locally
# a) Replace db connection settings in wp-config.php
# b) Database migration: Replace live site url (from pulled wp-config)
# with DDEV_PRIMARY_URL (<your-project>.ddev.site) in local database
# c) Overwrite WP_HOME and WP_SITEURL
files_import_command:
command: |
# set -x # You can enable bash debugging output by uncommenting
set -eu -o pipefail
pushd "/var/www/html/${DDEV_DOCROOT}" >/dev/null
echo "Adjusting wp-config db connection settings for DDEV ..."
wp config set DB_NAME "db" && wp config set DB_USER "db" && wp config set DB_PASSWORD "db" && wp config set DB_HOST "db"
# Important: Use wp search-replace for URL replacement
echo "Replacing the old URL ($(wp option get siteurl)) in database with DDEV local url (${DDEV_PRIMARY_URL})..."
wp search-replace $(wp option get siteurl) "${DDEV_PRIMARY_URL}"
# Additional update the wp config values, because some devs/hoster, e.g. raidboxes
# put it here (only remove if existent)
# TODO: if [wp config has] would be better, but how to implement it here?
# if [ "$(wp config has WP_HOME)" = 0 ]; then wp config delete WP_HOME; fi
# if [ "$(wp config has WP_SITEURL)" = 1 ]; then wp config delete WP_SITEURL; fi
# TODO: This throws a (false) error currently if var is not set, how do we get rid of it?
# event with --no-add:
# Error: The constant or variable 'WP_HOME' is not defined in the 'wp-config.php' file.)
wp config set WP_HOME "${DDEV_PRIMARY_URL}"
wp config set WP_SITEURL "${DDEV_PRIMARY_URL}/"
# Flush object cache
wp cache flush
# Optional: more steps would be possible here after import, e.g. for WP Super Cache
# wp config delete WPCACHEHOME
echo "All set, have fun! Run 'ddev launch' to open your site."
service: web
# ! EXPERIMENTAL - USE WITH CAUTION !
# Push child theme folder to remote server
# (Other reliable ways are WPPusher, Github Action pipeline, etc.)
db_push_command:
command: |
# set -x # You can enable bash debugging output by uncommenting
set -eu -o pipefail
echo "Skipping db_push_command, we don't use it."
service: web
files_push_command:
command: |
# set -x # You can enable bash debugging output by uncommenting
set -eu -o pipefail
pushd "/var/www/html/${DDEV_DOCROOT}" >/dev/null
# Add trailing slash for sshWpPath (maybe user mistakenly provided trailing slash)
# (thx https://gist.github.com/luciomartinez/c322327605d40f86ee0c)
[[ "${sshWpPath}" != */ ]] && sshWpPathTrailingSlash="${sshWpPath}/"
# Send child theme from source (DDEV) to target (SSH server / webspace)
source="/var/www/html/${DDEV_DOCROOT}/wp-content/themes/${childThemeFolderName}/"
target="${sshUser}@${sshHost}:${sshWpPathTrailingSlash}wp-content/themes/${childThemeFolderName}"
echo "Configuration for push:"
echo "Source: ${source}"
echo "Target: ${target}"
echo "Start pushing the child theme folder..."
rsync -rvzh "${source}" "${target}"
service: web
Why is this file needed?
This is our pull script which takes care of pulling the live web site to your local DDEV project. See DDEV docs for more information: Hosting Provider Integration
2.3 Ignore everything except child theme folder (optional)
This is for git usage. You can skip this file if you don't use a git repository.
.gitignore
# Ignore all ...
/*
# ... but track specific files / folders:
# General files
!.gitignore
!/README.md
!/LICENSE
# DDEV config and provider script
!/.ddev
/.ddev/*
!/.ddev/config.yaml
!/.ddev/providers
/.ddev/providers/*
!/.ddev/providers/ssh.yaml
!/.ddev/providers/backup.yaml
# Child theme:
!/wp-content
/wp-content/*
!/wp-content/themes
/wp-content/themes/*
!/wp-content/themes/twentytwentyone-child
Why is this needed?
This is needed to track and manage the child theme in a git repository, but ignore the rest of the WordPress files which will be pulled to the local project in the next step.
3. Start DDEV
Your configuration is all setup, run this command in your project folder:
ddev start
Add your SSH keys to DDEV to connect with the live site:
ddev auth ssh
4. Pull your live site 🙌
Alright, let's pull the live site content (files and database, except the child theme folder):
ddev pull ssh
Use with caution Please always check in the local wp-config.php-file that the database connection replacement was successfull and that you are connected to the local DDEV project database (and not the remote database of your live site).
Optional: Already have a child theme on your live site, but not in your local folder? Pull it in via:
ddev exec 'rsync -avhz user123@ssh.example.org:/sites/my-website/wordpress/wp-content/themes/twentytwentyone-child/ /var/www/html/${DDEV_DOCROOT}/wp-content/themes/twentytwentyone-child/'
5. Develop locally & have fun!
Now its possible to work with your site locally in a safe way.
Open the locally cloned site in your browser:
ddev launch
Open admin dashboard in your browser:
ddev launch wp-admin/
You can always pull again, for example when your live sites content changed and new images were added:
ddev pull ssh
Info Your local database and files will be overwritten with the latest database and files from your live site (expect for the child theme folder defined in .ddev/providers/ssh.yaml).
6. Experimental Push your child theme via SSH
You can push your local child theme folder via SSH (rsync) to your live site. While this is a quick and easy way of deploying for single developers, this can cause trouble in team projects (See alternatives below).
ddev push ssh --skip-db
Use with caution Please create a backup beforehand and make sure your configuration is correctly set in .ddev/providers/ssh.yaml.
Nice, that's all!
Alternatives for deployment in team projects (via git):
- Deploy your child theme via WPPusher plugin or SayHelloGmbH/git-installer on your live site. Both solutions offers the option of pulling the child theme from a repository subfolder. The best thing: Git is not required on your webspace, because WPPusher uses the HTTPS-API to get the repository contents. See WPPusher documentation for more information: WPPusher - Setting up a theme and screencast example 🎥 Pull a WordPress site into a local DDEV project (2022 edition - ddev pull ssh)
- Use git-updater (doesn't support subdirectory deployments, needs a workaround)
- Use a Github Action to deploy your child theme (via sftp/ssh)
Troubleshooting: Verify SSH connection in terminal
Verify that SSH connection works in general:
ssh user123@ssh.example.org
Use "exit" to end SSH connection for next test.
Verify that the "Path to WordPress" setting is correct:
ssh -t user123@ssh.example.org "cd /sites/my-website/wordpress; exec $SHELL --login"
Use "exit" to end SSH connection.