Cloud: Deployment Overview

This content was generated with the assistance of AI and should be verified against the official documentation of each platform before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against WooCommerce 11.2.1 (2026-10-09) on WordPress 7.1.4 (the security release of 2026-10-28; the WordPress pages of this site use 7.1.3 as their baseline), PHP 8.4 for the container image, MariaDB 11.8 or MySQL 8.4 on Amazon RDS, Kubernetes 1.36 or 1.37 on Amazon EKS and Action Scheduler 4.1.0, which WooCommerce 11.2.1 bundles, and was checked on 2026-10-29. It is the first of six pages on running a WooCommerce store on a cluster, and it names no prices: sizes are starting points to measure on your own traffic.

A WooCommerce store is a WordPress site, so everything the WordPress "Cloud Deployment at Scale" pages say still holds: stateless pods behind a load balancer, a managed database, a shared object cache, media off the pod, one cron scheduler, schema changes in a Job. This page does not repeat that; it lists what a store adds to it and sends you to the right page for each addition. Start from Cloud Reference Architecture if you have not read it. The running example is Pocket Coffee, the fictitious specialty-coffee shop of this section, moving www.pocketcoffee.example (Spanish and English storefronts, one catalog) from a single virtual machine to an EKS cluster in eu-west-1. Shared cloud fundamentals are linked, not repeated: EKS Clusters, Amazon RDS, CloudFront and the AWS section.

What a store adds

A publishing site mostly serves the same page to everybody and writes rarely. A store reads and writes on behalf of one shopper at a time, takes money, and is called by other systems when it did not ask. Seven additions change the architecture:

Addition What it means in a cluster Where it is covered

Orders

Orders are the data that must never be lost. In WooCommerce 11.2.1 they live in the HPOS tables (wc_orders and its siblings) of the one database; there is no per-pod copy. The database sizing, the sync with the posts tables and the growth of the order tables are a store concern.

Cloud: Database, HPOS and Sessions

Carts and sessions

WordPress has no sessions; WooCommerce does. Customer data (cart, address, chosen shipping) is stored in the table woocommerce_sessions, keyed by a cookie, and read from the object cache when one is present. Any pod can serve any shopper, but only because the table and the cache are shared.

Cloud: Database, HPOS and Sessions

Payments

The shopper leaves for the provider and comes back, possibly on another pod, and may never come back at all. The order is completed by the provider’s server-to-server call, not by the browser. Payment secrets are secrets of the release, not of the pod.

Cloud: Caching, CDN and Checkout

Webhooks

Incoming ones (wc-api callbacks from the payment provider) must reach any pod at any time, bypass every cache and pass the WAF. Outgoing ones (order.created to the roasting room) are delivered by a queued job, so something must run the queue.

Cloud: Caching, CDN and Checkout

Emails

Order, shipping and refund emails are transactional. With the feature "deferred transactional emails" they are queued and sent by a background job; either way they leave through an email service, not a mail server in the pod.

Amazon SES and Emails

Background jobs

Action Scheduler does the work that must not happen inside the shopper’s request: queued emails, webhook delivery, unpaid-order cancellation, session cleanup, HPOS sync, extension jobs, subscription renewals. A queue needs workers that are not the web pods.

Cloud: Action Scheduler Workers on Kubernetes

Pages that cannot be cached

Cart, Checkout and My Account, add-to-cart and wc-ajax requests, the Store API and everyone with a WooCommerce cookie always reach PHP. The CDN and the load balancer help only for the catalog, so the web tier is sized for dynamic traffic.

Cloud: Caching, CDN and Checkout

Two more things follow from the list and belong to the release process: the store has its own database update routine on every WooCommerce upgrade (see Cloud: Releases and Database Updates), and moving an existing store has to account for open orders, scheduled actions and payment callbacks (see Cloud: Migrating a Store from a Single Server).

The rest of this page is the map: one diagram, the names the six pages share, the state table, the paths one order takes and the order to read things in.

The architecture

