1b.1 part 4 — D11 backend deploy scope (SimpleSAMLphp / NetBadge)
Raised: 2026-07-13 (afternoon, Yuji). Continues the 1b.1 part 4 work whose app-repo half merged in PR #32. This doc records a scoping finding that reshapes the remaining part-4 terraform/Ansible work.
TL;DR — The morning plan assumed a dsf-shaped D11 deploy already existed in
terraform-infrastructure/mandala/drupal/*and that part 4 was just cloning the netbadge/SimpleSAMLphp config into it (+ one ALB cleanup). It does not exist. That terraform env still describes the legacy D7 Aegir stack. The whole dsf-style D11 AWS backend deploy is greenfield, and the SimpleSAMLphp SP is one component of it — it cannot be built in isolation. This doc scopes the D11 backend deploy end-to-end and folds netbadge + the ALB cleanup into it.
See also: deferred note saml-alb-routing-assumes-mod-shib.md
(the ALB half + the user-provisioning test matrix), ADR 014 (Redis visibility
token), the reference deploy pattern (dsf.library.virginia.edu/staging/ansible/).
1. The gap (what's actually there vs. what the pipeline expects)
The D11 CI/CD pipeline is already written and points at this terraform env:
pipeline/buildspec.yml→ buildsuvalib/mandala-drupalfrompackage/Dockerfile, pushes to ECR, records the tag in SSM/mandala/drupal/build_tag.pipeline/deployspec.yml→cd terraform-infrastructure/mandala/drupal/$ENVIRONMENT→terraform apply→ansible-playbook ansible/deploy_backend.yml -e deploy_tag=$DEPLOY_TAG.
But terraform-infrastructure/mandala/drupal/{staging,production} is the D7
Aegir environment:
ansible/configure_backend.ymlonly provisions the host (Docker, EBS mount, fail2ban ban-list reload, cache-warm timers). It deploys no app container — Aegir'sdockerfiles-hostmaster-1pulls the D7 sites itself.- There is no
deploy_backend.yml(the deployspec calls a file that does not exist), nocontainer_0.env.{generated,managed,secret}split, no SimpleSAMLphp Ansible assets, no SAML keys. production/alb-routing.tfstill carries 5 deadmod_shibrules (public-0-auth-0..4→authproxy_target_arn,/Shibboleth.sso/*).- The staging/dev hosts are named
mandala-drupal-dev-{0,1}.internal.lib.virginia.edu(staging env == the "dev" instance, per Yuji) but run the Aegir stack.
Second gap — the app image (smaller than first thought). The SimpleSAMLphp
library is already vendored by composer install — it's a dependency of
drupal/simplesamlphp_auth ^4.1 (in mandala's composer.json), installed via
simplesamlphp/composer-module-installer. Confirmed identical to dsf/library,
which are also built from the stock official Drupal image
(public.ecr.aws/docker/library/drupal:10.6.7 / :10.6.10) and likewise install
no standalone SP. So no "bake the SP into the image" work is needed.
What package/Dockerfile is actually missing is the Apache vhost that
dsf/library ship (package/data/…/000-default.conf):
ProxyPass "/simplesaml/" "http://sp:80/simplesaml/"
ProxyPassReverse "/simplesaml/" "http://sp:80/simplesaml/"
SetEnv SIMPLESAMLPHP_CONFIG_DIR /var/simplesamlphp/config
mandala's Dockerfile already runs a2enmod proxy proxy_http headers — it was set
up for exactly this and just never got the vhost. Correction to the "SP runs
inside the Drupal container" framing: the SP library runs in-process in Drupal
(for simplesamlphp_auth session validation), but the /simplesaml/* web
endpoints are served by the separate sp/netbadge-0 container
(uvalib/drupal-netbadge) and reverse-proxied to it. The two act as one SP because
they share the mounted /var/simplesamlphp/config and the Redis session store.
Consequence: the netbadge/sp container is required in every environment, not
a dev-only test IdP (enabling example-userpass is the only dev-specific part).
2. Target architecture (the dsf model)
Single Drupal container, SP embedded — verified from dsf.library.virginia.edu:
- Two containers on the
drupalnetnetwork:mandala-drupal-0(uvalib/mandala-drupal,8080:80→ ALB target group) andnetbadge-0(uvalib/drupal-netbadge, aliassp,8081) which serves the/simplesaml/*web endpoints. - The SP is split across both containers, unified by shared config + Redis
session store. The Drupal container's Apache reverse-proxies
/simplesaml/→http://sp:80/simplesaml/;simplesamlphp_auth(with the composer-vendored library) validates the session in-process viaSIMPLESAMLPHP_CONFIG_DIR.deploy_backend.ymlmounts from the host into the Drupal container: …/simplesamlphp/config→/var/simplesamlphp/config(authsources.php,config.php)…/simplesamlphp/metadata→/var/simplesamlphp/metadata(saml20-idp-remote.php— UVA IdP)…/simplesamlphp/cert→/var/simplesamlphp/cert(SAML.pem/.crt)…/simplesamlphp/drupal-config→/var/simplesamlphp/drupal-config(simplesamlphp_auth.settings.yml)- Post-boot:
drush cim -y --partial --source=/var/simplesamlphp/drupal-configloads the SAML Drupal config;apache2ctl configtest+ reload. /simplesaml/*and/saml_loginare served as ordinary app paths on the normal Drupal target — no dedicated ALB SAML rules (dsf has zero). This is why the 5public-0-auth-*rules are dead weight and get deleted.deploy_netbadge.ymldeploys thatsp/netbadge-0container. It is required in every environment (it serves/simplesaml/*), not a dev-only add-on. Its only dev-specific aspect is enablingSIMPLESAML_ENABLE_EXAMPLE_AUTH=true(example-userpasstest IdP) so validation needn't hit real UVA NetBadge.- Config is env-driven via a 3-file split loaded by the playbook:
container_0.env.generated(terraform-templated, e.g.MYSQL_HOST) +.managed(hand-managed non-secrets:SIMPLESAML_*, Redis) +.secret(.cpt-encrypted: salts, admin pw).
3. Component inventory — exists / missing
| Component | dsf reference | mandala state | Action |
|---|---|---|---|
| SP library in app image | via composer (simplesamlphp_auth) |
already vendored by composer install |
none — already present |
Apache vhost ProxyPass /simplesaml/ → sp:80 |
000-default.conf |
missing (a2enmod done, no vhost) |
Add the vhost to package/Dockerfile |
sp/netbadge-0 container (serves /simplesaml/*) |
uvalib/drupal-netbadge |
missing | Deploy via deploy_netbadge.yml — all envs |
ansible/deploy_backend.yml (app deploy) |
deploy_backend_0.yml |
missing (deployspec calls it) | Create from dsf, adapt drupal_home=/opt/drupal/app/drupal, image/container names |
ansible/configure_backend.yml (host provision) |
present (both) | present but Aegir-flavored | Adapt: drop Aegir/hostmaster bits; keep EBS/docker/fail2ban/cache-warm as applicable |
container_0.env.{generated,managed,secret} |
present | missing (only empty container.env) |
Create the 3-file split |
files/var/simplesamlphp/config/authsources.php |
present (env-driven) | missing | Clone verbatim (already env-driven) |
files/var/simplesamlphp/config/config.php |
(in image) | — | Confirm where store/Redis config is set |
files/var/simplesamlphp/metadata/saml20-idp-remote.php |
UVA IdP | missing | Clone verbatim (UVA urn:mace:incommon:virginia.edu) |
files/var/simplesamlphp/drupal-config/simplesamlphp_auth.settings.yml |
dsf mapping | app-repo CMI has mandala's version | Reuse the committed mandala CMI (uid→username, register_users:false baseline) |
SAML keys keys/mandala-drupal-saml-staging.{crt,pem.cpt} |
encrypted .pem.cpt + .crt |
✅ DONE (2026-07-13, committed) — reused the live SP pair off dev-0, crypt-key.ksh reusing the mandala-drupal-staging.pem secret |
Cert is expired (renew later — deferred saml-sp-cert-expired-renewal.md) |
deploy_netbadge.yml test-IdP sidecar |
present | missing | Clone (dev only; SIMPLESAML_ENABLE_EXAMPLE_AUTH=true) |
| Redis DB/prefix separation | db 3, SIMPLESAML_DSF: |
— | Pick distinct SIMPLESAML_REDIS_DATABASE + SIMPLESAML_MANDALA: prefix (≠ ADR 014 mandala_solr_fq:{uid}) |
ALB: delete 5 public-0-auth-* rules |
n/a (dsf has none) | present in production only | Delete (task 1); terraform plan first |
| Terraform host/target/RDS/Redis for D11 | dsf single-container | Aegir hosts + D7 DB | Decide (see §5) — biggest open item |
4. Work breakdown (folds in netbadge + ALB cleanup)
- App image — add the Apache vhost (
ProxyPass /simplesaml/ → http://sp:80/,ProxyPassReverse,SetEnv SIMPLESAMLPHP_CONFIG_DIR /var/simplesamlphp/config) topackage/Dockerfile, mirroring dsf/library's000-default.conf. The SP library is already vendored by composer;a2enmod proxy proxy_http headersis already done — this is just the vhost. - Ansible app deploy — create
mandala/drupal/<env>/ansible/deploy_backend.ymlfrom dsf'sdeploy_backend_0.yml; adapt paths (drupal_home=/opt/drupal/app/drupal),image_name=uvalib/mandala-drupal,container_name=mandala-drupal-0, ports, the SP mounts, and the post-bootdrush cim --partial. - Container env split —
container_0.env.generated(terraform template:MYSQL_HOST, DB creds ref),.managed(SIMPLESAML_*, Redis db/prefix, base URL/entityID for the dev SP),.secret(.cpt)(SIMPLESAML_SECRET_SALT,SIMPLESAML_ADMIN_PASSWORD,HASH_SALT). - SP assets — clone
authsources.php,saml20-idp-remote.phpverbatim; drop in the app-reposimplesamlphp_auth.settings.yml. ✅ SAMLkeys/done (committed 2026-07-13). - Test-IdP sidecar —
deploy_netbadge.ymlfor dev (example-userpass), so validation (part-4 §"outside-DDEV") can run without real NetBadge. - ALB cleanup (task 1) — ⏸ POSTPONED 2026-07-14; Yuji forwarding to Dave.
Delete the 5
public-0-auth-*rules inproduction/alb-routing.tf;terraform planto confirm listener-rule priorities;authproxycomponent stays (Solr proxies use it). Not a prerequisite for item 7 — see §4a. - Validation — the part-4 §"outside-DDEV" run + the 5-row provisioning matrix
from the deferred note (NetBadge/test-IdP → session →
/oauth/authorizeno-reprompt → tokensub=uid → proxy Redis read;register_usersfalse/true).
4a. Build-pass status (2026-07-14)
Items 1–5 are drafted and statically verified; items 6–7 remain (both need AWS
access). Item 1 landed in this repo; items 2–5 are files in
terraform-infrastructure/mandala/drupal/staging/.
| §4 item | Status |
|---|---|
1. App image /simplesaml/ vhost |
✅ done — package/data/files/etc/apache2/sites-available/000-default.conf + package/Dockerfile |
2. ansible/deploy_backend.yml |
✅ drafted — --syntax-check passes |
| 3. Container env 3-file split | ✅ drafted — terraform templating hook still pending (§5.2) |
| 4. SP assets | ✅ drafted — PHP -l clean; SAML keys already done |
5. deploy_netbadge.yml sidecar |
✅ drafted — --syntax-check passes |
| 6. ALB cleanup | ⏸ POSTPONED (2026-07-14) — Yuji forwarding to Dave; not a prerequisite for item 7, see below |
| 7. Validation run | ⬜ needs a deployed env |
Item 6 is postponed and does not gate item 7. Deleting the 5 public-0-auth-*
rules is hygiene rather than a prerequisite: they live only in the production
terraform env (part 4 targets the dev env inside the staging configs), they match
/user/netbadge + /Shibboleth.sso/* — paths the SimpleSAMLphp SP never uses, since
it serves /simplesaml/* and /saml_login — and live production already 404s
/Shibboleth.sso while NetBadge works, so they are demonstrably inert. Validation can
run with them still in place. See the deferred note for the full evidence.
Verified, not assumed: the vhost mechanism was checked by building the
COPY+sed layers and confirming the docroot rewrite, apache2ctl configtest
(Syntax OK) and the loaded proxy modules; PHP assets pass php -l; both
playbooks pass ansible-playbook --syntax-check; the env files parse as flat
YAML scalars and satisfy every required_env_vars; SIMPLESAML_PROJECT/_ENV
resolve to the committed mandala-drupal-saml-staging.{crt,pem.cpt}.
Where mandala must diverge from dsf — do not "fix" these back to the dsf shape:
- The vhost goes in
sites-available/, notsites-enabled/. The stock image shipssites-enabled/000-default.confas a symlink intosites-available/, and our Dockerfile'ssed(which sets the docroot) only globssites-available/*.conf. Writing dsf's file tosites-enabled/replaces the symlink with a regular file the sed never sees, strandingDocumentRootat the literal/var/www/html. dsf gets away with it only because it symlinks/var/www/html → /opt/drupal/web; we useAPACHE_DOCUMENT_ROOTinstead. DRUPAL_HASH_SALT, not dsf'sHASH_SALT. Our committedsettings.phpreadsgetenv('DRUPAL_HASH_SALT')and silently falls back to'temporary-local-dev-only'. Cloning dsf's variable name would leave that dev fallback live in a deployed environment.global/playbooksis four levels up, not three — mandala sits atmandala/drupal/<env>/ansible, one deeper than dsf's<site>/<env>/ansible. (The existingconfigure_backend.yml/idle_backend.ymlalready use../../../../.)- No
configbind mount. dsf mounts a host config dir because its image symlinks config out of a git checkout. Mandala bakes the committed CMI baseline into the image at/opt/drupal/app/drupal/config/sync; a bind mount would shadow it. - Redis must be genuinely env-driven. dsf's
config.phphardcodesstore.redis.hostandstore.redis.database => 8, which makes itsSIMPLESAML_REDIS_DATABASEenv var dead code. Mandala has three Redis consumers (§5.3), so ours reads both from the environment. - Build-tag SSM parameter mismatch. The app repo's
buildspec.ymlpublishes to/mandala/drupal/build_tag; dsf's playbook reads the house/containers/<image>/latest.deploy_backend.ymlcurrently follows the app repo (the thing that exists), and this is only thedeploy_tag=latestfallback sincedeployspec.ymlpasses an explicit tag — but the two conventions should be reconciled deliberately. - Added
mandala/.gitignorewith*.secret. dsf is protected by its owndsf.library.virginia.edu/.gitignore; the root patterns (*/*.secret,*/*/*.secret) do not reach mandala's deeper path, so a plaintextcontainer_0.env.secretholding real salts would otherwise be committable.
Remaining CHANGE_ME placeholders before any deploy: SIMPLESAML_SECRET_SALT,
SIMPLESAML_ADMIN_PASSWORD, DRUPAL_HASH_SALT (fill + encrypt to
.secret.cpt via crypt-key.ksh), and MYSQL_PASSWORD (terraform, §5.2). Both
playbooks assert on CHANGE_ME, so a premature deploy fails loudly rather than
coming up against the wrong database or a dev hash salt.
5. Open decisions (need Yuji + Dave Goldstein) — the real blockers
These gate the terraform half and are not ours to default:
- Host strategy — DECIDED (2026-07-13, Yuji): replace in place. The D11
instance replaces the
mandala-dev(staging-env) instance; there is no D7 dev instance to keep. We edit the existingmandala/drupal/stagingterraform env into the D11 shape (not a new env). Requirement: audit the old Aegir dev env for every unique component and account for each before cutover — see §6. - RDS — RESOLVED 2026-07-14: nothing to provision, and NOT a Dave dependency.
Correction (2026-07-14). This item was listed as "settle with Dave (RDS provisioning)". That was wrong, on evidence: - The secret terraform needs —
staging/rds/standard/mandala_drupal— has existed since 2021 and is readable by Yuji's aws-vaultstagingprofile (verified viaaws secretsmanager describe-secret). Note the path isrds/standard/, not dsf'srds/mysql8/— that segment is just naming drift;staging/datastores/databases/applies against onemysql_endpoint. -staging/datastores/databases/mandala_drupal.tfis the Aegir pattern: itsmysql_databaseresource is commented out (Aegir created databases on demand), and themandala_drupalaccount instead holdsALLonmandala%andsite_%, plusCREATE USER/GRANT OPTION. So a D11 database namedmandala_drupal_0is already covered by the existing grant — no new user, no new secret, no new grant, no DBA request. Creating the database is self-serve with those credentials. - The wiring is done:ansible.tfnow renderscontainer_0.env.generatedfrom the secret (count = 1— node 0 only, since dev-1 is the D7 migration source). Proven with a realterraform plan: 1 to add, 0 to change, 0 to destroy,content = (sensitive value). -MYSQL_USERismandala_drupal, not the database name. dsf's convention has user and database share a name; mandala's is one account across manymandala%databases. Do not align them.What is still open here: §5.6 (ALB target port/health check) and §5.3 (Redis).
The
durable fact: mandala already lives on rds-mysql8-staging.internal.lib.virginia.edu
(MySQL 8, the same RDS dsf uses; matches ADR 012), user mandala_sites_dev.
No new RDS needed — D11 just needs a database on it. The specific *_dev
databases are throwaway development data (not the migration source; Yuji) — no
need to reuse a particular one; a fresh consolidated D11 DB is fine. Migration
D7 source = the staging instance (dev-1), handled by the separate migration
track (deferred staging-migration-execution-prerequisites.md); the shared
user DB for that track is mandala_shared_* (see
[[project-d7-shared-user-database]]). The Aegir control DB (local mariadb
container) is irrelevant — Aegir is retired.
Migration strategy (Yuji, 2026-07-13): any migration we run now is not the final migration. Staging lags production by several months, but the shape of the data is essentially the same — little that's structurally significant is absent from staging. So working against staging now is sufficient to prove the migration and drive D11 development. A separate production migration must be planned as future work (D7 prod → D11 prod cutover) — see deferred
production-migration-planning.md.
- Redis — DEV RESOLVED 2026-07-14; enterprise/production deferred.
Correction + decision (2026-07-14). This item was listed as "settle with Dave". The dev half needed nobody. Dev = a
redis:alpinecontainer ondrupalnet(deploy_redis.yml), network aliasredis— a development box does not need an enterprise instance (Yuji), and reindeer_x already runs its own Redis on the box. Where the enterprise/production store resides is explicitly deferred (Yuji) — see deferredredis-enterprise-store-location.md.Two things this correction fixed: - Mandala defines no Redis at all in terraform, and nothing under
mandala/referencedha-redis-staging— that host was wrongly copied from dsf intocontainer_0.env.managedduring the build pass, then removed. The fleet cluster is real (dsf / library.virginia.edu / avalon use it) but was never mandala's. - ADR 014's visibility-token Redis had no production host either:mandala_solr_visibilityshipsredis_host: redis(DDEV's service name) with a comment to override per-environment, and that override was never written.The alias is deliberately
redis— matching DDEV and the module's installed default — so the dev box mirrors local DDEV and ADR 014 needs nosettings.phpoverride. Database separation is genuine because two consumers share the instance: db 0 = ADR 014 tokens (VisibilityTokenStorenever calls->select()), db 4 = SimpleSAMLphp sessions (SIMPLESAML_MANDALA:). reindeer_x'sworkqueueis a separate container on its own network — untouched.solr-proxystill defaultsREDIS_HOST=127.0.0.1and must be set torediswhen it is deployed to this box.
(Original framing:) (a) ADR 014
mandala_solr_fq:{uid} visibility token; (b) SimpleSAMLphp session store
(SIMPLESAML_STORE_TYPE=redis); (c) reindeer_x's workqueue (live on the
box as its own redis container). Decide per-consumer: shared cluster with
separate DB/prefix each (dsf shares ha-redis-staging) vs. dedicated. At minimum
SimpleSAMLphp must not collide with ADR 014's keyspace.
4. ~~Single node vs. HA pair~~ — RESOLVED (Yuji): the two nodes are two
logical environments, not an HA pair. dev-0 = development, dev-1 =
staging (the D7 migration source). So "replace mandala-dev with D11" = replace
dev-0; dev-1 stays D7 as the migration source until that track completes.
The terraform "staging" env's instance_count=2 is really dev + staging, not a
redundant pair — the D11 rework must keep that dual-env split (or split them into
proper separate envs).
5. Dev SP entityID / base URL. D7 default-sp lived on
mandala-dev.internal.lib.virginia.edu (existing dev SP available). Reuse that
entityID/ACS for the D11 dev SP, or register a new one? (Dave / ITS NetBadge.)
6. ALB target port. Repurposing target-0 to the D11 container's 8080 — confirm
the target group / health check path is right for D11.
Recommended next step — SUPERSEDED 2026-07-14. §5.2 turned out to need nobody
(see the correction above), and §5.5/ITS is not required for validation at all —
the example-userpass test IdP exists precisely so item 7 can run without real
NetBadge. The remaining path to a validating dev env is largely self-serve: create
the mandala_drupal_0 database, fill the three CHANGE_ME secrets and encrypt to
.secret.cpt, terraform apply, then run deploy_backend.yml +
deploy_netbadge.yml. Dave is genuinely needed only for §5.3 Redis sign-off
(confirm db 4 is unclaimed on ha-redis-staging) and the postponed production
ALB deletion. The one substantial piece still unwritten is the D11 terraform
host/target rework (§3's biggest open item, §5.1/§5.6) — ours, not his.
(Original, now historical:) settle §5.2–5.3 with Dave (RDS/Redis) before writing terraform; the Ansible + SP assets (§4 items 2–5) and the image work (§4.1) can be drafted in parallel since they're env-value-driven and don't depend on those. §5.1 (host strategy) is decided — replace in place (see §6).
6. Old dev-instance component audit (DECIDED: D11 replaces mandala-dev)
Decision (2026-07-13, Yuji): the D11 instance replaces the mandala-dev
instance in place — no D7 dev instance is retained. Every unique component of
the old dev instance must be accounted for before cutover (carry over / replace /
consciously drop).
Three sources of truth — the terraform audit is not enough
The old dev box's configuration is spread across (Yuji, 2026-07-13):
- Terraform (
mandala/drupal/staging/*) — audited below. Captures infra: instance, DNS, ALB routing, SGs, EBS, IAM. - Hand-installed drift on the live instance — packages/config/scripts applied
directly, not in any IaC. Only discoverable by inspecting the running box
(
mandala-drupal-dev-{0,1}.internal.lib.virginia.edu) — needs SSH + a diff against what terraform/ansible/the git repo would produce. - A git repo — the legacy Aegir/Docker deploy:
github.com/shanti-uva/mandala_drupal_docker(docker-compose,build/,scripts/,volumes/,env.dist), plus siblingsmandala-solr-proxy,mandala_s3_synch,kmaps_engine(all undermandala-legacy/locally). Must be diffed against the live box to separate repo-tracked config from hand drift.
Accounting is complete only when all three are reconciled. Items 2–3 are the risk; item 1 is the easy part.
Live-box audit — mandala-drupal-dev-0 (2026-07-13, read-only SSH)
Inspected the running instance directly (ip-10-130-109-110, AL2023). The box is a
hybrid: the legacy D7 Aegir stack and the new D11-era sync service run side by
side, all as hand-placed git checkouts under /usr/local/, started manually
(no systemd management except the two mandala-* timers already in
configure_backend.yml).
Running containers (docker ps):
| Container | Image | Ports | Compose project / dir |
|---|---|---|---|
dockerfiles-hostmaster-1 |
mandala/mandala_drupal_docker |
8080→80, 8222→22 |
dockerfiles — /usr/local/dockerfiles |
dockerfiles-database-1 |
mariadb |
3306 (internal) |
dockerfiles (same) |
mandala-solr-proxy |
mandala-solr-proxy-php-proxy |
8765→80 |
mandala-solr-proxy — /usr/local/mandala-solr-proxy |
reindeer_x |
reindeer_x |
9000/tcp, 9001/udp |
kmapssolrsync — /usr/local/kmaps-solr-sync-out |
workqueue |
redis |
6379 (internal) |
kmapssolrsync (same) |
Findings that change the audit:
:9001is reindeer_x, and it's UDP. reindeer_x's app port is9000/tcp(PORT=9000,npm run reindeer_x:dev,NODE_ENV=development,KMAPS_SYNC_CLASS=staging,REDIS_URL=redis://workqueue). The terraformrdx_service_port=9001/HTTP target +mandala-rdx-devCNAME + IP-allowlist SG are stale relative to what actually runs — the D11 replacement (reindeer_x, ADR 006/007) is already deployed here, not a future item. Reconcile the ALBrdx/indextargets against reindeer_x's real9000/tcp(+9001/udp) at cutover.- DB (corrected via
drush sql-connect, notdocker ps): the D7 site data is on the sharedrds-mysql8-staging(MySQL 8), usermandala_sites_dev— the localmariadbcontainer is only Aegir's control DB, which is retired and irrelevant (Yuji). → §5.2: D11 reuses this RDS, no new instance. Thedev-0*_devdatabases seen here are throwaway dev data, not the migration source. dev-0= development,dev-1= staging (Yuji). The two nodes in the terraform "staging" env are two logical environments, not an HA pair. This box (dev-0) is the one D11 replaces;dev-1(staging) is the D7 migration source. → §5.4.- Persistent EBS =
/mnt/docker(the Docker data-root;/dev/nvme1n1, 70 G/100 G used). "Persistent data" = docker overlay2 + volumes (var-aegir, mariadb data). StillBackupPolicy=none. - Hand-drift specifics to port/reconcile:
/usr/local/dockerfiles=shanti-uva/mandala_drupal_dockeron branchfail2ban-rework(notmain) + untrackeddocker-compose-dev.yml+volumes/aegir-sites-logs/./usr/local/mandala-solr-proxy=mainwith an uncommitted edit todocker-compose.yml./usr/local/mandala_drupal_docker= a second, idle checkout of the same repo.- Hand-created
.envsecrets (in no IaC): AegirHTTP_PORT/SSH_PORT/FQDN/MYSQL_LOCAL_ROOT_PW/COOKIE_DOMAIN; solr-proxySOLR_BASEURL/DEFAULT_RETURL. These must be recreated as D11 env/secret values. - Docker-network drift: two solr-proxy networks
(
mandala-solr-proxy_default+mandalasolrproxy_default). - No custom systemd/cron for the stacks — they were
docker compose up'd by hand, so nothing butrestart:alwaysbrings them back after a reboot. The only managed units aremandala-banlist-reload.timer+mandala-cache-warm.timer(already in IaC).
Terraform-captured components + D11 disposition
| Component | What it is | D11 disposition |
|---|---|---|
rdx/index CNAMEs :9001/:8765 |
mandala-rdx-dev (:9001/HTTP, IP-allowlisted to 3 Linode IPs via SG tcp-9001-in-from-public-sn) + mandala-index-dev (:8765). Live-box reality (see audit above): :8765 = mandala-solr-proxy container; the real KMaps-sync service is reindeer_x on 9000/tcp (+ 9001/udp), already running. The terraform rdx:9001/HTTP target is stale vs. what runs. |
RECONCILE, not build-from-scratch — reindeer_x is already the D11 replacement and already deployed here (ADR 006/007). At cutover: re-point/retire the rdx/index ALB targets against reindeer_x's actual 9000/tcp + the solr-proxy; keep the IP-allowlist for the 3 external KMaps sources if they still push; confirm push-vs-pull with Than/Andres. |
| Legacy Shanti redirects | {mandala,audio-video,images,sources,texts,visuals}-dev.shanti.virginia.edu → internal CNAMEs (301). |
CARRY (backward-compat for old Shanti URLs) — but repoint at the single consolidated D11 site. |
| 5 D7 site CNAMEs | internal_cnames (av/images/texts/sources/visuals/mandala/aegir) → LB, one per legacy multisite. |
COLLAPSE — D11 is one site; decide each name → single D11 app or retire. |
reindeer_x + workqueue (redis) stack |
Live D11-era KMaps sync (kmapssolrsync project, /usr/local/kmaps-solr-sync-out), reindeer_x on 9000/tcp+9001/udp, own redis workqueue. |
CARRY as its own service (ADR 007 = independent repo uvalib/mandala-reindeer_x). Not part of the Drupal container; deploy alongside. Reconcile its redis vs. ADR 014's + SimpleSAMLphp's. |
| Site databases on shared RDS | Corrected via drush sql-connect per site: the D7 site data is on rds-mysql8-staging.internal.lib.virginia.edu (MySQL 8, the same RDS dsf uses), user mandala_sites_dev, one DB per site. The local mariadb container holds only Aegir's own control DB (@mandala-aegir-dev → mandalaaegirde_2). |
Informs §5.2 — D11 needs no new RDS; reuse the shared instance. |
Persistent EBS = /mnt/docker |
100 GB gp3 (/dev/nvme1n1, 70 G used), the Docker data-root — holds var-aegir volume + mariadb data + images. BackupPolicy=none. |
INSPECT + migrate the relevant volumes (uploaded files) to the D11 sites/default/files bind mount. Flag the no-backup risk before touching. |
| fail2ban ban-list reload + cache-warm timers | mandala-specific scraper mitigation in configure_backend.yml (recently ported, mandala-fail2ban-rework). |
CARRY to the D11 host/app — actively maintained, mandala-unique. |
| Instance / IAM profile / keypair / CloudWatch / SGs | AL2023 t3a.medium ×2, root 100 GB, CloudWatch agent role, ssh SGs. | RECREATE in D11 shape (standard). |
Next audit actions: (a) ✅ live-box drift enumerated (see audit above);
(b) recover the hand-created .env values (MySQL root pw, SOLR_BASEURL,
DEFAULT_RETURL, FQDN, COOKIE_DOMAIN) into D11 env/secret management; (c) copy
off the checked-in edits before cutover — the fail2ban-rework branch state of
/usr/local/dockerfiles + the uncommitted mandala-solr-proxy/docker-compose.yml
edit (confirm both are captured upstream or port them); (d) confirm reindeer_x
push-vs-pull with Than/Andres to settle the rdx/index ALB target reconcile;
(e) identify which /mnt/docker volumes hold user-uploaded files to migrate.