Architecture charter¶
Product promise¶
A Magento developer adds magelift.yaml to an existing repository and uses one native
CLI to deploy and operate a production-grade environment in their own cloud account.
The supported path is Magelift commands only: doctor, bootstrap, deploy,
health, destroy. Operators should not need Pulumi, Kubernetes, Cosign, or
GitHub federation commands on a laptop preview.
Presets keep YAML small for agencies and small e-merchants coming from Adobe Commerce
Cloud or Upsun. Power users open catalog escape hatches
(capability matrix). Headless means Magento
application.mode: headless|integrated; storefront frameworks stay external.
MageLift provisions resources in the user's cloud account. It does not proxy cloud billing or add a resource markup. The user chooses the provider, runtime, managed services, edge layer, and telemetry destination, subject to the selected target's compatibility and certification status.
Boundaries¶
- The RC1 matrix marks AWS ECS Fargate and GCP GKE Autopilot as certified for
evidenced cells. GCP GKE Standard is experimental, with a recorded 2.4.6-p15 HA
pass and current 2.4.9 MySQL 8.4 passes, and no release-wide certification yet.
Magento 2.4.6 is no longer a supported line. AWS EKS
(
aws/eks), OVH (ovh/mks), and Scaleway (scaleway/kapsule) remain experimental (ADR 0002). Evidence, not a descriptor, is the authority for a certification claim. - Stable interfaces may exist for config, lifecycle, and capabilities; experimental targets must be labeled in docs and CLI output.
- The CLI orchestrates; Pulumi owns durable infrastructure.
- The core owns configuration envelopes, command UX, plugin discovery, trust, locking, and orchestration policy. Providers own cloud implementation, stack execution, and operating capabilities, reached over versioned typed operations with explicit negotiation; incompatibility fails closed (ADR 0013).
- Provider subprocesses run with the user's cloud privileges; the process boundary is a deployment and compatibility boundary, not a security sandbox.
- Artifacts are immutable, signed, built once, and promoted by digest.
magelift build --push,sign, andpromoteuse the operator's current cloud or CI login. Cosign keyless identity remains Sigstore OIDC under the hood (Actions, Google SA), not Artifact Registry or ECR native signatures. The YAML-only path does not require Cosign flags. - Secrets are references resolved at runtime, never plaintext config.
- Unsupported Magento/service combinations fail before mutate unless an auditable override accepts the risk.
- No shared Pulumi components that switch on provider. Each cloud owns topology
under
internal/cloud/<provider>/until Order 5 moves providers to nestedproviders/<name>/modules per ADR 0013.
System shape¶
The control plane is the magelift CLI: load typed YAML, resolve an environment,
validate compatibility, then drive build, Pulumi, and ops. On certified AWS the
request path is Route 53, CloudFront, WAF, ALB, and private ECS Fargate. Managed
AWS services hold state; S3 is the media path. GCP maps Magento onto GKE Autopilot
for the smaller path and GKE Standard when a workload needs node-level kernel
settings, alongside Cloud SQL and Memorystore. See gcp-experimental.md
for provider-specific implementation notes.
Magento migrate candidates run as GKE Jobs through the shared deployflow port.
The PHP package exposes a lifecycle DAG: validate, build, package, deploy, post-deploy. The build runner runs the first three without runtime credentials. Deploy and post-deploy run after connectivity and runtime config injection. Extensions use stable logical IDs, not raw provider schemas. Normal projects stay YAML-only.
Artifact manifest creation has a pre-digest prepare step and a post-build finalize step. The finalized manifest stays outside the image and binds the prepared metadata to BuildKit's OCI digest. An image may contain pre-digest build metadata, but MageLift does not rebuild or mutate it to embed the final manifest. See ADR 0005.
Portability boundary¶
The application model, build phases, artifact manifest, and capability requirements are portable contracts. Network layout, compute resources, managed services, recovery controls, and cost models belong to a target implementation.
Cross-cutting edge and telemetry¶
Edge delivery and observability are separate from the cloud runtime. edge and
observability in the configuration are typed intents, not vendor credential
containers. Fastly can sit in front of an AWS, GCP, OVHcloud, or Scaleway origin;
CloudFront, Cloud Armor, or another provider edge can remain the native path. The
core refuses an unknown edge provider instead of silently substituting the cloud
provider's CDN.
The public extension boundary receives sdk.EdgeIntent and
sdk.ObservabilityIntent, plus the optional typed sdk.ProjectionTarget inside
sdk.ResilienceIntent for cache/search rebuild workloads. An extension owns
provider credentials, VCL or policy artifacts, telemetry exporters, and resource lifecycle;
it does not re-parse YAML or receive raw SDK argument maps. First-party adapters
provision or validate the native graphs for AWS CloudWatch, GCP GKE plus Cloud
Monitoring, Scaleway Cockpit, and the OVHcloud MKS audit stream. Their coverage
is signal-specific and remains explicit in the plan; a destination resource is
not evidence that every requested signal is collected. The registered Fastly
adapter has live API lifecycle and cleanup evidence but remains experimental
until the full routed production edge gate closes. New Relic, Datadog, and OTLP
remain extension-owned; the provider-owned New Relic adapter supports direct
OTLP for generic workloads and maps the provider-neutral NRDOT Kubernetes Helm
boundary to EKS, GKE, Kapsule, and MKS, while the ECS path uses the documented
OpenTelemetry Collector Contrib boundary. The bounded 2026-08-09 New Relic CLI
marker/NRQL probe proves queryable data-plane delivery, and the provider-owned
Go adapter has an offline-tested NerdGraph/NRQL marker verifier. Collector
lifecycle and signal-specific operational evidence remain open.
The web runtime sits on the portable side of this boundary. nginx-fpm is the Adobe-aligned default. frankenphp-classic and php-apache are Adobe-unsupported plugins that require compatibility.allowUnsupported; Adobe has no row for either and neither is certified. frankenphp-worker is unregistered. The default ECS task uses an nginx HTTP container and a PHP-FPM container built from the same immutable image. Integrated tasks add the pinned Varnish 8.0.2
sidecar on port 6081 and route the load balancer through it to nginx on port 8080.
The Varnish container uses a writable root like the other Magento containers;
its VSM lives at /tmp/varnish on the image filesystem (-n /tmp/varnish),
not on a Fargate empty volume (those mount root-owned and break Varnish).
Headless tasks route directly to
nginx on port 8080. The selected application mode is passed to the application
container as a runtime contract, so integrated and headless deployments can change
request wiring without changing the public provider boundary.
Certified v1 targets are AWS ECS Fargate and GCP GKE Autopilot. Other runtimes and
clouds implement sdk Target contracts and register a platform.StackModule.
Each provider keeps capabilities explicit and must pass the shared Magento
acceptance suite before certification. Portable YAML is not a lowest-common-denominator
cloud catalog.
AWS Magento product matrix¶
"Full AWS Magento" means the Magento acceptance path on ECS Fargate with explicit escape hatches, not every AWS SKU.
| Choice | Certified (ECS Fargate) | Experimental / deferred |
|---|---|---|
| Runtime | ecs-fargate |
eks (EKS Auto Mode-shaped; live certification pending) |
natMode |
nat-gateway (default), fck-nat |
- |
natTopology |
single-az or multi-az; omitted defaults to single-AZ for preview and multi-AZ for standard/HA |
- |
natReplacementMode |
none or auto-scaling; omitted defaults to automatic replacement for multi-AZ fck-nat |
- |
natInstanceType |
ARM64 Graviton fck-nat size; omitted defaults to cost-optimized t4g.nano |
x86 instance types are rejected for the first-party ARM64 AMI |
databaseEngine |
rds-mysql |
aurora-mysql (experimental-warn; unverifiable on maintainer free-tier). Adobe gate is Aurora 3.11/3.12. Unaimed Aurora 8.4 fails closed. A private shop on Aurora 8.4 is an example, not the certified pin. |
| Managed database durability | RDS backup and maintenance windows, deletion protection, automated-backup deletion policy | Aurora inherits the same YAML knobs but is not a certified apply cell |
| Valkey snapshot durability | ElastiCache snapshot retention and daily snapshot window | 0 explicitly disables automatic snapshots; Valkey remains reconstructible state |
searchMode |
disabled (preview / free-tier) |
serverless (AOSS + Magento SigV4 sidecar) and provisioned (unsigned HTTPS:443 in-VPC). Both remain experimental until matrix + evidence say otherwise. A private Magento-on-AWS shop may illustrate the provisioned path. It does not certify MageLift. |
Queue (catalog.queueMode) |
db (preview default), ecs-rabbitmq (standard/HA default when unset) |
Explicit amazon-mq remains experimental-warn. EKS database or rabbitmq pending live certification. ecs-artemis remains experimental. |
| Edge | CloudFront + WAF | deferred on EKS |
| Day-2 ops | deploy/logs/exec via AWS adapters | shared Kubernetes deploy/logs/exec path (live certification pending) |
See aws-eks-experimental.md for the EKS path and gcp-experimental.md for GCP implementation notes.
Evidence tiers differ: Pulumi mocks prove composition, Floci AWS and floci-gcp prove selected API contracts without an account, and packed live sessions (certification sessions) prove Magento on GCP then AWS. Emulators do not certify Autopilot, Armor, managed TLS, or Magento Cloud SQL PITR. Mocks alone do not certify a matrix cell for production use.
Coming from Adobe Commerce Cloud or Platform.sh? Start with migrating-from-paas.md.
See ADR 0003 and ADR 0004. Contributor checklist: adding-a-provider.md.
Repository layout¶
Provider-neutral code: internal/platform (stack modules, Magento output keys,
env bindings), internal/automation, internal/deploy, internal/infra (SDK
extension index), internal/topology, and sdk. Providers live under
internal/cloud/<provider>/.
AWS packages today include bootstrap, secrets, target, network,
security, ingress, runtime, edge, database, cache, search, queue,
storage, observability, operations, deployment, stack, and state.
GCP ships as an autonomous plugin module under providers/gcp/ (own go.mod, versioned typed net/rpc protocol); the core talks to it through a plugin-backed shim, never in-process.
CLI deploy path: platform.ModuleRegistry selects a StackModule by
provider + runtime. Magento lock and candidate steps are optional via
platform.HasOps. Day-2 surfaces (bootstrap, state, secrets, logs, exec, runtime
health) resolve the same way through HasBootstrap / HasState / HasSecrets /
HasRuntimeObserve (ADR 0004). DIY stack names
include provider and runtime so targets do not collide.
internal/infra.Registry is for Target/Capability/Hook discovery tests; it does
not replace module registration.
The SDK registry indexes targets, capabilities, typed transforms, and lifecycle
hooks by stable IDs. Transforms stay type-checked at the boundary. Hook discovery
is deterministic by phase and ID. The default CLI ships the first-party AWS, GCP,
OVH, and Scaleway modules. Third-party modules use a custom binary that calls
cli.NewWithExtensions with an explicit sdk.Module (ADR 0008).
The AWS stack package composes a validated plan: catalog values from the caller,
Pulumi outputs between capabilities, separate providers for regional vs
CloudFront/WAF resources. Each capability validates typed inputs before register.
The planner checks Magento release against Adobe's AWS service versions.
allowUnsupported is the only recorded exception path. An existing-network
reference can supply VPC and subnets; MageLift then skips routing, NAT, and VPC
endpoints for that network.
Standard and high-availability networks create private interface endpoints for ECR, CloudWatch Logs, Secrets Manager, SSM, and ECS Exec, alongside the S3 gateway endpoint. Preview keeps only the S3 gateway endpoint to preserve its low-cost profile. Endpoint security groups allow HTTPS from the VPC CIDR and endpoint private DNS remains enabled.
Production observability also provisions a CloudWatch Synthetics canary with the current Puppeteer 11 runtime. The canary checks the public HTTPS health endpoint every five minutes, stores encrypted run artifacts in a versioned private S3 bucket, and publishes a breaching alarm when two of three five-minute windows are unsuccessful. Its execution role is limited to that bucket prefix, the configured KMS key, its log groups, and the CloudWatch Synthetics metric namespace. Preview and non-production environments do not incur canary resources.
The Go build system follows the same rule. Its packages are grouped below
internal/build/kit, internal/build/pipeline, internal/build/plan, and
internal/build/runner. The repository-level build/ directory remains the
Magento Composer package and is separate from these Go internals.
Infrastructure commands keep seams narrow: config yields a platform.PlannedStack
from the selected module; Automation API owns the Pulumi workspace; the CLI owns
environment selection, approvals, provider Ops (lock / Magento steps when present),
and post-update health. Stack backends are swappable in tests so commands can run
without cloud credentials.
ECS web, cron, and queue tasks use the application task role. The one-off migration candidate uses a separate ECS deployment role, which can be tightened independently as the Magento lifecycle gains more granular capability declarations. Bootstrap also creates a recovery-only state role, a repository-scoped GitHub Actions CI role, and a read-only build role for MageLift-scoped secrets. The state role cannot mutate infrastructure; generated CI uses the CI role for Pulumi, ECS, and managed-service changes, while the build job uses the build role.
Magento containers (php-fpm, web/nginx, Varnish, cron, consumers) use a writable
root. On ECS the flag is explicit (ReadonlyRootFilesystem: false):
Magento needs to write env.php, generated files, and the nginx pid, and
Fargate empty volumes mount as root-owned, so overlaying /tmp or
/app/var would break the non-root runtime. On GKE the workload
specifiers set no SecurityContext, so the same writable default holds
for the same writable set (internal/cloud/kube/web.go,
providers/gcp/runtime/runtime.go). Only the search-proxy sidecar runs
with a read-only root. The image contains only a non-secret env.php
scaffold. ECS Secrets Manager selectors inject the managed database JSON
fields and the stable Magento encryption key; GKE mounts Secret Manager
values through Kubernetes Secrets consumed by SecretKeyRef only
(internal/cloud/kube/secrets.go). Adobe's MAGENTO_DC_* environment
configuration supplies capability endpoints and credentials at task start.
Runtime storage (definitive writable set, 2026-09-16)¶
Baked into the immutable image (read at runtime, never written): application
code, DI output (setup:di:compile), and static content for the configured
locales plus themes (build.staticContent is required). Written at runtime:
app/etc/env.php (generated from Secrets Manager selectors plus
MAGENTO_DC_* values), disposable var/ (cache, page cache, logs, tmp),
and /tmp (nginx pid, Varnish VSM at /tmp/varnish, probe scratch).
Durable state lives outside the container filesystem: media in buckets,
sessions in Valkey, secrets in the cloud secret store, state in versioned
backups. Writable root is the deliberate alpha answer for this set
(internal/cloud/aws/runtime/containers.go, GKE unset container
SecurityContext in internal/cloud/kube/web.go); no read-only flip is
planned until a writable-storage design proves itself against these
requirements.
Alpha security review (minimum, 2026-09-16)¶
Reviewed for the alpha recipe; each item names its evidence or stays explicitly open. A full security review and penetration test remain outstanding and are not claimed here.
- Secret references: reviewed. YAML carries references only; validation
rejects plaintext for Composer credentials, Magento variables, email
credentials, and edge tokens (
internal/config/config.go). GCP consumes Secret Manager values through Kubernetes Secrets referenced bySecretKeyRefonly, and plugin errors pass a credential redactor before crossing the wire (internal/cloud/kube/secrets.go,providers/gcp/plugin/server.go). - Log redaction: reviewed within its design boundary. Reference-only config
keeps secret values out of YAML, logs, and evidence;
env dumpwarns its output is unsanitized unless--sanitize(mailbox-address hashing only, not certified anonymization) and database passwords travel via environment variables unprinted (internal/cli/env.go). No global log-scanning redactor is claimed. - Least-privilege permissions: reviewed per provider. AWS bootstrap mints
a state role that cannot mutate infrastructure, a repository-scoped CI
role for Pulumi and service changes, and a read-only build role for
MageLift-scoped secrets (
internal/cloud/aws/bootstrap; see the IAM paragraph above). GCP bootstrap mints the pool, provider, and service account plus the repository-scoped impersonation binding only (providers/gcp/bootstrap/identity.go); API roles on the CI service account stay an operator step (seedocs/onboarding.md), and the state bucket is private with versioning plus soft-delete by default. - Network exposure: reviewed for workload isolation. On AWS, workload
security groups accept ingress only within the VPC CIDR; workloads sit
in private subnets with NAT egress
(
internal/cloud/aws/network/network.go). On GCP, the cluster is VPC-native on private subnets with private Standard nodes (providers/gcp/runtime/runtime.go); the control-plane endpoint stays public by default and no Kubernetes network policies are installed: stated here as the shipped posture, not as a claim of hardening. The public surface is the load balancer by storefront design; a rule-by-rule edge audit stays with the full review. - Artifact trust: reviewed. Images build once and promote by digest;
magelift upgradeverifies the keyless Sigstore bundle forchecksums.txtthen the archive SHA-256 before atomic replace (docs/operations.md, upgrade command). AWS acceptance requires a signed immutable runtime digest with a matching Cosign identity (docs/aws-acceptance.md); GCP plan validation requiresrepository@sha256:digests and the CLI verifies the plugin binary digest plus Cosign bundle before spawn (providers/gcp/stack/spec.go,internal/providerhost). - Recovery material: reviewed. State backups are versioned snapshots under
the deployment lock and the bootstrap lock object reports its owner
(
docs/operations.md, state commands). AWS keeps 90-day retention and removes incomplete multipart uploads after seven days; GCP keeps bucket versioning with seven-day soft-delete (providers/gcp/bootstrap/bootstrap.go).
Local development¶
Local development is an execution context, not a cloud target or a low-cost preview
environment. It does not require AWS credentials, Pulumi, remote state, or an
environment account. magelift local init creates a small Docker Compose project,
and magelift local up, status, logs, exec, down, and reset manage it. The
default services are MySQL and Valkey. Starting the app profile also starts the
pinned OpenSearch and RabbitMQ services, so a local Magento install exercises the
same capability names as a deployed environment. nginx with PHP-FPM is the default local web runtime. frankenphp-classic and php-apache are Adobe-unsupported plugins that require compatibility.allowUnsupported; they are not certified. The catalog can select Artemis over RabbitMQ and Varnish in front of nginx when those image and health contracts are available. frankenphp-worker is unregistered.
The local workflow provides fast source synchronization, deterministic reset commands,
service logs, and direct Magento command execution. magelift local seed provides a
non-interactive first install for repositories with bin/magento and Composer
dependencies already present. It uses local-only database, cache, search, queue,
and administrator defaults, persists the administrator password in the ignored
mode-0600 .magelift/local.env, and stops when app/etc/env.php already exists.
The app binds loopback HTTP 8080 and HTTPS 8443. Those certificates are local
only. Use them for cookie and integration tests, not as production TLS evidence.
Local development uses the compatibility catalog and capability names used by
deployed environments so that validation catches unsupported combinations early.
It does not promise identical managed-service behavior; tests that depend on AWS
semantics still run against an ephemeral AWS environment.
Preview queueing uses Magento's database queue. Certified AWS queue cells are
db and ecs-rabbitmq. Standard and high-availability YAML that omits
catalog.queueMode selects ecs-rabbitmq. Explicit amazon-mq remains
experimental-warn, not certified. Amazon MQ clusters span three availability
zones, so that experimental shape needs three private queue subnets even when
the web tier uses fewer zones.
On EKS and GKE, changing in-cluster RabbitMQ from one replica to quorum (or the
reverse) is refused in place; create a new environment instead.
Docker is the supported host boundary. Debian is the certified container base. A Nix development shell may pin host tools for contributors, but it remains optional and does not replace the container contract. Alpine and NixOS images can be evaluated later if they pass the same Magento and extension tests.
Safety invariants¶
Production changes require a preview, signed digest, deployment lock, approval, successful pre-traffic deploy phase, ECS stabilization, and smoke checks. The provider-neutral deployment orchestrator keeps that order and always releases the lock. Rollback is a forward deployment of an older signed digest and does not reverse database changes. Protected and retained resources must be explicit during destruction.
Evolution¶
Public contracts are independently versioned. Current and previous major configuration schemas receive deterministic migrations. Released infrastructure component names use aliases or migration logic before renaming. Material decisions are recorded as ADRs.
Provider growth follows ADR 0003 and
ADR 0004: Magento-shaped ports live in
internal/platform; cloud adapters live under internal/cloud/<provider>.
Certified Magento targets are AWS ECS Fargate and GCP GKE Autopilot. GKE Standard,
AWS EKS, OVH (ovh / mks), and Scaleway (scaleway / kapsule) stay
experimental. See
gcp-experimental.md, ovh-experimental.md,
and scaleway-experimental.md. Later clouds
register another adapter without expanding portable YAML into a lowest-common-denominator
cloud schema.