Pocket Coffee on EKS: shoppers through CloudFront and a load balancer to the web pods, with a worker Deployment, a cron job and a migrate job added for the store, all sharing RDS, Valkey, S3 and Secrets Manager

The blue and teal parts are the WordPress architecture unchanged. Shoppers reach CloudFront and its web ACL, then the load balancer, which picks a healthy pod of the Deployment pocketcoffee-web. Each pod runs two containers: nginx serves static files and hands PHP to php-fpm over FastCGI on the pod’s loopback address. Every pod talks to the same data tier: RDS for orders, sessions, customers and the queue; Valkey for the object cache; S3 for media; Secrets Manager for the keys, salts and gateway secrets, injected as environment variables when the pod starts.

The amber parts are what the store adds or changes:

  • The worker. A second Deployment, pocketcoffee-worker, runs the same image with a different command: WP-CLI’s Action Scheduler runner. The web pods stop starting the queue themselves (the async request runner is off), so the queue does not compete with shoppers for PHP workers, and the worker’s size and count are tuned for jobs, not for page views.

  • The cron job. The WordPress cron CronJob remains the only scheduler of WP-Cron events, but a store runs it every minute rather than every five: queued emails and recurring jobs that wait for a WP-Cron tick are shopper-visible delays.

  • The update job. pocketcoffee-migrate runs wp core update-db and the WooCommerce database update before the rollout, as a Job, so a new pod never meets an old schema. The WooCommerce part is new on top of the WordPress Job.

  • The payment path. The arrow from the payment provider is the one that must never be cached, rate-limited as if it were a bot, or tied to a pod. It enters through the load balancer like a shopper, reaches any pod, and changes the order in RDS.

  • The outgoing arrows. Emails and outgoing webhooks leave from the worker (or from a web pod if emails are not deferred), to the email service and to the receiving systems. If a receiver is down the worker retries; the shopper never waits.

Everything that executes PHP, which is the web pods, the worker, the cron Job and the update Job, uses the same container image (see the next section) and the same environment, so a plugin runs the same code wherever it runs.

The names used on the six pages

The six store pages use one set of names, which extend the WordPress set listed in Cloud Reference Architecture, so a snippet on one page connects to the others. The store has its own Helm chart, pocketcoffee-woocommerce. It is built exactly the way the WordPress chart lumenpress-wordpress is, with the same structure and the same helpers, except that the helper prefix is pc.* instead of lp.* (pc.name, pc.labels, pc.selector, pc.phpImage, pc.nginxImage, pc.envFrom, pc.uploadsVolume, pc.containerSecurity). The chart itself is written out on Deploying on EKS with Helm; these pages show only what the store changes and never repeat the Deployment, the Service, the HPA or the PDB.

Item Name

Region and account

eu-west-1, account 123456789012

Site and media hosts

www.pocketcoffee.example (domain pocketcoffee.example), media.pocketcoffee.example

EKS cluster and namespace

pocketcoffee-prod, namespace pocketcoffee

Helm release and chart

release pocketcoffee, chart directory pocketcoffee-woocommerce, helpers pc.*

Image

ECR repository pocketcoffee/woocommerce, tag <git-sha>, built from the official wordpress:7.1.4-php8.4-fpm image with WooCommerce 11.2.1 and the store’s plugins and theme inside

Workloads

Deployment pocketcoffee-web (nginx and php-fpm containers), CronJob pocketcoffee-cron, Job pocketcoffee-migrate; new for the store: Deployment pocketcoffee-worker

Database

RDS instance pocketcoffee-prod, database pocketcoffee, users wc_app and wc_migrate, table prefix wp_

Secrets

pocketcoffee/prod/woocommerce, pocketcoffee/prod/valkey

Media

S3 bucket pocketcoffee-prod-media, served from media.pocketcoffee.example

The tag wordpress:7.1.4-php8.4-fpm existed on Docker Hub on the check date (it was resolved with docker manifest inspect), and so did wordpress:cli-2.12.0-php8.4, the WP-CLI image the examples assume. Confirm the tags with docker pull when you build. WooCommerce is installed into the image at build time (from the Composer or the release zip of your pipeline), never from the dashboard of a running pod: the same rule as plugins and themes on the WordPress pages, with more at stake because an update runs a database routine.

