Local environment
Audience: developers & AI agents · Scope: running and inspecting the site locally · Last reviewed: 2026-07-12
TL;DR: DDEV (Docker) is the local stack — MySQL + nginx, mirroring production (DDEV ships PHP 8.2,
prod targets 8.1). ddev start → https://betacalco.ddev.site. Reproducible and committed under
.ddev/README.md. See Running with DDEV.
Database is MySQL, prefix e5xae_, credentials in docroot/configuration.php (git-ignored); reach it with
ddev mysql. Migrations apply automatically on the next page load.
Don't work in the main checkout — work in a worktree.
tools/worktree.sh create <slug>gives a branch its own DDEV project, database, HTTPS host and copy of the runtime data, so migrations, settings changes and test runs can't reach this install. See worktrees.md. Everything on this page describes the stack itself and applies equally to a worktree.
MAMP PRO is gone. The old
https://betacalco.local:7890vhost is no longer used or supported — everything local runs on DDEV. If you still have the MAMP install, nothing here depends on it.
Running with DDEV (Docker)
The container stack mirrors production (PHP 8.2 — DDEV 1.25 dropped 8.1 — MySQL 8.0, nginx-fpm) and is fully committed,
so it's reproducible across machines. Full setup, commands, and the ionCube wrinkle are in
.ddev/README.md — the short version:
ddev start # build + start; post-start hook prepares dirs & config
ddev import-db --file=path/to/dump.sql # load a dump (prefix e5xae_)
ddev launch # open https://betacalco.ddev.site
Two things stay per-machine: docroot/configuration.php and a DB dump. On a fresh checkout the
post-start hook installs a DDEV-ready configuration.php from .ddev/configuration.ddev.php
automatically; an existing file is left untouched, so a config carried over from the old MAMP setup must
be replaced with that template (keeping its own $secret — see
.ddev/README.md). The file holds the container's values
directly; it does not switch on the environment.
The ionCube wrinkle (important). ConfigBox's five "brain" helpers (rules, calculation,
configurator, addon, shapediver) ship only as ionCube-encoded builds under
helpers/encoded/{13,14,15}/, and ionCube + Xdebug are incompatible on PHP 8.1–8.4. So the
container installs the ionCube 15 loader (production parity — the encoded bundles run) and
bind-mounts the plaintext sources from the sibling ../configbox-protected-files/protected_files/
to /var/protected_files. helpers/init.php checks /var/protected_files before the encoded
bundles, so when the sibling is present the engine runs as plaintext (live edits + Xdebug); when it
is absent it falls back to encoded/15 via ionCube. So the sibling is an optional bypass, not
required. (ionCube + Xdebug coexist — you just can't step into encoded code, which is why the
plaintext mount takes precedence.)
First-time setup (from zero)
Getting a fresh machine running. Pair with a teammate to obtain a database dump (and, if you don't want
the generated one, a configuration.php) — those hold secrets and aren't in git.
- Prerequisites — install Docker and DDEV. No
/etc/hostsedit is needed: DDEV managesbetacalco.ddev.siteand its TLS certificate itself. - Get the code — clone the repo.
docroot/is the Joomla document root. ddev start— builds and starts the containers. The post-start hook prepares directories and, whendocroot/configuration.phpis missing, installs a DDEV-ready one from.ddev/configuration.ddev.php(that file is git-ignored — it holds DB creds + secrets). Keep$sef/$sef_rewriteas in SEF / clean URLs below.- Database — load a dump with
ddev import-db --file=dump.sql. Thetools/scripts pull the DB and media from upstream — e.g.tools/get_from_live.sh/tools/get_website_db_dump.sh(read the script first; they contain environment-specific credentials). The prefix ise5xae_. - Verify —
ddev launch, or loadhttps://betacalco.ddev.site/. The first request auto-applies any pending migrations. A200means you're up.
Capture anything machine-specific you had to figure out into
notes/(and promote durable steps back into this section).
Web
- URL:
https://betacalco.ddev.site. DDEV issues a locally-trusted certificate (aftermkcert -install);curl -kstill works if your shell doesn't trust it. - Stack: DDEV (Docker) — nginx-fpm, PHP 8.2, MySQL 8.0; document root
docroot/. Config is committed under.ddev/. - Joomla: version 5.x (see
docroot/libraries/src/Version.php).
# Trigger a request (also applies any pending migrations)
curl -k -s -o /dev/null -w "HTTP %{http_code}\n" -A "Mozilla/5.0" "https://betacalco.ddev.site/"
Database
- Prefix:
e5xae_· Host: thedbcontainer (DDEV wires it up; nothing listens on the host). - Credentials:
docroot/configuration.php($user/$password/$db/$dbprefix). This file is git-ignored and.aiignore-d — don't commit it or paste secrets into docs.
# Read-only inspection — ddev mysql connects to the project database, no credentials needed
ddev mysql -e "SELECT \`key\`,\`value\` FROM e5xae_configbox_system_vars;"
The migration version state lives in
e5xae_configbox_system_vars(the columns are literally`key`and`value`). See migrations.md.
SEF / clean URLs
SEF is enabled in docroot/configuration.php: $sef = true, $sef_rewrite = true,
$sef_suffix = false — so URLs are clean (/path) with no index.php and no .html suffix.
This is required for the routing in sef-links.md to produce pretty paths.
Static assets & cache busting
Custom CSS/JS lives under data/customization/assets/. When not in debug mode the framework
serves the minified sibling (foo.min.css / foo.min.js) even though the view references the
plain source, and appends a ?version= cache-buster, e.g.
…/quote-landing-page.min.css?version=3.5.0-3.1.2.
That version is getApplicationVersion() (core ConfigBox, e.g. 3.5.0) + - + getReleaseNumber()
(the customization release), assembled in ConfigboxViewHelper::getCacheBusterValue()
(docroot/components/com_configbox/helpers/view.php). The core part only changes on a ConfigBox
upgrade, so the customization half is the one you bump.
To bump the cache-buster for customizations, increment the version string returned by
getReleaseNumber() in
docroot/components/com_configbox/data/customization/system_overrides/releasenumber.php:
function getReleaseNumber() {
return '3.1.3'; // was 3.1.2
}
This changes ?version= on every customization asset, so returning visitors refetch instead of
serving a stale cached copy.
Do this before any commit that touches static assets. Whenever you change a customization CSS/JS file, two steps are required:
- Regenerate the minified file — it's what's actually served. There's no build tool in the repo; use the installed minifier, e.g.
csso input.css -o input.min.css.- Bump
getReleaseNumber()so browsers don't keep the old cached asset.Skip either and the change won't reach users who already loaded the page.
Deploy & live-data sync
The tools/ directory holds shell scripts for pulling data from the live/staging servers and for
deploying. Examples (read the script before running it):
tools/get_data_from_live.sh,tools/get_from_live.sh,tools/get_website_db_dump.sh,tools/get_sql_dump.sh— fetch DB / files from upstream.tools/git-deploy scripts,tools/staging,tools/sync_to_excluded_paths— deployment helpers.
Deploy is git-push-based, not from
master. Pushing thefor_livebranch to the production bare repo triggerstools/git-deploy scripts/post-receive, which rsyncsdocroot/(minustools/sync_to_excluded_paths) to/var/www/betacalco.com, sets production perms, and clears APCu/opcode caches by curling thecom_configboxbccaches&task=clearCachesendpoint. The default working branch ismaster; feature work happens onfeature/*branches.
Excluded-from-AI/git paths worth knowing (see .aiignore): docroot/configuration.php and the
generated media caches under data/store/private/custom_media/{aws,sheets_json}/.
Gotchas
- Self-signed cert → every
curlneeds-k. - After editing a migration or adding
updates/<version>.php, just load any page to apply it; if a migration throws,failed_update_detectedis set and all further migrations are blocked until that flag is cleared (see migrations.md).