If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.
This add-on integrates Upkeep into your DDEV project: it provides named database fixtures — create a gzipped SQL dump of the current database, reload it later in seconds, and share it via the module’s own repository or a local fixture library.
It is the fixture layer of the Upkeep maintenance orchestrator (which installs it into every environment it provisions), but it is equally usable standalone in any Drupal ddev project — in particular a module checkout using ddev-drupal-contrib.
ddev add-on get owenbush/ddev-upkeep
ddev restart
After installation, make sure to commit the .ddev directory to version control.
To pin a release, or to install from a checkout while developing the add-on:
ddev add-on get owenbush/ddev-upkeep --version 1.0.0
ddev add-on get /path/to/ddev-upkeep
Upkeep installs this add-on itself, at a
pinned release, into every environment it provisions — set
UPKEEP_ADDON_SOURCE=/path/to/ddev-upkeep to point it at a checkout instead.
The fixture commands were ddev fixture-create, -load, -list and -prune
until they were namespaced. ddev puts every add-on’s host commands in one
flat namespace per project, so a name as general as fixture-load claims
ground this add-on has no business claiming — the next add-on that wants it
has nowhere to go, and nothing in the name says where the command came from.
Re-running ddev add-on get performs the upgrade: the namespaced commands are
installed and the old ones are removed, so nothing is left behind under a name
this add-on no longer documents. Removal is guarded on the file being ours —
a fixture-load somebody else wrote is not this add-on’s to delete.
Update any scripts or CI that call the old names; there is no alias.
| Command | Description |
|---|---|
ddev upkeep-fixture-create <name> [--dest=module\|library] [--no-sanitize] |
Dump the current DB to a portable fixture (<name>.sql.gz) and write its manifest (<name>.yml). Sanitizes via drush sql:sanitize by default when destined for the module repo. Warns when the dump exceeds 5 MB (UPKEEP_FIXTURE_SIZE_WARN_MB) |
ddev upkeep-fixture-load <name> |
Load a fixture: first use imports the dump and materializes a snapshot; later loads restore the snapshot (fast path). Module tests/fixtures/ shadows the shared library |
ddev upkeep-fixture-list |
List fixtures in both scopes with size and snapshot state |
ddev upkeep-fixture-prune |
Delete this project’s disposable materialized snapshots (never the .sql.gz dumps) |
ddev describe |
View service status and used ports for Upkeep |
ddev logs -s upkeep |
Check Upkeep logs |
Fixtures resolve module-first: tests/fixtures/<name>.sql.gz in the module checkout, then the shared library (UPKEEP_FIXTURE_LIBRARY, defaulting to $UPKEEP_COCKPIT/fixtures, defaulting to ~/.upkeep/fixtures).
A dump is not a self-contained artifact. It encodes references to code — enabled extensions, plugin IDs inside config entities, field types, schema versions — and captures none of it. Import one into a codebase that does not provide that code and Drupal cannot build its container; the failure lands in whatever ran next, and reads as though that thing is broken.
So every dump gets a sidecar beside it:
tests/fixtures/smoke.sql.gz # the dump, unchanged
tests/fixtures/smoke.yml # what it needs in order to mean anything
core: '11'
core_version: '11.4.6'
db_engine: 'mariadb:10.11'
created_at: '2026-09-08T18:00:00Z'
require:
drupal/admin_toolbar: '^3.4'
extensions:
- admin_toolbar
- node
- pathauto
It is generated, never hand-written. upkeep-fixture-create already has
the database open and the project’s composer.json in front of it, so it
reads the enabled extensions from core.extension and the requirements from
the project’s own direct dependencies, minus core, drush and the module the
project is a checkout of. Nothing to remember and nothing to keep in step.
upkeep-fixture-load then makes the codebase able to hold the dump, or
refuses before touching the database:
ddev composer require. What it installs is printed.Refusing happens before the import, so a fixture that cannot work leaves the database exactly as it was rather than half-replaced.
A dump with no sidecar behaves exactly as fixtures did before manifests existed: it is imported, and nothing is checked. Nothing that already works stops working.
The dump itself is untouched by any of this, so gunzip -c <name>.sql.gz and
import it with whatever you like remains true.
A fixture is a named gzipped SQL dump, <name>.sql.gz. The dump is the
portable source of truth — it is what you commit, share, and keep.
On first ddev upkeep-fixture-load, the dump is imported and materialized into a
fast-format snapshot artifact (.ddev/upkeep/materialized/<name>.sql,
streamed straight into the DB server on restore). Subsequent loads restore
that snapshot, which is much faster than re-importing the dump. A metadata
file records the DB engine identity and the dump’s checksum at
materialization time; if either changes — you upgraded the database engine,
or the dump was updated — the snapshot is considered stale and is rebuilt
from the dump on the next load.
Materialized snapshots are disposable, engine-tied local caches — never
authoritative. ddev upkeep-fixture-prune deletes them (and only them); the next
load rebuilds from the dump. Don’t commit them.
upkeep-fixture-load resolves a name per-module first:
tests/fixtures/<name>.sql.gz in the project root, when
the project root is a module checkout (an *.info.yml at the root — the
ddev-drupal-contrib layout).$UPKEEP_FIXTURE_LIBRARY, defaulting to
$UPKEEP_COCKPIT/fixtures, defaulting to ~/.upkeep/fixtures.A module fixture always shadows a same-named library fixture. Configuration
can come from the caller’s environment or from .ddev/.env.upkeep (the ddev
dotenv convention); real environment variables win over the dotenv file.
tests/fixtures/ convention (for module maintainers)This section stands alone: it applies to any Drupal contrib module, whether or not you or your co-maintainers use Upkeep or this add-on.
tests/fixtures/ in the module
repository, alongside the module’s other test resources.tests/fixtures/<name>.sql.gz, and its generated manifest,
tests/fixtures/<name>.yml. Commit both: the dump is what the fixture is,
the manifest is what it needs, and a dump whose requirements nobody
recorded is a dump that only works on the machine it was made on. Names are lowercase, filesystem-safe
(letters, digits, dot, dash, underscore), and describe the state the
fixture provides — e.g. smoke.sql.gz, two-vocabularies.sql.gz,
upgrade-from-2x.sql.gz.drush sql:sanitize) or build the
fixture from a scratch install that never held real data.
(ddev upkeep-fixture-create runs drush sql:sanitize for you by default when
the destination is the module repo; --no-sanitize opts out for databases
that are already clean.)Anyone with this add-on can then load your fixture with
ddev upkeep-fixture-load <name>; anyone without it can simply
gunzip -c tests/fixtures/<name>.sql.gz and import it with the tool of
their choice.
To change the Docker image:
ddev dotenv set .ddev/.env.upkeep --upkeep-docker-image="ddev/ddev-utilities:latest"
ddev restart
then re-run the ddev add-on get you installed with (see Installation) if
you want the change reflected in a fresh add-on install.
Make sure to commit the .ddev/.env.upkeep file to version control.
All customization options (use with caution):
| Variable | Flag | Default |
|---|---|---|
UPKEEP_DOCKER_IMAGE |
--upkeep-docker-image |
ddev/ddev-utilities:latest |
UPKEEP_FIXTURE_LIBRARY |
--upkeep-fixture-library |
$UPKEEP_COCKPIT/fixtures, else ~/.upkeep/fixtures |
UPKEEP_FIXTURE_SIZE_WARN_MB |
--upkeep-fixture-size-warn-mb |
5 |
The bats test suite and how to run it (including the shim-HOME setup some macOS configurations need) are documented in docs/testing.md. A recorded end-to-end pass against a real contrib module is in docs/manual-e2e-conditions-helper.md.
Contributed and maintained by @owenbush
If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.