The values file of the chart is the contract between the infrastructure and the workloads. The excerpt shows only the keys the store adds or changes; the rest is the WordPress values file with the names above. The final spelling of the worker keys is owned by Cloud: Action Scheduler Workers on Kubernetes.

# values.yaml of pocketcoffee-woocommerce: the store-specific additions (the rest is as in lumenpress-wordpress).
image:
  repository: 123456789012.dkr.ecr.eu-west-1.amazonaws.com/pocketcoffee/woocommerce
  tag: ""             # required: the git SHA of the release

site:
  url: https://www.pocketcoffee.example
  tablePrefix: wp_
  redisPrefix: "pocketcoffee:"

database:
  host: pocketcoffee-prod.cxxxxxxxxxxx.eu-west-1.rds.amazonaws.com
  name: pocketcoffee
  user: wc_app
  migrateUser: wc_migrate

secrets:
  wordpressKey: pocketcoffee/prod/woocommerce
  valkeyKey: pocketcoffee/prod/valkey

web:
  replicaCount: 3
  asyncQueueRunner: false      # the web pods never start an Action Scheduler runner
  wpCron: false                # DISABLE_WP_CRON: only the CronJob runs WP-Cron events

worker:                        # new for a store: Deployment pocketcoffee-worker
  enabled: true
  replicaCount: 2
  batchSize: 100
  resources:
    requests: { cpu: 250m, memory: 384Mi }
    limits: { memory: 512Mi }

cron:
  schedule: "* * * * *"        # every minute: emails and syncs should not wait five minutes

store:
  paymentWebhookPath: /wc-api/pocketcoffee_pay/   # reaches any pod; never cached
  hpos:
    enabled: true
    compatibilityMode: false   # posts-table sync off once HPOS is proven

The excerpt is a values file, not a chart: web.asyncQueueRunner and web.wpCron are read by the chart’s ConfigMap template and become environment variables that a small must-use plugin in the image turns into the action_scheduler_allow_async_request_runner filter and the DISABLE_WP_CRON constant (both are written out in the Action Scheduler page). store.hpos.compatibilityMode is likewise documentation of a decision rather than a setting WooCommerce reads from the environment: HPOS is switched with wp option or wp wc hpos as shown on the database page. Treat the keys as the chart’s own vocabulary.

Where the state of a store lives

The WordPress page lists where each piece of WordPress state goes. A store has more state, and some of it is easy to miss because it sits in the database or in the uploads directory by default. The table is the checklist for the six pages: shared means one copy that every pod and Job uses, per pod means a copy that may differ or be lost when a pod is replaced.

State Default location in WooCommerce 11.2.1 In the tiered design Class

Orders, order items, order meta, addresses

HPOS tables wc_orders, wc_order_addresses, wc_orders_meta, wc_order_operational_data (the posts tables are the backup while compatibility mode is on)

One database; HPOS authoritative; posts sync off once proven

Shared (RDS)

Customers and accounts

wp_users, wp_usermeta, wc_customer_lookup

The same database

Shared (RDS)

Cart and customer session

Table woocommerce_sessions, keyed by the wp_woocommerce_session_ cookie; also cached in the object cache group wc_session_id

The table stays authoritative; Valkey caches reads; a recurring action woocommerce_cleanup_sessions removes expired rows (every 12 hours)

Shared (RDS plus Valkey)

Stock and reservations

Product meta and lookup tables; the table wc_reserved_stock holds stock held by pending checkouts (woocommerce_hold_stock_minutes, 60 by default)

The same database; hold time and unpaid-order cancellation run in Action Scheduler

Shared (RDS)

The job queue

Tables actionscheduler_actions, actionscheduler_claims, actionscheduler_groups, actionscheduler_logs

The same database; claimed by the worker and never by the web pods

Shared (RDS)

WP-Cron events

The option cron in wp_options

The same database; run by one CronJob

Shared (RDS)

