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:
| Page | What it covers |
|---|---|
| Docker | The container stack and the docker-compose.yml anatomy |
| Pygmy | The shared proxy, DNS, SSH agent, and mail catcher services |
| Ahoy | Command shortcuts, configuration, and running CLI commands |
| Doctor | Stack diagnostics and system information |
| Drush | The Drupal CLI, its configuration, and aliases |
| Logs | Container logs and the Drupal watchdog log |
| Debugging | Xdebug, authenticated requests, and container debugging |
| Troubleshooting | Common 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, andnode_modules - The Drupal scaffold files that the build copies into
web/ - Compiled theme assets in
web/themes/**/build - With Storybook, the generated
*.stories.jsonfiles and thestorybook-staticoutput - Local overrides:
.env.local,.ahoy.local.yml,docker-compose.override.yml,settings.local.php, andservices.local.yml - Content files and test artifacts in
web/sites/*/filesandweb/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.jsonand.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:
.dockerignorecontrols the container image build context. ➡️ See Build context and.dockerignore.gitignore.artifactcontrols the deployment artifact. ➡️ See Deployment > Artifact
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:
- Ahoy
- Docker Compose
# 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
# Update Composer packages (only if composer.json/composer.lock changed)
docker compose exec cli composer install
# Rebuild frontend assets (only if theme files changed)
docker compose exec cli bash -c \
"cd \${WEBROOT}/themes/custom/\${DRUPAL_THEME} && npm run build"
# Provision site (only if database or configuration changes expected)
docker compose exec cli ./vendor/bin/vortex-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, the contrib directories in web/modules, web/profiles, and
web/themes, and the build directories of the custom 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:
- Ahoy
- Docker Compose
# Reset local environment
ahoy reset
# Fully reset repository to a state as if it was just cloned
ahoy reset --hard
# Reset local environment
docker compose down --remove-orphans --volumes
./vendor/bin/vortex-reset
# Fully reset repository to a state as if it was just cloned
docker compose down --remove-orphans --volumes
./vendor/bin/vortex-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:
- Set the new value in
.env.localfor your machine only, or in.envfor the whole team. - Recreate the containers, so the variables that
docker-compose.ymlpasses to them get the new value (a plain restart keeps the old one):
- Ahoy
- Docker Compose
ahoy up
docker compose up -d
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 appserver service
in docker-compose.override.yml. Then set its value in .env.local and run
ahoy up:
services:
appserver:
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.