Architecture
This page describes how the Vortex template is structured and how its components work together in your project.
Design principles
Vortex follows these principles:
- Simple is better than complex: The template encourages clarity, even in customizations.
- Use standard tools: Wherever possible, Vortex follows upstream conventions and avoids reinvention.
- Avoid silent errors: Misconfigurations should fail loudly.
- Readability counts: Code and configuration are meant to be understood.
- Explicit logging helps: Scripts log every major step, so you can follow what's going on.
Code lifecycle
Vortex follows the same code lifecycle in local, CI, and hosting environments, which reduces the chance of errors.
The lifecycle below covers the whole system, and each section of this page describes one of its stages:
Local Development
═════════════════════════════════════════════════════════════════════════════════════════
Developer writes code ──► Build and test locally ──► Commit changes
│
▼
Git Repository
═════════════════════════════════════════════════════════════════════════════════════════
Push to remote branch ──► Open/Update Pull Request
│
▼
┌─ CI Pipeline ────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─ Database Job (Nightly) ───────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Scheduled ──► Download production ──► Sanitize database ──► Store database cache │ │
│ │ trigger database Remove sensitive data │ │
│ │ │ │
│ └─────────────────────────────────────┬──────────────────────────────────────────────┘ │
│ │ Provides cached database │
│ ▼ │
│ ┌─ Lint Job ────────────┐ ┌─ Database Job ────────────────┐ ┌─ Audit Job ──────────┐ │
│ │ │ │ ◆ Nightly cache exists? │ │ Composer audit │ │
│ │ Build CLI container │ │ Yes ──► job succeeds │ │ (advisories) │ │
│ │ ▼ │ │ No ──► download, sanitize, │ │ ▼ │ │
│ │ Composer validate │ │ store cache │ │ Gitleaks │ │
│ │ ▼ │ └───────────────┬───────────────┘ │ (committed secrets) │ │
│ │ Composer normalize │ ▼ │ │ │
│ │ ▼ │ ┌─ Build Job ───────────────────┐ │ │ │
│ │ Hadolint │ │ Code assembly │ │ │ │
│ │ ▼ │ │ Docker, Composer deps, │ │ │ │
│ │ DCLint │ │ NPM deps, assets │ │ │ │
│ │ ▼ │ │ ▼ │ │ │ │
│ │ PHPCS │ │ Website setup │ │ │ │
│ │ ▼ │ │ Import cached DB, drush │ │ │ │
│ │ PHPStan │ │ deploy, custom scripts │ │ │ │
│ │ ▼ │ │ ▼ │ │ │ │
│ │ Rector │ │ Testing │ │ │ │
│ │ ▼ │ │ PHPUnit tests ──► Behat tests │ │ │ │
│ │ Twig CS Fixer │ └───────────────┬───────────────┘ │ │ │
│ │ ▼ │ │ │ │ │
│ │ Gherkin Lint │ │ │ │ │
│ │ ▼ │ │ │ │ │
│ │ ESLint / Stylelint │ │ │ │ │
│ │ │ │ │ │ │
│ └──────────┬────────────┘ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ │ │ │
│ ┌─ Deployment Job ─────────────────────────────────────────┐ │ │ │
│ │ │ │ │ │
│ │ Webhook Artifact Lagoon │ │ Does not gate │ │
│ │ Call URL Package artifact Run Lagoon CLI deploy │ │ deployment │ │
│ │ │ │ │ │
│ └──────────────────────────────────────────────────────────┘ └──────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────┘
│
▼
Hosting Platform
═════════════════════════════════════════════════════════════════════════════════════════
◆ Environment ──No──► Sync DB from production ───┐
exists? │
│ Yes ▼
└──────────────────────────────────► drush deploy ──► Custom scripts ──► Notifications
Available Environments
═════════════════════════════════════════════════════════════════════════════════════════
┊ PR Environment ┊ Dev Staging Production
┊ (auto-removed) ┊ develop branch main branch production branch or tag
Local development environment
Your project includes a containerized local environment that uses Docker Compose and production-grade Lagoon images, so local, CI, and hosting environments stay consistent.
Ahoy wraps complex tasks like provisioning, testing, or database imports in short, memorable commands.
Support for DDEV and Lando is on our roadmap for local development environments.
➡️ See Development > Environment
Automation scripts
Vortex uses POSIX-compliant Bash scripts shipped via the
drevops/vortex-tooling
Composer package (installed to vendor/drevops/vortex-tooling/src/) to
automate common tasks and connect components together.
These scripts:
- Run identically across local, CI, and hosting
- Support environment variables to adapt behavior
- Are modular and extensible
The scripts aren't part of the template's own files: they're a dependency.
Your project's composer.json requires drevops/vortex-tooling with a
tilde constraint (~1.4.0 in the shipped template), which accepts patch
releases but holds the minor version. As a result, a new minor release can't
reach your deployments until you raise the constraint yourself. Once
installed, every script is available as a Composer binary at
vendor/bin/vortex-<script> (for example vendor/bin/vortex-provision or
vendor/bin/vortex-deploy), and the Ahoy commands wrap those binaries.
The package repository on GitHub is a read-only mirror of the
.vortex/tooling/
directory of the Vortex repository. Bugs and changes go to the
Vortex issue queue, not to
the mirror.
Never edit the files under vendor/drevops/vortex-tooling/src/, because the
next composer update discards the change. Adapt behavior through
environment variables, and add project logic as your own scripts under
scripts/ (the provision script discovers and runs
scripts/provision-*.sh). Use a
cweagans/composer-patches
patch only when a shipped script itself must change.
➡️ See Development > Composer > Vortex tooling package for updating the package and Development > Provision > Running custom scripts for extending provisioning.
Customizing scripts
All scripts read their configuration from environment variables, so you can adapt them to a project or an environment.
During initial project setup, the .env file is updated with
project-specific values such as the project name and email. Secrets, tokens,
and other environment variables are then set in CI or hosting environments.
➡️ See Development > Variables
Router scripts
Most Vortex commands are implemented as router script entry points,
like vortex-fetch-db or vortex-deploy, that dynamically invoke the more
specific logic for your setup, based on configuration or environment
variables.
For example:
vortex-fetch-dbfetches a database from any supported hosting provider or a custom location, so the caller doesn't need to know how each provider works.vortex-deploydeploys code the same way to Acquia, Lagoon, or another hosting provider.
Script architecture diagram
Drupal management
Provisioning
The provisioning process is central to how Vortex works. Instead of manually running Drush commands, you run a single provision script. This script handles:
- Importing a database or initializing a database by installing Drupal from a profile
- Running updates, config imports, cache rebuilds, and deploy hooks
- Executing post-provision custom scripts
Because provisioning is centralized, it runs the same way locally, in CI, and in hosting.
You can also add automation around provisioning, such as running migrations conditionally or creating demo content.
➡️ See Development > Provision
Settings management
Vortex organizes Drupal settings per environment:
- The environment type (for example, local, CI, stage) is automatically
detected based on the environment provider settings located in
web/sites/default/includes/providers/. - Settings overrides are stored in
web/sites/default/includes/modules/with logic per environment inside each module-specific file.
Keeping each module's settings in its own file avoids config sprawl and lets you remove a module's settings cleanly when you no longer need them.
Vortex also includes tests that check these settings load correctly in each environment.
➡️ See Development > Settings
Module and theme scaffolds
Vortex includes an example custom module and theme, each with tests. They show how to:
- Structure custom features
- Write functional, kernel, and unit tests
- Connect theme assets to a build pipeline
Use these scaffolds as starting points for your own work.
➡️ See Development > Modules > Module scaffold and Development > Themes
Code quality and testing
Vortex ships with pre-configured tools for maintaining code quality:
- PHP CodeSniffer (PHPCS)
- PHPStan
- Rector
- Twig CS Fixer
- ESLint
- Stylelint
You'll also find scaffolds for:
- PHPUnit: Unit and kernel testing
- Behat: Behavior-driven testing with screenshot capture and extra steps
- Jest: JavaScript unit testing
➡️ See Development > Code quality and Development > Testing
Automated dependency updates
Vortex includes configuration for RenovateBot to automate dependency upgrades:
- Critical updates checked daily
- Regular updates run weekly
- Most new releases wait 14 days before they're proposed, so a registry can pull a compromised release first
- PRs include changelogs and pass through your continuous integration pipeline
You can host RenovateBot yourself or use the cloud version, and you can tweak the schedule as needed.
➡️ See Development > Dependency updates
Environment variables
Vortex uses environment variables extensively to configure behavior across local, CI, and hosting environments.
These variables are either defined in the .env file or set within
CI/hosting environments as secrets.
➡️ See Variables
Continuous integration workflows
Vortex supports GitHub Actions and CircleCI. You choose which one to use in your project. These pipelines include:
- Database fetch and caching (for faster builds)
- Full site provisioning in CI
- Running code quality checks
- Running unit, functional, and behavior tests
- Triggering deploys when tests pass
Each continuous integration configuration mirrors what happens locally and in hosting.
➡️ See Continuous Integration
Hosting integrations
Vortex has built-in support for Acquia and Lagoon hosting. These integrations:
- Trigger provisioning with the same steps used locally and in CI
- Provide deployment pipelines suited to each platform
You can use Vortex with other platforms too, but only Acquia and Lagoon have built-in integrations.
➡️ See Hosting
Documentation & onboarding
Vortex includes centralized documentation (what you're reading now), as well as a scaffold for adding project-specific docs within your own repository.
Testing the template
The template and its automation scripts are tested at multiple levels:
- Unit tests: Shell scripts are tested in isolation using Bats with mocked commands
- End-to-end tests: Full build pipelines are tested using PHPUnit in real Docker containers
- Real-world validation: The DrevOps website is a production reference site that receives regular upstream updates