Transients and object cache

wp_options rows, or the persistent object cache when a drop-in exists

Valkey, with a prefix per site and environment

Shared (Valkey)

Product and customer-visible media

wp-content/uploads

S3 behind media.pocketcoffee.example, or EFS

Shared (S3 or EFS)

Downloadable product files

wp-content/uploads/woocommerce_uploads, protected by the web server’s rules

The same store as the media, with the protection kept (private prefix or EFS path); not served by the CDN

Shared (S3 or EFS)

WooCommerce logs

Files under wp-content/uploads/wc-logs by default; the database handler is selectable in WooCommerce > Status > Logs > Settings; the directory moves with the filter woocommerce_log_directory

Standard output, or the database handler, or a shared path; never a per-pod file that disappears with the pod

Per pod unless moved

Gateway secrets and API keys

Settings stored in wp_options and the gateway’s own tables

A secret reference read at start, or encrypted settings in the database with the keys identical on every pod

Shared (Secrets Manager, RDS)

Keys and salts

wp-config.php

A secret, identical on every pod; the Store API cart token and the nonces depend on them

Shared (Secrets Manager)

Webhook delivery log

wc_webhooks and the webhook logs, plus actionscheduler_logs

The same database

Shared (RDS)

Outgoing mail

The server’s mail transport

An email service through SMTP or its API

External

PHP code, plugins, theme

Files in /var/www/html

The container image

Image

OPcache, nginx buffers, /tmp

The pod’s memory and disk

Rebuilt at start

Per pod

The table was read from the WooCommerce 11.2.1 source and from the existing pages of this section, not from a running cluster. Two rows deserve a sentence more.

Sessions are the reason a store needs more than WordPress. WordPress core has no sessions, so a WordPress cluster needs neither stickiness nor a session store. WooCommerce has woocommerce_sessions, and it is written on every change of the cart: the session handler runs an INSERT ... ON DUPLICATE KEY UPDATE on the table and then sets the same value in the object cache. Nothing is per pod, so stickiness is not needed either, but the write rate of that table is proportional to cart activity and the table needs a cleanup that keeps up; both are on Cloud: Database, HPOS and Sessions.

Logs and downloads are the two per-pod traps. WooCommerce writes its own logs (payment gateway traces, Action Scheduler errors, webhook failures) under the uploads directory by default. On a pod with a read-only root file system and an emptyDir for uploads, they disappear with the pod, which is exactly when you need them. Choose the logging handler on purpose, as the database page explains. Downloadable products live in a protected subdirectory of the uploads; if the uploads move to S3, the protection must move with them.

The paths of one order

The paths below follow one order of Pocket Coffee through the cluster. They show where each step may run, which is the reason for the amber parts of the diagram.

sequenceDiagram actor S as Shopper participant W as Web pod A participant D as RDS participant P as Payment provider participant V as Web pod B participant K as Worker pod participant X as Email service S->>W: Add to cart (session row created) W->>D: INSERT woocommerce_sessions S->>W: Place order W->>D: Create order, reserve stock W-->>S: Redirect to the provider S->>P: Pays P->>V: Webhook on wc-api (any pod) V->>D: Mark paid, record the transaction V->>D: Schedule queued email and outgoing webhook S->>W: Returns to the order-received page K->>D: Claim due actions K->>X: Send the order email K->>K: Deliver the order.created webhook K->>D: Mark the actions complete

The same paths, as a table of who does the work:

Step Runs on Needs

Browse the catalog

CDN edge on a hit; any web pod on a miss

Cache rules that keep carts out of the cache

Add to cart, update cart

Any web pod (add-to-cart and wc-ajax bypass every cache)

The sessions table and the object cache

Block checkout (Store API)

Any web pod; the Cart-Token header and a nonce travel with the request

Identical keys and salts on every pod

Create the order, reserve stock

The web pod serving the checkout request

A database primary: checkout never reads a stale replica

Payment return and webhook

Any web pod, possibly not the one that created the order

wc-api bypassing the cache, a WAF that lets the provider in, an idempotent gateway

