Architecture
Vortex is a production-ready Drupal project template designed to simplify onboarding, standardize processes, and ensure long-term maintainability.
This page provides a high-level overview of how the template is structured, how its components work together, and how your project benefits from its architecture.
Design principles
Vortex is guided by principles that prioritize simplicity, visibility, and maintainability:
- 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 it's easy to follow what's going on.
Code lifecycle
Vortex standardizes the code lifecycle across local, CI, and hosting environments. This ensures that the same steps are followed everywhere, reducing the chance of errors and making processes predictable.
The lifecycle below is the map of the whole system - every 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 using Docker Compose and production-grade Lagoon images. This ensures consistency between local, CI, and hosting environments.
To simplify command usage, we use Ahoy as a wrapper. It lets you run complex tasks like provisioning, testing, or database imports with short, easy-to-remember 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 easy to extend
The scripts are not part of the template's own files: they are 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, so a new minor release cannot 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/ - the next
composer update discards the change. Adapt behavior through environment
variables, add project logic as your own scripts under scripts/ (the
provision script discovers and runs scripts/provision-*.sh), and reach for 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 support configuration via environment variables, so they can be adapted to specific project or environment needs.
During initial project setup, .env file is updated with project-specific
values like project name, email etc. Then, environment variables (secrets,
tokens, etc.) are 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-dbis a router script that fetches a database from any supported hosting provider or custom location without needing to know the specifics of each provider.vortex-deployis a router script that deploys code to any hosting provider in a consistent manner, regardless of whether it's Acquia, Lagoon, or another platform.
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 in every environment: local, CI, or hosting. This eliminates "works on my machine" problems and makes the process predictable for everyone.
It also makes it possible to add more automation around provisioning, like conditionally running migrations or creating demo content.
➡️ See Development > Provision
Settings management
Vortex includes a structured way to manage Drupal settings per environment. Here's how it works:
- The environment type (e.g. 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.
This structure gives you clarity, avoids config sprawl, and lets you remove a module's settings cleanly when no longer needed.
Vortex also includes tests for these settings to ensure they are loaded correctly in each environment.
➡️ See Development > Settings
Module and theme scaffolds
We include examples of a custom module and theme, each fully integrated with tests. These show you 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
- 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 to ensure uniformity.
➡️ See Continuous Integration
Hosting integrations
Out of the box, Vortex supports 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 these are first-class 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
Vortex itself is tested at multiple levels to ensure the template and its automation scripts work correctly:
- 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 serves as a production reference site that receives regular upstream updates