CWS Pantheon DDEV for Windows WSL2
Prerequisites
-------------
- Windows with WSL2 enabled
- Docker Desktop installed
- DDEV installed inside WSL2
- A CWS Pantheon machine token
You will need “Admin By Request” [add more]
1) Install WSL2 and Ubuntu
Open PowerShell as Administrator and run:
wsl --install
wsl --update
wsl --install Ubuntu --name DDEV
Reboot if Windows asks. Then open the new DDEV/Ubuntu distro from the Start menu and create your Linux username/password. DDEV’s docs specifically say WSL2 must be version 2 and recommend Ubuntu or another Linux distro first.
Open Docker Desktop and go to:
Settings → Resources → WSL Integration
Enable integration with my default WSL distro is ON
Close Docker Desktop & Ubuntu
wsl --shutdown
-wait-
Reopen Docker Desktop, wait until running
Reopen Ubuntu
In Ubuntu,
> ddev version
> ddev poweroff
Step 1: Cloning the Pantheon Repository
In the Pantheon dashboard for your site, copy the git clone stub for the needed environment (dev, or multisite)
In Ubuntu, execute the clone stub:
cd ~/projects
git clone <repo-url> azs-immunobiology
cd azs-immunobiology
Step 2: Fix the DDEV Project Name
----------------------------------
The repo ships with a generic name in .ddev/config.yaml. Change it:
sed -i 's/^name: my-site/name: azs-immunobiology/' .ddev/config.yaml
If you get "project root already contains a project named my-site":
ddev stop --unlist my-site
Step 3: Set Pantheon Machine Token (If not previously configured)
-----------------------------------
Save your token to the DDEV global config (one-time setup):
ddev config global --web-environment-add="TERMINUS_MACHINE_TOKEN=YOUR_TOKEN_HERE"
Verify it was saved correctly (should have exactly one entry):
grep -A3 "web_environment" ~/.ddev/global_config.yaml
Should show:
web_environment:
- TERMINUS_MACHINE_TOKEN=YOUR_TOKEN_HERE
If there is a duplicate/malformed entry, edit ~/.ddev/global_config.yaml
and remove the extra line.
Step 4: Configure Pantheon Provider
------------------------------------
Copy the provider example and set the project name:
cp .ddev/providers/pantheon.yaml.example .ddev/providers/pantheon.yaml
Edit .ddev/providers/pantheon.yaml and change:
environment_variables:
project: azs-immunobiology.dev
Step 5: Start DDEV
--------------------
ddev start
Step 6: Install Composer Dependencies
---------------------------------------
ddev composer install
Step 7: Create Fresh Backups on Pantheon
-----------------------------------------
Create fresh backups so you get current content and files:
ddev exec terminus backup:create azs-immunobiology.dev --element=db
ddev exec terminus backup:create azs-immunobiology.dev --element=files
Step 8: Pull Database and Files from Pantheon
----------------------------------------------
ddev pull pantheon -y
Step 9: Unblock Admin User and Log In
--------------------------------------
The Pantheon admin account may be blocked locally. Unblock and get a login link:
ddev drush user:unblock azadmin
ddev drush user:login --name=azadmin
Paste stub it into your browser, enter.
ddev drush cr
Site URL: https://azs-immunobiology.ddev.site
Site should have all content and images from Pantheon dev.
Helpful Commands
--------------
Start: ddev start
Stop: ddev stop
Admin login link: ddev drush user:login --name=azadmin
Clear cache: ddev drush cr
Open site in browser: ddev launch
SSH into container: ddev ssh
MySQL CLI: ddev mysql
Uncompress: gunzip filename.gz
Refresh dev Pantheon and pull to local :
ddev exec terminus backup:create azs-immunobiology.dev
ddev pull pantheon -y
Troubleshooting
---------------
- "Unsupported Request" at /user/login:
That error comes from the CAS/WebAuth SSO gateway. Use the standard
Drupal username/password form on that page, NOT the "Log in using
University of Arizona WebAuth" link. Or use: ddev drush user:login
- "xdg-open: command not found" from ddev launch:
Install wslu: sudo apt install wslu -y
- Site looks empty after pull:
The backup was stale. Create a fresh one on Pantheon first:
ddev exec terminus backup:create azs-immunobiology.dev --element=db
ddev exec terminus backup:create azs-immunobiology.dev --element=files
Then: ddev pull pantheon -y
- Images missing after pull:
Files backup was stale. Create a fresh files backup and pull just files:
ddev exec terminus backup:create azs-immunobiology.dev --element=files
ddev pull pantheon --skip-db -y
ddev drush cr
DDEV vs Lando
Action Lando Command DDEV Command
Initialize lando init (interactive) ddev config (interactive)
Start lando start ddev start
Stop lando stop ddev stop
Restart/Rebuild lando rebuild ddev restart
View Project Info lando info ddev describe
View Logs lando logs ddev logs
Remove Project lando destroy (deletes data) ddev delete (options to keep data)
Local DDEV QS2 -> QS3 Migrations
Setting up a dev site from scratch
Source https://live-cmm-medicine.pantheonsite.io/
- to -
https://dev-azs-cmm.pantheonsite.io/
[ add notes: git action ]
[ add notes: clone dashboard stub, cd into ]
[ ] Verify DDEV is installed and check version ---
which ddev && ddev --version
[ ] Initialize DDEV configuration ---
* Sets project type to Drupal, docroot to web/, PHP 8.4 (matching composer.json platform config)
ddev config --project-type=drupal --docroot=web --php-version=8.4 --project-name=azs-cmm
[ ] Start DDEV containers (web server, database, router) ---
*Note: Port 80 timed out, changed DDEV's port to 33000
ddev start
[ ] Install Composer dependencies inside the DDEV container ---
*Quickstart profile, Drupal, and all contrib modules
ddev composer install
[ ] Verify terminus CLI is available ---
which terminus && terminus --version
[ ] Check terminus authentication status ---
terminus auth:whoami
[ ] Check git remote to identify Pantheon site UUID ---
git remote -v
[ ] Create a fresh database backup on Pantheon dev environment ---
terminus backup:create azs-cmm.dev --element=db
[ ] Download the database backup from Pantheon ---
terminus backup:get azs-cmm.dev --element=db | xargs wget -q -O database.sql.gz
[ ] Import the database dump into the DDEV database ---
ddev import-db --file=database.sql.gz
[ ] Clear Drupal caches and verify site status ---
ddev drush cr
ddev drush status --fields=drupal-version,db-status,bootstrap
[ ] Unblock the admin user (blocked by Pantheon security practice) and generate login link ---
ddev drush user:unblock azadmin
ddev drush uli
[ ] Clean up the downloaded database dump ---
rm -f database.sql.gz
Download and map
Download the source database from live and decompress in your new DDEV site root
> gunzip filename.gz
Open the Drupal /structure /content types /people and make a mapping spreadsheet with the source and destination Person fields, example