Order and shipping emails

The worker when emails are deferred; the web pod otherwise

An email service, and a running worker

Outgoing webhooks

The worker (the async delivery is an Action Scheduler action in the group woocommerce-webhooks)

A running worker, reachable receivers

Unpaid-order cancellation, session and draft-order cleanup, HPOS background sync

The worker (all of them are Action Scheduler actions: woocommerce_cancel_unpaid_orders, woocommerce_cleanup_sessions, woocommerce_cleanup_draft_orders)

A running worker

Schema updates after an upgrade

The Job pocketcoffee-migrate

wc_migrate privileges, run before the rollout

The paths hold only if three rules hold, and they are the three the pages that follow turn into configuration: nothing a shopper or a provider needs is cached, nothing is stored on the pod, and every job has exactly one kind of runner. If the payment webhook arrives while the pod that created the order has already been replaced, the order is still correct, because the pod held nothing; if the worker is down, orders are still taken and paid, and the emails and outgoing webhooks wait in the queue until it is back.

Single server and tiered, for a store

The WordPress page makes the general case for staying on one server when that is enough, and the same reasoning holds for a store with one change in emphasis. A store has money and orders on the line, so the cost of an outage is measurable and a managed database with a standby is often the first step worth taking, before any pod is added. The trade is the same: simplicity against availability.

One server Tiered on Kubernetes

Checkout during a deploy

The server is updated in place; a failed update interrupts checkout

A rolling update keeps most pods serving; schema changes run first in a Job

A payment webhook during a failure

Lost if the server is down; the provider retries for a while

Delivered to a surviving pod

Background jobs

Share the server with the shop; one runaway import slows checkout

A worker Deployment with its own resources

Peak season

The largest machine you buy

Pods and workers added by an autoscaler; the database stays one writer

Plugin updates

From the dashboard

Through the image and a pipeline

Operating it

A system administrator

Kubernetes, a cloud, infrastructure as code and a pipeline

Stay on one server, or on a managed WooCommerce host, when traffic is modest, a few minutes of downtime is acceptable and no team operates Kubernetes. The decision criteria are on Cloud Reference Architecture (section "When one server, or a managed WordPress host, is still right") and the middle paths on Google Cloud, Azure and Alternatives to Kubernetes. The store-specific version of the question is whether the shop’s revenue per hour of downtime justifies the cost of operating the tiers; the answer is a business number that this section cannot give.

Tutorials that start a store from a Bitnami WordPress image or chart are out of date: the images and charts are no longer a safe default, as Cloud Reference Architecture explains in its section "The end of the Bitnami images". The pages of this section build on the official wordpress image and on managed services.

Reading path

Read the WordPress pages first; the store pages add to them and assume their names and their Helm chart.

  1. Cloud Reference Architecture: the architecture, the state table and the names that this page extends.

  2. Managed Database: RDS and Aurora: the engine, the parameter group, TLS, users and the connection math, which the store database page builds on.

  3. Media: S3 Offload, EFS and CloudFront: media for the catalog, and the place to put downloadable files.

  4. Object Cache, Sessions and Cron in a Cluster: Valkey and the one-cron-scheduler pattern that the store reuses.

  5. EKS Infrastructure with OpenTofu and Deploying on EKS with Helm: the infrastructure and the chart lumenpress-wordpress that pocketcoffee-woocommerce copies.

  6. Releases, Upgrades and Autoscaling on Kubernetes: the pipeline the store extends with its update routine.

Then the store pages, in this order:

  1. This page: the map.

  2. Cloud: Database, HPOS and Sessions: sizing for order volume, the HPOS tables and sync, the sessions table, read replicas and checkout, and the growth of the order and queue tables.

  3. Cloud: Action Scheduler Workers on Kubernetes: turning off the web runners, the worker Deployment, concurrency, scaling on pending actions and failure handling.

  4. Cloud: Caching, CDN and Checkout: what CloudFront may cache, the WooCommerce cookies, the Store API, webhooks reaching any pod and IP allow-lists.

  5. Cloud: Releases and Database Updates: the database update as a Job, extension updates through the image, a peak-season freeze and rollback with snapshots.

  6. Cloud: Migrating a Store from a Single Server: an order-safe cutover, HPOS state, scheduled actions, renewals and payment webhook URLs.

