Skip to main content
Version: v1

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-db fetches a database from the source in VORTEX_FETCH_DB_SOURCE: url (the default), ftp, s3, container_registry, acquia, or lagoon.
  • vortex-export-db exports the database to a dump file, or to a container image when VORTEX_DB_IMAGE is set.
  • vortex-import-db imports a dump file, and runs the import inside the cli container when you call it from the host.
  • vortex-deploy runs each deployment type listed in VORTEX_DEPLOY_TYPES (artifact, lagoon, or webhook), so the same call deploys to Acquia, Lagoon, or any host with a deployment webhook.
  • vortex-notify sends a notification to each channel listed in VORTEX_NOTIFY_CHANNELS: email, Slack, New Relic, GitHub, Jira, a webhook, or Diffy.
  • vortex-task runs a hosting operation (copy-db, copy-files, purge-cache, or custom) with the script for the platform in VORTEX_TASK_PLATFORM or VORTEX_PLATFORM, either acquia or lagoon.
Script architecture diagram
Diagram of scriptsDiagram of scripts

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-*.sh 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 provider settings files in web/sites/default/includes/providers/ detect the environment type: local, ci, dev, stage, or prod. The ENVIRONMENT_TYPE variable overrides the detected type. Set it in .env.local and run ahoy up for 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.json below 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

➡️ See Contributing > Maintenance > Template