A Pantheon Development Workflow: Terminus, Sandbox, DDEV, and GitHub

I help manage production Drupal sites. Real users depend on it, and for a long time my development workflow was embarrassingly simple: pull it down to DDEV, fix the thing, push directly to Pantheon Dev, and hope for the best.

That works until it doesn't. So I finally sat down and worked out a proper workflow using Terminus, Pantheon Multidev environments, and GitHub as a mirror. Here's what I came up with — including the snags I actually hit along the way.


The Problem With Pushing Directly to Pantheon

Pantheon has its own Git remote. It works fine. But pushing directly to it means:

  • No web UI to review your commit history
  • No pull request workflow before code hits Dev
  • No easy way to share or collaborate on the codebase
  • No safety net — what you push is what's running

For a solo developer on a low-stakes site, this is fine. For a production system that real people use, it's sloppy. I wanted something better without over-engineering it.


Before You Start: Two Things to Check

1. Verify your email rerouting

If your site sends transactional email, you need to confirm that non-Live environments won't fire real emails before you clone anything. The reroute_email module is the standard tool for this, but the configuration matters.

The wrong pattern checks for specific environment names:

// DON'T do this — Multidev names won't match
if (in_array($_ENV['PANTHEON_ENVIRONMENT'], ['dev', 'test'])) {
  $config['reroute_email.settings']['enable'] = TRUE;
}

The right pattern catches everything that isn't Live:

// DO this — catches Dev, Test, Multidev, and local
if (defined('PANTHEON_ENVIRONMENT') && PANTHEON_ENVIRONMENT === 'live') {
  $config['reroute_email.settings']['disable'] = TRUE;
} else {
  $config['reroute_email.settings']['enable'] = TRUE;
  $config['reroute_email.settings']['address'] = '[email protected]';
}

Multidev environments have custom names like event-fix-v — they'll never match a hardcoded list of environment names. The !== 'live' pattern is the only safe approach.

2. Know the Multidev name limit

Pantheon truncates Multidev environment names to 11 characters. Plan your branch names accordingly — fix-validation becomes fix-validat, or in my case I named it event-fix-v to stay readable. Whatever you name it, that same name becomes both the Multidev environment identifier and the Git branch on Pantheon's remote.


Understanding the Two Separate Environments

Before walking through the steps, it's worth being clear about something that's easy to blur:

Your local DDEV environment and the Pantheon Multidev are completely separate. DDEV runs on your machine. The Multidev runs on Pantheon's servers. They don't automatically stay in sync.

  • ddev pull pantheon refreshes your local DDEV's database and files from whichever Pantheon environment you've configured — it does not connect your local to the Multidev
  • Your local git branch and the Pantheon Multidev share the same branch name, but their databases and files are independent
  • You can develop and test locally in DDEV, then push code to the Multidev for a final remote check before promoting to Dev

Keep this distinction in mind as you work through the steps.


The Workflow

The core idea: test everything in a throwaway sandbox before touching production, and mirror the repo to GitHub so you have a real web UI.

Step 1: Clone Production to a Sandbox (Multidev)

Pantheon's Multidev feature lets you spin up a full copy of any environment — database, files, and code — as a separate environment with its own URL.

# Create a Multidev environment cloned from Live
# Remember: name must be 11 characters or fewer
terminus multidev:create your-site-name.live event-fix-v

This spins up event-fix-v.your-site-name.pantheonsite.io — a full copy of production to break things in safely.

If you need to refresh the Multidev's database and files from Live later:

terminus env:clone-content your-site-name.live event-fix-v

Step 2: Set Up Your Local Repo on the Right Branch

Important: All git commands must be run from inside your local cloned repo. If you're not already in the directory, navigate there first:

cd /path/to/your/local/site

If you haven't cloned the site locally yet, get the Git URL from the Dev environment (the Git remote is the same for all environments — Dev, Test, Multidev):

terminus connection:info your-site-name.dev --field=git_url
git clone [that-url] my-site
cd my-site

Set the Multidev environment to Git mode, then create a matching local branch:

terminus connection:set your-site-name.event-fix-v git
git checkout -b event-fix-v

Add Pantheon as a named remote if you haven't already:

git remote add pantheon $(terminus connection:info your-site-name.dev --field=git_url)

Step 3: Refresh Your Local DDEV

Pull the database and files into your local DDEV environment so you're working against real data:

ddev pull pantheon

Note: ddev pull pantheon pulls from whichever environment is configured in your .ddev/providers/pantheon.yaml — not automatically from the Multidev branch. For most fixes, pulling from Dev or Live is fine. Configure the source environment in your provider config.


Step 4: Make and Test Your Fix Locally

Make your code changes in DDEV and test locally first. This is the fastest feedback loop.

# Make your fix, then:
ddev drush cr
# Test in browser at your local DDEV URL

When it looks good locally, commit and push to the Pantheon Multidev for a remote check:

git add web/modules/custom/your_module/your_module.module
git commit -m "Fix: loosen validation on past event edits"
git push pantheon event-fix-v

Verify at https://event-fix-v-your-site-name.pantheonsite.io.


Step 5: Merge and Push to Production Dev

Once confirmed on the Multidev, merge back to main and push to Pantheon's Dev environment:

git checkout main
git merge event-fix-v
git push pantheon main:master

Pantheon's master branch maps to Dev. Verify it there, then promote through the dashboard:

Dev → Test → Live

Don't skip Test. It takes 30 seconds and has saved me more than once.


Step 6: Stand Up a GitHub Mirror

This is the part I should have done from day one. Add GitHub as a second remote and you immediately have a full web UI for your Pantheon codebase — commit history, diffs, pull requests before pushing to Pantheon.

# One-time setup
git remote add github [email protected]:your-org/your-site.git
git push github main

To push to both remotes in one command, configure multiple push URLs on a single remote:

git remote set-url --add --push origin [email protected]:your-org/your-site.git
git remote set-url --add --push origin ssh://your-pantheon-remote-url

Now git push origin main pushes to both simultaneously.


The Full Picture

Local DDEV  (ddev pull pantheon ← Dev/Live)
    │
    ├── git push pantheon event-fix-v  →  Multidev Sandbox (test)
    │
    └── git push pantheon main:master  →  Pantheon Dev
    │                                           │
    └── git push github main               Test → Live (dashboard)
              │
         GitHub (web UI, history, PRs)

Why This Works for Me

I'm not running a 10-person engineering team. I don't need a full CI/CD pipeline with automated tests and deployment gates. What I needed was:

  • A safe place to test before touching production ✓
  • A web UI to see what's in the codebase ✓
  • Enough structure to write a playbook and stop winging it ✓

Terminus makes the sandbox setup almost trivial. GitHub as a mirror costs nothing but a git push. And DDEV means I can pull down the full site and run it locally in minutes.

If you're managing a Drupal site on Pantheon and you're still pushing directly to Dev — this is the upgrade that's worth doing this weekend.


Stephen runs Gluebox.com, building calendar reservation and event systems on open source. He maintains RSVP-System.org.