The existing WooCommerce pages supply the application side that these pages configure: HPOS, Action Scheduler, Performance and Security, Payment Gateways, REST API and Webhooks and WC-CLI. The Kubernetes and AWS fundamentals are in Helm, Jobs and CronJobs and EKS Clusters.

Pitfalls

  • Treating the store as a bigger blog. The web tier of a store is sized for dynamic traffic: a large part of a shopping session never touches the CDN. Plan for more PHP workers per visitor than a publishing site needs.

  • Leaving the async runner and WP-Cron on in the web pods. The queue then runs inside shopper requests, on whichever pod the load balancer chose, in parallel with the worker. Disable both on the web pods and run exactly one kind of runner.

  • A worker that is not running. Orders keep working; emails, outgoing webhooks, stock holds and cleanups quietly stop. Alert on the age of the oldest pending action, not only on pod health.

  • Caching the payment return or the webhook. A cached wc-api response, or a WAF rule that blocks the provider’s addresses, turns a paid order into an unpaid one with a puzzled customer. The rules are on the caching page.

  • Per-pod logs. The default WooCommerce log directory is under the uploads; on a read-only pod it is gone after the next rollout. Pick the handler before the first incident.

  • Reading checkout from a replica. A read replica lags; a session, stock level or order read from one can be stale. Checkout and order paths must read the primary (see the database page).

  • Different keys on different pods. The cart token of the Store API is signed with a key derived from the site’s salts (wp_salt()), and the nonces depend on them too. A pod with other values rejects the shopper’s requests while the rest accept them.

  • Updating WooCommerce from the dashboard. The update runs a database routine from a web request and changes files that the next pod start discards. Update through the image and the Job.

  • Copying the diagram without the cost model. A tier that is not needed is still operated and paid for; the worker and the cron Job are small, but every tier is a thing to patch.

Version notes and what was not verified

  • Verified against the source. The WooCommerce 11.2.1 release zip was downloaded from WordPress.org and read: the session handler (table, cache group, default lifetimes of one day before a refresh and an expiry after two days for a guest or one week for a logged-in customer, the action woocommerce_cleanup_sessions every 12 hours), the asynchronous webhook delivery (action woocommerce_deliver_webhook_async, group woocommerce-webhooks), the deferred transactional email queue (hook woocommerce_send_queued_transactional_email, enabled by the feature deferred_transactional_emails), the logging default (a file handler under uploads/wc-logs, directory filter woocommerce_log_directory, a database handler available), the reserved-stock table and the default hold of 60 minutes, wp wc update, and the Action Scheduler 4.1.0 filters action_scheduler_allow_async_request_runner, action_scheduler_queue_runner_batch_size and action_scheduler_queue_runner_concurrent_batches.

  • Validated locally. The values excerpt was placed in a minimal chart named pocketcoffee-woocommerce with the helpers pc.*; helm lint --set image.tag=... passed, and helm template | kubeconform -strict -summary -ignore-missing-schemas reported two valid resources (a ConfigMap and a Deployment). Without image.tag, the render fails on purpose, as in the WordPress chart. The tags wordpress:7.1.4-php8.4-fpm and wordpress:cli-2.12.0-php8.4 were resolved with docker manifest inspect. The SVG was checked with xmllint.

  • Not verified. Nothing was deployed to a cluster, to AWS or to a payment provider. The Mermaid diagram was written to the rules of this repository but was not rendered with npm run validate:mermaid in this task. The environment variables PC_AS_ASYNC_RUNNER and PC_DISABLE_WP_CRON and the must-use plugin that reads them are a design of this series, not a WooCommerce feature; the filter and the constant they set are the documented ones. The sizing of the worker is a starting point. WooCommerce 11.3.0 was in beta (2026-10-26) on the check date and was not tested.