Skip to main content
Version: v1

Environment

Your local environment is a Docker Compose stack built from Lagoon container images, the images Lagoon runs in production. Ahoy wraps the longer docker compose commands into short ones you'll remember.

This section is for developers working day to day on a project built from Vortex. After reading this page, you can do the following:

  • Tell which files Git, the image build, and the deployment artifact leave out
  • Switch branches without a full rebuild
  • Reset the project to a clean state
  • Change an environment variable and get the new value into the containers

In this section​

Each layer of the stack has its own page in this section:

PageWhat it covers
DockerThe container stack and the docker-compose.yml anatomy
PygmyThe shared proxy, DNS, SSH agent, and mail catcher services
AhoyCommand shortcuts, configuration, and running CLI commands
DoctorStack diagnostics and system information
DrushThe Drupal CLI, its configuration, and aliases
LogsContainer logs and the Drupal watchdog log
DebuggingXdebug, authenticated requests, and container debugging
TroubleshootingCommon local stack failures and fixes

Ignored files​

Vortex writes its 3 ignore files as deny lists: nothing is excluded by default, and each file lists only what must stay out of its target. An allow list works the other way around: it excludes everything, and every addition has to be re-included explicitly. Deny lists follow the "avoid silent errors" principle, so a file you add is visible by default instead of silently disappearing until someone remembers to allow it.

The .gitignore file controls which paths Git ignores in the project repository; a file still reaches the repository only when you commit it. It excludes the following:

  • Directories that Composer and npm generate, such as web/core, web/modules/contrib, vendor, and node_modules
  • The Drupal scaffold files that the build copies into web/
  • Compiled theme assets in web/themes/**/build
  • With Storybook, the generated *.stories.json files and the storybook-static output
  • Local overrides: .env.local, .ahoy.local.yml, docker-compose.override.yml, settings.local.php, and services.local.yml
  • Content files and test artifacts in web/sites/*/files and web/sites/simpletest
  • Caches, data, logs, and temporary artifacts in .artifacts, .data, .logs, .phpunit.cache, and .twig-cs-fixer.cache

It keeps 2 sections as small allow lists because a deny rule can't express them:

  • recipes/ - Composer installs contributed recipes next to your custom ones, so each custom recipe is un-ignored explicitly.
  • .claude/ - Claude writes machine-local state here, so only .claude/settings.json and .claude/skills/ are un-ignored by default. This section is present when the installer adds the AI agent instructions.

The other 2 ignore files apply the same deny-list model to different targets:

A new file or directory that no entry matches is excluded from nothing. Git tracks it after you commit it, and the image build and the deployment artifact include it with no change to any ignore file. Add an entry to an ignore file only when a file must stay out of that particular target.

Switching branches​

Switching branches doesn't call for a full rebuild, which takes a while. Run only the commands that match what changed:

# Update Composer packages (only if composer.json/composer.lock changed)
ahoy composer install
# Rebuild frontend assets (only if theme files changed)
ahoy fe
# Provision site (only if database or configuration changes expected)
ahoy provision

If the branch changes docker-compose.yml or a file in .docker/, rebuild the images and recreate the containers with ahoy up --build, or with docker compose up -d --build without Ahoy.

Confirm that the stack is healthy: ahoy doctor ends with [ OK ] All required checks have passed.

Resetting​

ahoy reset takes the stack down and deletes what the build installed, so your next ahoy build starts clean. After you confirm, it runs ahoy down, which removes the containers together with their volumes, your local database included. It then deletes vendor, every node_modules directory, web/core, and the contrib directories in web/modules, web/profiles, and web/themes.

ahoy reset --hard goes further and returns the repository to a freshly cloned state at the current commit. On top of the soft reset, it deletes every untracked file that .gitignore matches and discards uncommitted changes to tracked files (git reset --hard). It then removes the remaining untracked files (git clean -f -d) and any empty directories.

That sweep takes your .env.local, the database dumps in .data, and every other local override with it, so copy out anything you want to keep first.

The following commands run each kind of reset:

# Reset local environment
ahoy reset
# Fully reset repository to a state as if it was just cloned
ahoy reset --hard

Confirm that the reset finished: the last line it prints is [ OK ] Finished reset.

Environment variables​

To change an environment variable in your local stack:

  1. Set the new value in .env.local for your machine only, or in .env for the whole team.
  2. Recreate the containers, so the variables that docker-compose.yml passes to them get the new value (a plain restart keeps the old one):
ahoy up

A value in .env.local overrides the one in .env, and a variable exported in your shell overrides both. ahoy up loads both files before it calls Docker Compose, but Docker Compose on its own reads only .env, so the Docker Compose tab's command applies only .env changes. To include .env.local without Ahoy, pass both files: docker compose --env-file .env --env-file .env.local up -d.

Drupal also reads variables that docker-compose.yml doesn't pass, such as DRUPAL_CLAMAV_ENABLED. For web requests, PHP-FPM loads them from .env when its container starts, never from .env.local. To apply a change to .env, run ahoy restart (or docker compose restart): ahoy up recreates a container only when its configuration changes, and these variables aren't part of it.

To set such a variable on your machine only, for example a credential that must stay out of the committed .env, pass it to the php service in docker-compose.override.yml. Then set its value in .env.local and run ahoy up:

docker-compose.override.yml
services:
php:
environment:
DRUPAL_EXAMPLE_API_KEY: ${DRUPAL_EXAMPLE_API_KEY:-}

Variables that only the vendor/bin/vortex-* scripts use, such as VORTEX_PROVISION_OVERRIDE_DB, take effect without recreating anything, because the scripts read both files each time they start. The Docker page explains how each value reaches a container.

Confirm that a container has the new value with docker compose exec cli printenv VARIABLE_NAME, where VARIABLE_NAME is a variable that docker-compose.yml passes, such as DRUPAL_THEME.

➡️ See Variables for the full variable reference.