[add steps for running the action “configure qs2 to qs3 migrations”]
Creating Recipes
In this example we are using a recipe to add a test field into an existing content type, specifically “Recipe Test” into the Person content type.
DDEV Pathing
DDEV mounts the project root into the container at /html. So:
WSL2 path Container path
===============================================
/azs-immunobiology /html
/recipes /var/www/html/recipes/
/web /var/www/html/web/
DDEV drush runs inside the container, so it needs the html path. The files you edit on your Windows host at recipes are the exact same files the container sees at /var/www/html/recipes/.
Starting with the structure:
recipes/az_person_recipe_test/
├── recipe.yml
└── config/
├── field.storage.node.field_recipe_test.yml
└── field.field.node.az_person.field_recipe_test.yml
- Create the recipe folder under recipes with a recipe.yml manifest that declares what config to import
- Add field config files in a config subfolder:
field.storage.node.field_recipe_test.yml
(defines the field type globally (plain text)
field.field.node.az_person.field_recipe_test.yml
( attaches it to the az_person with label "Recipe test")
- Update .gitignore to allow your recipe folder to be tracked
- Create az_person_recipe_test.yml
- Apply with: ddev drush recipe /var/www/html/recipes/az_person_recipe_test
recipe.yml
==============
name: 'Person Recipe Test Field'
description: 'Adds a plain text field named "Recipe test" to the Person content type.'
type: 'Content type'
install: []
config:
import:
node:
- field.storage.node.field_recipe_test
- field.field.node.az_person.field_recipe_test
field.field.node.az_person.field_recipe_test.yml
==============
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_recipe_test
- node.type.az_person
module:
- node
id: node.az_person.field_recipe_test
field_name: field_recipe_test
entity_type: node
bundle: az_person
label: 'Recipe test'
description: ''
required: false
translatable: false
default_value: []
default_value_callback: ''
settings: []
field_type: string
field.storage.node.field_recipe_test.yml
==============
langcode: en
status: true
dependencies:
module:
- node
id: node.field_recipe_test
field_name: field_recipe_test
entity_type: node
type: string
settings:
max_length: 255
is_ascii: false
case_sensitive: false
module: core
locked: false
cardinality: 1
translatable: true
indexes: []
persist_with_no_fields: false
custom_storage: false
Apply the recipe
> ddev drush recipe /var/www/html/recipes/az_person_recipe_test
> ddev drush cr