Provision
The provisioning process, handled by the provision script shipped via the
drevops/vortex-tooling
Composer package (installed at vendor/drevops/vortex-tooling/src/provision),
sets up a Drupal site on already assembled codebase by either importing an
existing database from the dump or installing a fresh instance of Drupal using a
profile, followed by running the necessary configuration import and database
updates.
The main purpose of the script is to automate the setup of a Drupal site in every environment, ensuring consistency and eliminating manual steps.
Rationale
While Drush provides individual commands for deployment steps (such as
drush updatedb, drush config:import, and drush deploy:hook), using them
directly assumes the site is already in a bootstrapped, stable state. In
practice, especially during initial setup or provisioning in dynamic
environments (like CI pipelines, container builds, or multisite setups),
additional orchestration is needed.
The provision script addresses these gaps by:
- Bootstrapping the environment: It can import a database dump or install a fresh Drupal instance from a profile.
- Handling conditional logic: It accounts for different runtime scenarios, such as skipping provisioning, enforcing fresh database imports, or using maintenance mode.
- Enforcing consistency: The same provisioning logic runs across local, CI, staging, and production environments, eliminating "it works on my machine" issues.
- Supporting extensibility: It allows for custom post-provisioning scripts, making it easy to layer in project-specific logic like enabling test modules or running migrations.
In short, provision orchestrates standalone Drush commands in a consistent,
repeatable, and configurable process — turning a manual setup step into a
reliable automation layer.
Database import vs full provisioning
Vortex provides two main approaches for setting up your database:
Full provisioning (ahoy provision)
Runs the complete provisioning process including:
- Database import from dump or profile installation
- Database updates via
drush updatedb - Configuration import via
drush config:import(if config files present) - Cache rebuilds
- Deployment hooks
- Post-provisioning custom scripts
Use this when you want a fully configured site ready for development or deployment.
# Full provisioning (recommended for most cases)
ahoy provision
Database import (ahoy import-db)
Imports only the database dump without running deployment scripts:
- Imports the database dump
- Skips configuration imports
- Skips database updates
- Skips post-provisioning scripts
# Import database only from default dump in .data/db.sql
ahoy import-db
# Import database only from a specific dump file
ahoy import-db path/to/dump.sql
Use ahoy import-db when you only need the raw database data without any configuration changes or updates.
Use ahoy provision when you want the full setup including configuration and updates.
Provisioning flow
Below is a generic provisioning flow diagram illustrating the steps taken by the
provision script. The flow may vary based on environment variables and
conditions that could alter the execution path.
The numbered steps refer to the environment variables described in the next section.
🚀 Start
│
① 💡 Skip provision? ──Yes──► 🏁 End
│ No
▼
② 💡 Provision type = database
│
├─ 💡 Existing site found = YES
│ ├─ Container image set? ──► preserve content (no change)
│ ├─ ③ Overwrite DB? ──Yes──► 🗑️ Drop DB ──► 🛢️ Attempt import from dump
│ │ │
│ │ ├─ Dump file exists? ──► 🛢️ Import DB
│ │ └─ Dump file missing?
│ │ ├─ ④ Fallback? ──Yes──► 📦 Install from profile ✓
│ │ └─ ④ Fallback? ──No───► 🏁 EXIT 1 (fail) ✗
│ └─ else ──► preserve content, ⑦ skip sanitization
│
└─ 💡 Existing site found = NO
├─ Container image set?
│ ├─ ④ Fallback? ──Yes──► 📦 Install from profile ✓
│ └─ ④ Fallback? ──No───► 🏁 EXIT 1 ("corrupted image") ✗
└─ Container image not set? ──► 🗑️ Drop DB ──► 🛢️ Attempt import from dump
│
├─ Dump file exists? ──► 🛢️ Import DB
└─ Dump file missing?
├─ ④ Fallback? ──Yes──► 📦 Install from profile ✓
└─ ④ Fallback? ──No───► 🏁 EXIT 1 (fail) ✗
── after provisioning (both DB and profile paths) ──
⑤ 💡 Skip other operations? ──Yes──► 🏁 End
│ No
▼
⑥ 🚧 Enable maintenance mode
▼
🔄 Run DB updates
▼
⑦ 💡 Verify config unchanged? ──Config changed──► 🏁 EXIT 1 (fail) ✗
│ Config unchanged (or check disabled)
▼
⑧ 🧹 Rebuild caches after DB updates (skippable)
▼
⬇️ Import configuration (if config files present)
▼
⑨ 🔁 Repeat configuration import (opt-in)
▼
🧹 Rebuild caches
▼
🔄 Run deployment hooks
▼
⑩ 😷 Run DB sanitization
▼
⚙️ Run custom scripts
▼
⑥ 🚧 Disable maintenance mode
▼
🏁 End
Customizing flow
You can control the provisioning flow using the following environment variables:
VORTEX_PROVISION_SKIP=1
Kill-switch to completely skip provisioning. The script will exit immediately after start. Useful in emergencies when any kind of automation needs to be disabled.VORTEX_PROVISION_TYPE=profile
Install from a Drupalprofileinstead of importing from adatabasedump. Useful for building sites without the persistent DB and/or test profile configuration installation.VORTEX_PROVISION_OVERRIDE_DB=1
Drop an existing database before importing from dump/installing from profile. This is useful when an already provisioned environment requires a fresh database to be imported.VORTEX_PROVISION_FALLBACK_TO_PROFILE=1
Automatically fall back to installing from profile if the database dump file or container image is not available. The site is installed from the configured profile, the Shield module is enabled to protect the environment, and all post-provision operations (configuration import, database updates, deployment hooks, etc.) are skipped. This provides a minimal working Drupal site when no database is available.VORTEX_PROVISION_POST_OPERATIONS_SKIP=1
Skip configuration imports, database updates, and other post-provisioning steps. Essentially, this isdrush sql:dropand$(drush sql:connect) < .data/db.sqlcommands. This is useful when you want to provision a site without running any additional operations.ahoy import-dbuses this flag to import DB and exit.VORTEX_PROVISION_USE_MAINTENANCE_MODE=1
Enable maintenance mode right after the site is bootstrappable and disable it at the end. Useful when you want to prevent users from accessing the site while it is being provisioned.VORTEX_PROVISION_VERIFY_CONFIG_UNCHANGED_AFTER_UPDATE=1
Verify that active configuration was not changed by database updates. When enabled and config files are present, the provision will fail ifdrush updatedbmodifies active configuration, preventingdrush config:importfrom silently overwriting those changes.VORTEX_PROVISION_CACHE_REBUILD_AFTER_DB_UPDATE_SKIP=1
Skip the cache rebuild that runs between database updates and configuration import. By default, caches are rebuilt afterdrush updatedbto ensure a clean state before importing configuration.VORTEX_PROVISION_CONFIG_IMPORT_REPEAT=1
Repeat the configuration import after the initial import. Useful when database update hooks introduce new configuration that affects subsequent configuration imports (e.g., new config_split settings). Disabled by default.VORTEX_PROVISION_SANITIZE_DB_SKIP=1
Disable database sanitization.
These variables can be set in your .env file to apply them globally or set
in your CI or hosting provider's specific environment based on your needs.
Maintenance mode
During the provisioning process, you may want to enable maintenance mode to prevent users from accessing the site while it is being updated.
To enable maintenance mode, set the VORTEX_PROVISION_USE_MAINTENANCE_MODE=1
environment variable in your .env file to apply it globally or set it in your
hosting provider's specific environment.
Database sanitization
The provision script includes a step to sanitize the database after
provisioning. This helps ensure that sensitive data — like real email addresses,
passwords, and user information — is replaced with safe, generic values in
non-production environments. It prevents issues like accidentally sending emails
to real users or exposing private data during testing, making shared
environments safer to work with.
Sanitization takes place only after the database is imported, so anyone with access to the dump file can still see sensitive data.
If your database has highly sensitive data, consider sanitizing the database dump before it can be downloaded (sanitize on export). There are tools available for this purpose, such as Drush GDPR Dumper or MTK. These tools can be easily integrated into Vortex-based projects without changing the provisioning process.
By default, the database sanitization step is enabled by default on all
environments except production. To disable database sanitization, set the
VORTEX_PROVISION_SANITIZE_DB_SKIP=1 in the .env file or in your hosting
provider's specific environment.
Customizing database sanitization
Place these variables in the .env file or in your hosting provider's specific
environment to further customize the database sanitization:
VORTEX_PROVISION_SANITIZE_DB_EMAIL=user_%uid@your-site-domain.example
Replace all emails with a tokenized email string.VORTEX_PROVISION_SANITIZE_DB_PASSWORD=<random or exact>
Replace passwords with a random or exact value.VORTEX_PROVISION_SANITIZE_DB_REPLACE_USERNAME_WITH_EMAIL=0
Replace username with email. Useful to also sanitize user names.VORTEX_PROVISION_SANITIZE_DB_ADDITIONAL_FILE=./scripts/sanitize.sql
Path to a file with custom sanitization SQL queries.
Running custom logic on every deploy
Logic that must run on every deploy - conditionally enabling modules,
running migrations, rebuilding derived data - is a DeployStep plugin in
a custom module, not a shell script. The
deploy_steps module discovers
these plugins and runs them around drush deploy:hook in every environment,
ordered by phase and then weight.
Declare a plugin in any enabled module's src/Plugin/DeployStep/
namespace:
namespace Drupal\my_module\Plugin\DeployStep;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\deploy_steps\Attribute\DeployStep;
use Drupal\deploy_steps\DeployStepBase;
use Drupal\deploy_steps\DeployStepInterface;
use Drupal\deploy_steps\DrushTrait;
use Drupal\deploy_steps\EnvironmentTrait;
#[DeployStep(
id: 'rebuild_search_index',
label: new TranslatableMarkup('Rebuild the search index'),
weight: 10,
phase: DeployStepInterface::PHASE_POST,
)]
final class RebuildSearchIndex extends DeployStepBase {
use DrushTrait;
use EnvironmentTrait;
public function skip(): ?string {
return $this->environment() === 'prod' ? 'production environment' : NULL;
}
public function run(): void {
$this->drush('search-api:index');
}
}
phaseselectsPHASE_PRE(before thedeploy:hookbody) orPHASE_POST(after it). A pre-phase plugin that enables modules lets the body run their run-oncehook_deploy_NAME()in the same deploy.weightorders plugins within a phase (lower runs first).skip()returnsnullto run, or a short reason to skip that is logged verbatim. ComposeEnvironmentTraitfor theenvironment()helper to gate by environment.run()is the idempotent work. ComposeDrushTraitfor thedrush()helper to run a long sub-command such asmigrate:importin its own memory-bounded, resumable subprocess.
Expand below for a complete deploy step you can copy as a starting point. It
mirrors the legacy provision-10-example.sh: it skips on production, enables
custom modules before the deploy:hook body, and branches on whether the
database was freshly imported (VORTEX_PROVISION_OVERRIDE_DB, which the
provision script exports before drush deploy:hook).
Example of a custom deploy step
<?php
declare(strict_types=1);
namespace Drupal\my_module\Plugin\DeployStep;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\deploy_steps\Attribute\DeployStep;
use Drupal\deploy_steps\DeployStepBase;
use Drupal\deploy_steps\DeployStepInterface;
use Drupal\deploy_steps\EnvTrait;
use Drupal\deploy_steps\EnvironmentTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Example deploy step: prepares a non-production environment on every deploy.
*
* The deploy_steps equivalent of the legacy `provision-10-example.sh` custom
* provision script. Copy it into any enabled module's `src/Plugin/DeployStep/`
* namespace and adapt it - or remove it. The deploy_steps runner discovers it
* and runs it around every `drush deploy:hook`.
*
* It runs in the PRE phase so the modules it enables have their run-once
* `hook_deploy_NAME()` fired by the `deploy:hook` body that follows.
*/
#[DeployStep(
id: 'example',
label: new TranslatableMarkup('Example environment setup'),
weight: 0,
phase: DeployStepInterface::PHASE_PRE,
)]
final class ExampleDeployStep extends DeployStepBase {
use EnvironmentTrait;
use EnvTrait;
/**
* The module installer.
*/
protected ModuleInstallerInterface $moduleInstaller;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->moduleInstaller = $container->get('module_installer');
return $instance;
}
/**
* {@inheritdoc}
*/
public function skip(): ?string {
// Run only in non-production environments. The value of
// $settings['environment'] is set in the Drupal settings file.
return $this->environment() === 'prod' ? 'production environment' : NULL;
}
/**
* {@inheritdoc}
*/
public function run(): void {
// Enable custom site modules. Idempotent: already-enabled modules are
// skipped. Their run-once deploy hooks fire in the deploy:hook body because
// this step runs in the PRE phase.
$this->moduleInstaller->install(['my_module_search', 'my_module_demo']);
// Conditionally act on a freshly imported database. The provision script
// exports VORTEX_PROVISION_OVERRIDE_DB before `drush deploy:hook`, so the
// step reads it here via getenv().
if ($this->env('VORTEX_PROVISION_OVERRIDE_DB', '0') === '1') {
// Fresh database detected - place one-off setup here.
}
}
}
Vortex ships two examples: the development and demo environment setup in
ys_base, and the content migration in ys_migrate.
For information about different types of Drupal hooks used during deployment and updates.
➡️ See Update Hooks