Build a reliable local Drupal environment with DDEV. Follow the steps to start a new site, import an existing project, and export the code, database, and uploaded files needed to recreate it elsewhere.
Getting a Drupal site running locally should not require an afternoon of negotiating with PHP versions, database settings, and a web server that worked perfectly yesterday.
DDEV helps by giving each project a configurable development environment. You work with familiar commands while it manages the containers running the application and database.
That makes it useful for starting a new Drupal site, taking over an existing project, or handing your work to another developer.
This guide covers all three, including an easily missed detail: a Drupal project is more than its code folder.
What DDEV does for a Drupal projectDDEV uses containers to provide the services your application needs. Project settings live in a .ddev directory, allowing a team to share the environment configuration alongside its code.
You can run PHP tools inside that environment:
ddev composer install
ddev drush statusThis helps avoid situations where one developer runs Composer against a different PHP version from everyone else.
Your project files remain available to your editor on your computer. The database lives in a container-managed volume, so copying the code directory alone does not copy the database. Read how DDEV works (opens in a new tab).
DDEV provides the local environment. Your project may still need additional services, such as search, Redis, or a frontend build process.
Before you begin: understand the pieces you are movingA typical Drupal site has these components:
| Component | What it contains | How it usually moves |
|---|---|---|
| Code | Custom modules, themes, Composer files, scripts | Git repository or reviewed source archive |
| Database | Content, users, active configuration, application data | SQL export |
| Uploaded files | Images, documents, and other managed assets | File archive or approved file transfer |
| Environment settings | DDEV configuration, local overrides, service connections | Shared configuration plus separately managed secrets |
Drupal configuration exports are another important part of the workflow. They describe settings such as fields, content types, and Views. They do not replace a database export containing your content and users. Read about Drupal configuration management (opens in a new tab).
The examples below assume a Composer-managed Drupal site with a web document root. Run project commands from the directory containing composer.json.
Commands use a macOS, Linux, or Windows WSL2 shell. Replace example paths and repository addresses with your own.
Step 1: Install DDEV and a Docker providerDDEV needs a supported Docker provider. The installation route depends on your operating system.
On macOSInstall and start a supported provider, then use Homebrew:
brew install ddev/ddev/ddev
mkcert -installThe certificate setup allows browsers to trust local HTTPS sites.
On WindowsFollow DDEV’s Windows installer instructions. Its recommended WSL2 option can configure Docker CE inside your Linux environment.
For that setup, run project commands inside WSL2 and keep project files in its Linux filesystem, such as ~/projects, rather than /mnt/c.
Follow DDEV’s instructions for your distribution’s Docker and package-manager setup. Repository configuration differs between Debian-based and RPM-based systems.
Use the official instructions for your platform rather than mixing installation methods from different tutorials. See DDEV’s installation guide (opens in a new tab) and Docker provider setup (opens in a new tab).
Check that both tools respond:
docker version
ddev versionIf Docker reports that it cannot connect to the server, resolve that before creating a project.
Step 2: Create a new Drupal 11 projectUse this section for a new, empty site. If you are importing an existing website, skip to Step 3.
Create a directory and configure DDEV:
mkdir -p ~/projects/drupal-local
cd ~/projects/drupal-local
ddev config \
--project-type=drupal11 \
--docroot=web \
--php-version=8.3 \
--database=mariadb:10.11
ddev startThese PHP and database versions are an example environment. For a team project, choose supported versions that match its requirements.
DDEV records the settings in .ddev/config.yaml. See DDEV’s configuration options (opens in a new tab).
Download Drupal and add Drush:
ddev composer create-project "drupal/recommended-project:^11"
ddev composer require drush/drushThis follows DDEV’s Drupal quickstart approach while explicitly selecting the Drupal 11 release series. See DDEV’s Drupal quickstart (opens in a new tab).
Install the new site:
ddev drush site:install standard \
--account-name=admin \
--site-name="Drupal Local" \
-y
ddev drush user:login
ddev launchOpen the one-time login URL printed by Drush to access the local administration interface. Treat that URL as a login credential.
Do not run site:install against an existing site you intend to preserve. It initializes a Drupal database; it is not an import command. Read Drush’s site:install documentation (opens in a new tab).
To bring in a site from a hosting provider, another developer, or a different local tool, obtain:
composer.json and composer.lock.database.sql.gz.web/sites/default/files.Use backups and exports from the same site, matched to a compatible code revision.
Get the database and files through your hosting provider’s backup tools or your team’s established export process. A ZIP containing website code usually does not include the database.
Keep database dumps and transfer archives outside the public document root.
If the source contains customer information, use an approved sanitized dataset where possible. Also arrange local credentials, or disable integrations, before bootstrapping the imported application.
Step 4: Bring the code into DDEVFor a Git-based project, replace this example URL:
mkdir -p ~/projects
git clone \
https://example.com/your-team/your-drupal-project.git \
~/projects/drupal-existing
cd ~/projects/drupal-existingCheck out the branch or commit that matches the database backup.
If you received a code archive, extract it into a project directory instead. Locate composer.json before continuing.
If .ddev/config.yaml already exists, review and reuse it. Check project-specific instructions and hooks before starting the environment.
If the project has no DDEV configuration, create one. For example, for a Drupal 11 site using PHP 8.3 and MariaDB 10.11:
ddev config \
--project-name=drupal-existing \
--project-type=drupal11 \
--docroot=web \
--php-version=8.3 \
--database=mariadb:10.11Use drupal10 for a Drupal 10 project, and match the source environment’s supported database engine and version. Do not treat MySQL and MariaDB as automatically interchangeable.
Start the environment and install its locked dependencies:
ddev start
ddev composer install
ddev restartUse composer install to reproduce the versions recorded in composer.lock. Running composer update would introduce dependency changes while you are trying to recreate the existing site. Read about Composer’s lock-file workflow (opens in a new tab).
DDEV supports adding its configuration to an existing codebase before importing the application’s data. Read about starting a DDEV project (opens in a new tab).
Step 5: Check local settings before importing the databaseDDEV normally creates settings.ddev.php and adds an include to Drupal’s settings.php.
Check that the local settings load correctly, especially if the imported project has custom settings logic or references to hosting-specific files.
DDEV’s default database connection uses:
| Setting | Value |
|---|---|
| Database host | db |
| Database name | db |
| Username | db |
| Password | db |
Let DDEV manage these local connection settings rather than placing production database credentials into your local environment.
Keep local-only overrides separate from shared production settings. The generated settings.ddev.php file is normally Git-ignored. Read about DDEV’s CMS settings management (opens in a new tab).
If the site depends on Redis, Solr, or another service, configure its local equivalent or the project’s documented local fallback. DDEV offers add-ons for additional services. See DDEV services and add-ons (opens in a new tab).
Step 6: Import the database and uploaded filesImport the database from your transfer directory:
ddev import-db --file=/path/to/database.sql.gzThis replaces the current local database by default. Export anything you need from that local database before proceeding. Read about DDEV database imports (opens in a new tab).
Import the public files:
ddev import-files --source=/path/to/files.tar.gzYou can also import from an existing directory:
ddev import-files --source=/path/to/filesUse the directory containing the uploaded files, not the entire Drupal codebase.
For an archive, its root should contain the contents of the files directory. If it instead contains a top-level files folder, select that path:
ddev import-files \
--source=/path/to/files.tar.gz \
--extract-path=filesThis avoids accidentally producing sites/default/files/files. Read DDEV’s file import guidance (opens in a new tab).
Check your destination files before importing into a populated project. DDEV empties the destination directory before it imports, so treat imports as replacements, not a way to merge different sets of uploads.
Private files and remote object storage need separate handling. A successful public-file import does not mean every asset has been transferred.
Step 7: Verify the imported websiteOnce local settings and integrations are configured:
ddev drush cache:rebuild
ddev drush status
ddev drush user:login
ddev launchVerify more than the homepage:
If the checked-out code intentionally requires database updates, follow the project’s update process after backing up the local database. Do not run updates or import configuration merely because you copied a site.
For local email testing, open Mailpit:
ddev mailpitDDEV captures normal PHP mail, but direct SMTP or external email APIs can bypass that capture. Verify your project’s mail configuration before submitting forms or triggering notifications. Read about DDEV email capture (opens in a new tab).
Likewise, switch payment systems, webhooks, CRM connections, and other integrations to local or sandbox settings.
Step 8: Export your local databaseCreate a handoff directory outside the project’s web root:
mkdir -p ../drupal-handoff
ddev export-db \
--file=../drupal-handoff/database.sql.gzThis creates a compressed SQL export. It contains database data, not your custom code or uploaded files. See the DDEV export command (opens in a new tab).
For a quick local database checkpoint before experimenting, you can also use:
ddev snapshot --name=before-changesSnapshots are useful for local recovery, but database type and version matter when restoring them. A SQL export is generally the clearer handoff format between environments. Read about DDEV database management (opens in a new tab).
Neither option is a complete project backup on its own.
Step 9: Export your uploaded files and configurationFrom the project root, archive the public files:
tar -czf ../drupal-handoff/files.tar.gz \
-C web/sites/default/files .The -C option changes the source directory for the archive. The final dot includes its contents, producing the layout expected by ddev import-files.
If the project uses private files, create a separate archive from the configured private directory. Keep those files outside the public web root when restoring them.
If you made intended Drupal configuration changes, export them:
ddev drush config:export -yUse the project’s existing configuration-sync directory. If a new project does not have an appropriate shared directory configured, establish one outside the public document root before adopting the configuration workflow.
Review the exported changes before committing them. Local-only service settings and development modules should not accidentally become production configuration. Read Drush’s config:export documentation (opens in a new tab).
Step 10: Export the code and environment definitionFor a team handoff, share the Git repository and the exact commit corresponding to the database export.
Include the intended, non-secret parts of:
composer.json and composer.lock..ddev/config.yaml and required shared DDEV files.Review and commit the intended files first.
If the recipient needs a source archive, create one from the committed revision:
git archive \
--format=tar.gz \
--output=../drupal-handoff/code.tar.gz \
HEADThis includes committed files only. Uncommitted work, ignored files, and untracked assets will not appear in the archive. Git submodules also require separate attention.
A typical handoff directory now looks like this:
drupal-handoff/
├── code.tar.gz
├── database.sql.gz
├── files.tar.gz
└── README.mdAdd private-file archives or other required assets separately.
Document the code revision, PHP and database versions, required services, and asset locations. Share credentials through your team’s approved secret-management process.
Step 11: Restore the export into a second DDEV projectThe most useful verification is recreating the site from the exported files.
Create a separate directory:
mkdir -p ~/projects/drupal-restored
tar -xzf /path/to/drupal-handoff/code.tar.gz \
-C ~/projects/drupal-restored
cd ~/projects/drupal-restoredAssuming the archive includes the project’s DDEV configuration, give this copy a distinct name:
ddev config --project-name=drupal-restored
ddev start
ddev composer install
ddev restartImport the database and files:
ddev import-db \
--file=/path/to/drupal-handoff/database.sql.gz
ddev import-files \
--source=/path/to/drupal-handoff/files.tar.gz
ddev drush cache:rebuild
ddev drush status
ddev launchRestore any private files and additional services described in the README.
If the archive did not include DDEV configuration, configure the correct project type, document root, PHP version, and database before starting.
Repeat the functional checks from Step 7. Missing custom code, incorrect archive layouts, and undocumented services are much easier to fix now than after someone else receives the handoff.
Exporting a project is different from deploying itAn export recreates a site at a particular point in time.
A production deployment usually moves reviewed code and configuration forward while preserving live content and activity.
Uploading your local database over an active production database can overwrite newer submissions, users, orders, or editorial changes. Use your hosting platform’s deployment process and your team’s release plan.
Similarly, a DDEV environment does not supply production secrets, DNS configuration, backups, or operational monitoring.
Use the local project to prepare and test the release. Treat the live deployment as its own controlled step.
Common problems and what to check| Problem | First checks |
|---|---|
| DDEV cannot start | Docker provider is running; inspect ddev logs |
| Drupal files exist but the browser shows an error | Check docroot and whether Composer installation completed |
| Database connection fails | Confirm DDEV settings load and hosting-specific settings are overridden |
| Composer reports a PHP mismatch | Match DDEV’s PHP version to the project requirements |
| Images are missing | Check file-import location and nested archive folders |
| Site redirects to production | Inspect local overrides, domain modules, redirects, and custom code |
| Imported database reports collation errors | Check source and target database compatibility |
| Local forms send real email | Inspect SMTP and API integrations; verify Mailpit routing |
| A restored project is incomplete | Check uncommitted files, private assets, submodules, and external services |
Avoid changing PHP, database engines, dependencies, and Drupal configuration simultaneously while troubleshooting an import. First recreate the original environment, then make deliberate changes.
Everyday DDEV commands worth keeping nearby| Task | Command |
|---|---|
| Start this project | ddev start |
| Stop this project | ddev stop |
| List local projects | ddev list |
| Show project URLs and connection details | ddev describe |
| Open the website | ddev launch |
| Open a container shell | ddev ssh |
| Rebuild Drupal caches | ddev drush cr |
| Inspect Drupal status | ddev drush status |
| Open captured email | ddev mailpit |
Use ddev stop when you finish working. It preserves your local database. Removing a DDEV project is a different operation and can delete its database. Read about managing DDEV projects (opens in a new tab).
A useful local environment should be repeatable: another developer can obtain the code, import the data and files, follow the README, and reach the same working site.
What usually takes the longest when you set up a Drupal project locally: dependencies, database imports, missing files, or reproducing the hosting environment? Share your experience in the comments.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | DDEV Blog: DDEV July 2026: New Screencasts, Partner Perks | 5 | 7 | 16-07-2026 |
| 2 | DDEV Blog: PHP2Shell: What DDEV Users Need to Know | 0 | 13.6 | 21-10-2026 |
| 3 | Berliners blog: Generate Drupal local actions from Views configuration | 0 | 6.86 | 23-09-2026 |
| 4 | dev | 0 | 11.17 | 14-08-2026 |
| 5 | PreviousNext: AI accelerates Drupal development. Here's why human review still comes first | 0 | 13.06 | 20-10-2026 |
| 6 | Très Bien Blog: Drupack: Drupal infrastructure in a single binary | 0 | 10.53 | 22-09-2026 |
| 7 | Dynamosys Insights: PHP2Shell: Updating Drupal Won’t Fix These Critical PHP Flaws | 0 | 7.88 | 22-10-2026 |
| 8 | Drupal Association blog: The work that just happens: the DA Insider for September 2026 | 0 | 13.04 | 20-10-2026 |
| 9 | Darren Oh: Bring hands-on demos to your content with Drupal Forge | 0 | 10.76 | 21-10-2026 |