Skip to content

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, without editing Pulumi or Go for the supported path.

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.

Boundaries

  • V1 certifies AWS ECS Fargate. GCP (gcp / gke-autopilot), AWS EKS (aws / eks-autopilot), OVH (ovh / mks), and Scaleway (scaleway / kapsule) are experimental (ADR 0007 / 0008). GCP Magento Ops and day-2 ports exist as cloud adapters. Multi-cloud is not claimed until two targets are certified.
  • 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.
  • Artifacts are immutable, signed, built once, and promoted by digest.
  • 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>/.

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. Experimental GCP maps Magento onto GKE Autopilot, Cloud SQL, and Memorystore. See gcp-experimental.md. 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 0003.

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.

The web runtime sits on the portable side of this boundary. Nginx with PHP-FPM is the default certified implementation; its 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 root filesystem is read-only; its VSM and transient cache use an executable task-scoped tmpfs at /var/lib/varnish. Headless tasks route directly to nginx on port 8080. FrankenPHP classic is a selectable runtime contract with its own image adapter. Both use the same health, port, task, and artifact checks. The selected application mode is passed to the application container as a runtime contract, so integrated and headless deployments can evolve their request and capability wiring without changing the public provider boundary. FrankenPHP worker mode is reserved and does not appear in configuration because Magento compatibility has not been proven.

Certified v1 targets are AWS ECS Fargate and GCP GKE Autopilot. Other runtimes and clouds implement sdk/v1 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-autopilot (EKS Auto Mode-shaped; infra-only)
natMode nat-gateway (default), fck-nat (cost/preview) -
databaseEngine aurora-mysql, rds-mysql -
searchMode serverless, provisioned, disabled OpenSearch deferred on EKS
Queue (catalog.queueMode) db, amazon-mq, ecs-rabbitmq ecs-artemis (experimental); deferred on EKS
Edge CloudFront + WAF deferred on EKS
Day-2 ops deploy/logs/exec via AWS adapters ErrNotSupported on EKS until phase 3

See aws-eks-experimental.md for the EKS path and gcp-experimental.md for GCP.

Evidence tiers differ: Pulumi mocks prove composition, Floci proves selected AWS API contracts without an account, and scripts/aws-acceptance-local.sh proves a real account path with destroy + assert_clean. 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 0002 and ADR 0008. 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/v1. 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 experimental code mirrors the stack composition boundary under internal/cloud/gcp/.

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 0009). 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 AWS plus experimental GCP; third-party modules use a custom binary that calls RegisterModule (ADR 0007).

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.

Runtime configuration keeps the root filesystem read-only. 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, while Adobe's MAGENTO_DC_* environment configuration supplies capability endpoints and credentials at task start.

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 dev init creates a small Docker Compose project, and magelift dev 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. The app uses the same FrankenPHP classic image contract used by the runtime.

The local workflow provides fast source synchronization, deterministic reset commands, service logs, and direct Magento command execution. magelift dev 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 FrankenPHP app also exposes https://localhost:8443 through Caddy's internal development CA. The CA state is ephemeral and local to the container, so this listener is for secure-cookie and integration testing rather than production TLS. 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. Standard and high-availability environments use Amazon MQ for RabbitMQ cluster deployments with quorum queues. A RabbitMQ cluster spans three availability zones, so the target must provision three private queue subnets even when the web tier uses fewer zones.

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 0007 and ADR 0008: AWS is the certified v1 target; Magento-shaped ports live in internal/platform; cloud adapters live under internal/cloud/<provider>. GCP (gcp / gke-autopilot), OVH (ovh / mks), and Scaleway (scaleway / kapsule) are experimental first-party candidates. 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.