Architecture
This page explains how the Vortex template fits together, for developers and leads who want the big picture before they work on a project or while they evaluate the template. It covers the principles behind it, the lifecycle your code follows from your machine to production, and the parts that handle each stage. Think of it as the map for the rest of the documentation: the sections link to the pages that cover each part in depth.
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 fail loudly. A script missing a required value stops and names the variable.
- 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 runs your code through the same lifecycle locally, in CI, and in hosting, with the same scripts at every stage. That reduces the chance of an error that appears in 1 environment and nowhere else.
The following diagram covers the whole system, and the sections after it take its stages one at a time:
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 ──► Import and export ──► Store database cache │ │
│ │ trigger database to validate the dump │ │
│ │ │ │
│ └─────────────────────────────────────┬──────────────────────────────────────────────┘ │
│ │ Provides cached database │
│ ▼ │
│ ┌─ Lint Job ────────────┐ ┌─ Database Job ────────────────┐ ┌─ Audit Job ──────────┐ │
│ │ │ │ ◆ Nightly cache exists? │ │ Composer audit │ │
│ │ Composer validate │ │ Yes ──► job succeeds │ │ (advisories) │ │
│ │ ▼ │ │ No ──► download, validate, │ │ ▼ │ │
│ │ Hadolint │ │ store cache │ │ npm audit │ │
│ │ ▼ │ └───────────────┬───────────────┘ │ (advisories) │ │
│ │ DCLint │ ▼ │ ▼ │ │
│ │ ▼ │ ┌─ Build Job ───────────────────┐ │ Gitleaks │ │
│ │ Build CLI container │ │ Code assembly │ │ (committed secrets) │ │
│ │ ▼ │ │ Docker, Composer deps, │ │ │ │
│ │ Composer normalize │ │ NPM deps, assets │ │ │ │
│ │ ▼ │ │ ▼ │ │ │ │
│ │ PHPCS │ │ Website setup │ │ │ │
│ │ ▼ │ │ Import cached DB, drush │ │ │ │
│ │ PHPStan │ │ deploy, custom scripts │ │ │ │
│ │ ▼ │ │ ▼ │ │ │ │
│ │ Rector │ │ Testing │ │ │ │
│ │ ▼ │ │ Jest ──► PHPUnit ──► Behat │ │ │ │
│ │ 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 runs locally in containers. Docker Compose starts a stack built from the Lagoon container images that Lagoon runs in production, the same ones CI runs, so your local environment matches CI and Lagoon hosting.
Ahoy wraps multi-step tasks like provisioning,
testing, and database imports in short commands such as ahoy provision
and ahoy test.
Docker Compose is the only local environment Vortex supports, so DDEV and Lando setups aren't covered. The roadmap lists support for both as a goal.
➡️ See Development > Environment
Automation scripts
Vortex automates its common tasks with Bash scripts from the
drevops/vortex-tooling
Composer package, installed to vendor/drevops/vortex-tooling/src/. They
also connect the components together.
The scripts share these traits:
- They run identically across local, CI, and hosting environments.
- They read environment variables to adapt their behavior.
- They're 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.5.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 the constraint is raised. A Vortex release
that changes the scripts raises it, and
Updating Vortex brings that change into your
composer.json.
The entry-point scripts install as Composer binaries at
vendor/bin/vortex-<script>, for example vendor/bin/vortex-provision or
vendor/bin/vortex-deploy, and the Ahoy commands wrap those binaries. The
scripts they hand work to, such as the one for each deployment type, stay
inside the package. Ahoy and CI install the package through
scripts/vortex-tooling.sh before the full composer install runs, so the
binaries are there from the first command.
The package's GitHub repository is a read-only mirror of the
.vortex/tooling/
directory in the Vortex repository. Report bugs and propose changes in
the Vortex issue queue, not
on the mirror.
Never edit the files under vendor/drevops/vortex-tooling/src/: the next
composer update discards your change. Adapt behavior through environment
variables instead, and put project logic in your own scripts under
scripts/. vortex-provision discovers and runs every
scripts/provision-*.sh file in alphabetical order. Reach for a
cweagans/composer-patches
patch only when a shipped script itself has to 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.
The installer writes your project's values into .env during setup, such as
the project machine name and the notification email addresses. Secrets,
tokens, and per-environment values go into your CI or hosting environment
instead, and local overrides go into .env.local, which Git ignores.
➡️ See Development > Variables
Router scripts
Vortex has 6 router scripts: entry points that read your configuration and hand the work to the script for your setup. The caller doesn't need to know how each setup works. Each router picks its script as follows:
vortex-fetch-dbfetches a database from the source inVORTEX_FETCH_DB_SOURCE:url(the default),ftp,s3,container_registry,acquia, orlagoon.vortex-export-dbexports the database to a dump file, or to a container image whenVORTEX_DB_IMAGEis set.vortex-import-dbimports a dump file, and runs the import inside theclicontainer when you call it from the host.vortex-deployruns each deployment type listed inVORTEX_DEPLOY_TYPES(artifact,lagoon, orwebhook), so the same call deploys to Acquia, Lagoon, or any host with a deployment webhook.vortex-notifysends a notification to each channel listed inVORTEX_NOTIFY_CHANNELS: email, Slack, New Relic, GitHub, Jira, a webhook, or Diffy.vortex-taskruns a hosting operation (copy-db,copy-files,purge-cache, orcustom) with the script for the platform inVORTEX_TASK_PLATFORMorVORTEX_PLATFORM, eitheracquiaorlagoon.
Script architecture diagram
Drupal management
Provisioning
Provisioning is central to how Vortex works. Instead of running Drush
commands by hand, you run 1 script, vortex-provision (ahoy provision
locally), and it handles:
- Importing a database dump, or installing Drupal from a profile instead
- Running updates, config imports, cache rebuilds, and deploy hooks
- Sanitizing a freshly imported database
- Running your custom
scripts/provision-*.shscripts
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 provider settings files in
web/sites/default/includes/providers/detect the environment type:local,ci,dev,stage, orprod. TheENVIRONMENT_TYPEvariable overrides the detected type. Set it in.env.localand runahoy upfor your local stack, or set it in your hosting environment's variables. - Each module's settings live in their own file in
web/sites/default/includes/modules/, with the per-environment logic inside that 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.
PHPUnit tests in tests/phpunit/Drupal/ check that 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. Together, they show the following practices:
- Structuring custom features
- Writing functional, kernel, and unit tests
- Connecting 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
- Gherkin Lint
- Hadolint
- DCLint
- SDC Devel
Vortex also ships scaffolds for these test frameworks:
- PHPUnit: Unit, kernel, and functional 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 Renovate to automate dependency upgrades:
- Drupal core minor and patch updates are checked daily, with no waiting period, so security releases arrive on the next run
- Container images, GitHub Actions, and the CI tools are checked daily, and the other PHP and JavaScript packages weekly, as is the Renovate image that CircleCI runs
- Most new releases wait 14 days before they're proposed, so a registry can pull a compromised release first
- Major versions of PHP and JavaScript packages are left for you to upgrade by hand
- The PHP, Node.js, and npm versions aren't updated, and neither are the
packages in any
package.jsonbelow the project root, such as the theme's - PRs include changelogs, pass through your continuous integration pipeline, and can merge automatically after it's green
Renovate runs as the hosted GitHub app or self-hosted in your CI, and the
installer asks which. The schedules live in renovate.json, so you can
tweak them.
➡️ See Development > Dependency updates
Environment variables
Vortex relies on environment variables to configure its behavior across
local, CI, and hosting environments. You define them in the .env file or
set them in your CI and hosting environments, which is where secrets belong.
➡️ See Variables
Continuous integration workflows
Vortex supports GitHub Actions and CircleCI, and the installer asks which one your project uses. Both pipelines include the following:
- Database fetching and caching, for faster builds
- Full site provisioning
- Code quality checks
- Unit, functional, and behavior tests
- Deployment after the tests pass
- Security audits of Composer and npm packages and a Gitleaks secret scan, in a workflow of their own
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 do the following:
- Trigger provisioning with the same steps used locally and in CI
- Provide deployment pipelines suited to each platform
Other platforms work too, through the artifact and webhook deployment
types, but only Acquia and Lagoon have built-in integrations.
➡️ See Hosting
Documentation & onboarding
Besides this documentation site, Vortex adds a docs/ scaffold to your
repository for the project-specific decisions, unless you answer No to
the installer's Preserve project documentation? prompt. The scaffold has
a page for each of these topics:
- Testing conventions
- CI, when the project has a CI provider
- Deployment, when the project has a deployment type
- Releasing
- FAQs
The README.md that Vortex writes for your project includes local
setup steps for new team members.
Testing the template
We test the template and its automation scripts at 3 levels:
- Unit tests: Bats tests run each shell script in isolation, with external commands mocked
- End-to-end tests: PHPUnit tests run full build pipelines in real Docker containers
- Real-world validation: The DrevOps website is a production reference site that receives regular upstream updates