E-commerce AWS

Drupal Commerce on AWS User Guide

| Product: Drupal Commerce

Overview

Drupal Commerce is the open source e-commerce framework built on Drupal. Rather than bolting a shop onto a content system, it models commerce natively: products and variations, carts and orders, a configurable checkout flow, flexible pricing and promotions, tax and currency handling, and pluggable payment gateways are all first class Drupal entities. Every commerce object is fieldable, referenceable and viewable with the same tools you use for the rest of the site, so catalog data and editorial content are modelled together instead of synchronised between systems.

The cloudimg image delivers Drupal Commerce on a LAMP stack: Drupal core 11.4.4 with Drupal Commerce 3.3.8, served by Apache 2.4 with PHP 8.3 and backed by MySQL 8.0, all from the Ubuntu main and universe repositories with no third-party APT repositories. The site is installed onto your own instance at first boot, with the Commerce modules already enabled and a default store already created, so you land on a working storefront and Commerce admin rather than an installer. Backed by 24/7 cloudimg support.

What is included:

  • Drupal core 11.4.4 (GPL-2.0-or-later) at /var/www/html/drupal, installed from the official ftp.drupal.org tarball and verified against the publisher's own checksum at build time
  • Drupal Commerce 3.3.8 (Commerce Core, GPL-2.0-or-later) installed via Composer, with nine Commerce modules enabled: commerce, commerce_price, commerce_store, commerce_product, commerce_order, commerce_cart, commerce_checkout, commerce_payment and commerce_tax
  • Apache 2.4 with mod_rewrite, MySQL 8.0, and PHP 8.3 with the mysql, gd, mbstring, xml, curl, zip, intl and bcmath extensions
  • Drush 13 at /usr/local/bin/drush for command line administration
  • A default store created at first boot, so products are immediately sellable
  • Two dedicated EBS data volumes: the MySQL datadir at /var/lib/mysql and the Drupal application tier at /var/www, each independently resizable
  • Per-instance administrator, application database and MySQL root passwords, all generated on first boot and written to a root-only file, plus a Drupal site installed fresh per instance (unique admin, unique security hash salt) — no shared or default credential ships in the image
  • 24/7 cloudimg support

Connecting to your instance

Connect over SSH on port 22 using the private key for the key pair you selected at launch. The login user depends on the operating system variant you launched:

OS variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i /path/to/your-key.pem ubuntu@<instance-public-ip>

Prerequisites

An AWS account, an EC2 key pair, and a VPC subnet. m5.large (2 vCPU, 8 GB RAM) is the recommended instance type and is sufficient for a low to medium traffic store; move to an m5.xlarge or larger for large catalogs, heavy concurrent checkout traffic, or many contributed modules.

In the instance's security group, allow inbound TCP 80 from the addresses that should reach the storefront, and TCP 22 for administration. To serve HTTPS later, also allow TCP 443.

Step 1 - Launch from AWS Marketplace

In the AWS Marketplace console find Drupal Commerce by cloudimg and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch. Pick your region, the m5.large instance type, your VPC subnet, your key pair, and a security group that allows inbound 22 and 80.

Step 2 - Launch from the EC2 CLI

aws ec2 run-instances \
  --image-id <ami-id-from-the-listing> \
  --instance-type m5.large \
  --key-name your-key \
  --security-group-ids sg-xxxxxxxx \
  --subnet-id subnet-xxxxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=drupal-commerce}]'

Step 3 - Confirm the services are running

Both services that back Drupal Commerce should report active, and the storefront answers HTTP 200. MySQL listens on 127.0.0.1:3306 only, so the database is never exposed to the network:

for s in apache2 mysql; do echo "$s -> $(systemctl is-active "$s")"; done
ss -tlnH | awk '{print $4}' | grep -E ':80$|:3306$'
curl -s -o /dev/null -w 'storefront -> HTTP %{http_code}\n' http://127.0.0.1/

Expected: apache2 -> active, mysql -> active, the two listening sockets, and storefront -> HTTP 200.

Step 4 - Retrieve your administrator credentials

On the first boot of every instance, a one-shot service (drupal-commerce-firstboot.service) generates credentials that are unique to that instance: a fresh MySQL root password, a fresh application database password, and a fresh Drupal administrator password. It then installs the site (so the security hash salt and the administrator account are unique too), enables the Commerce modules and creates the default store. No shared or default credential ships in the image.

sudo stat -c '%a %U:%G %n' /stage/scripts/drupal-commerce-credentials.log
sudo awk -F= '/^DRUPAL_ADMIN_USER=/{print} /^DRUPAL_COMMERCE_URL=/{print} /^DRUPAL_COMMERCE_ADMIN_URL=/{print}' /stage/scripts/drupal-commerce-credentials.log

The file is mode 0600 and owned by root, so only root can read it. It contains the storefront and admin URLs, the administrator username (cloudimg) and password, and the database credentials. Read the administrator password itself with sudo cat /stage/scripts/drupal-commerce-credentials.log.

You can prove the database credentials really authenticate - and that a wrong password is genuinely rejected - from the instance's own shell, without printing any secret:

DB_USER=$(sudo awk -F= '$1=="DRUPAL_DB_USER"{print $2}' /stage/scripts/drupal-commerce-credentials.log)
DB_PASS=$(sudo awk -F= '$1=="DRUPAL_DB_PASSWORD"{print $2}' /stage/scripts/drupal-commerce-credentials.log)
OK=$(mysql -u "$DB_USER" -p"$DB_PASS" -D drupalcommerce -N -B -e 'SELECT 1' 2>/dev/null || true)
BAD=$(mysql -u "$DB_USER" -p'wrong-password' -D drupalcommerce -N -B -e 'SELECT 1' 2>/dev/null || true)
echo "correct password -> ${OK:-rejected}"
echo "wrong password   -> ${BAD:-rejected}"

Expected: correct password -> 1 and wrong password -> rejected.

Step 5 - Visit the storefront

Browse to http://<instance-public-ip>/product/1 (or any product) to see the storefront, served by the Olivero front-end theme with a price and an add-to-cart action. The store is ready for your own products and content.

A Drupal Commerce storefront product page

The image does not bake an address into the site configuration. Drupal derives the base URL from the incoming request, so the storefront answers correctly on the instance's public address, its private address, or a custom domain you point at it later - with no reconfiguration and no "host not trusted" error. You can see that for yourself:

PRIVATE_IP=$(hostname -I | awk '{print $1}')
curl -s -o /dev/null -w 'custom host   -> HTTP %{http_code}\n' -H "Host: shop.example.com" http://127.0.0.1/user/login
curl -s -o /dev/null -w 'private addr  -> HTTP %{http_code}\n' "http://${PRIVATE_IP}/user/login"

Expected: 200 for both.

Step 6 - The shopping cart

Adding a product places it in the cart, where the customer can adjust quantities before checking out.

The Drupal Commerce shopping cart

Step 7 - Checkout

The default checkout flow offers a returning-customer login and a guest checkout, then collects order information and payment. Add a payment gateway under Commerce -> Configuration -> Payment gateways to take live payments.

The Drupal Commerce checkout

Step 8 - Sign in as the administrator

Browse to http://<instance-public-ip>/user/login and sign in with the username cloudimg and the password from the credentials file. Then choose Commerce in the administration menu: the dashboard gives you Orders, Products and Configuration, alongside store performance panels for carts, placed orders, gross sales and best selling products.

The Drupal Commerce administration dashboard

You can confirm the administrator account from the command line:

cd /var/www/html/drupal && sudo -u www-data /usr/local/bin/drush user:information cloudimg --field=name

Step 9 - Add your first product

Choose Commerce -> Products -> Add product. Give the product a title and description, then use Save and add variations to create the purchasable variations, each with its own SKU and price. Variations are what customers actually add to the cart, so a single product can carry several sizes, colours or editions.

Editing a product in the Drupal Commerce administration area

Step 10 - Confirm the versions

cd /var/www/html/drupal && sudo -u www-data /usr/local/bin/drush status --field=drupal-version
php -r '$l=json_decode(file_get_contents("/var/www/html/drupal/composer.lock"),true); foreach($l["packages"] as $p){ if($p["name"]==="drupal/commerce") printf("drupal/commerce %s\n",$p["version"]); }'
php -v | head -1

Expected: 11.4.4, drupal/commerce 3.3.8, and PHP 8.3.

Step 11 - Confirm Commerce is installed

cd /var/www/html/drupal && sudo -u www-data /usr/local/bin/drush pm:list --status=enabled --filter=commerce --field=name
cd /var/www/html/drupal && sudo -u www-data /usr/local/bin/drush php:eval 'printf("stores configured: %d\n", count(\Drupal::entityTypeManager()->getStorage("commerce_store")->loadMultiple()));'

Expected: the enabled Commerce modules, then stores configured: 1.

First-boot service and security model

drupal-commerce-firstboot.service is a one-shot unit ordered After=mysql.service apache2.service. On the first boot of each instance it generates the per-instance secrets, provisions the application database, installs the site, enables the Commerce modules, creates the default store, writes the credentials file at mode 0600, and drops a sentinel at /var/lib/cloudimg/drupal-commerce-firstboot.done so it runs exactly once. It resolves the instance's public address via EC2 IMDSv2 for the credentials-file URL, falling back to the private address.

sudo test -f /var/lib/cloudimg/drupal-commerce-firstboot.done && echo "sentinel present - first boot completed"

Because every secret and the whole site are generated on your own instance, no two cloudimg deployments of this image share a credential or a hash salt, and nothing sensitive exists in the published image.

Administration from the command line

Drush is installed for routine administration. To reset the administrator password, or to add another administrator:

cd /var/www/html/drupal
sudo -u www-data /usr/local/bin/drush user:password cloudimg 'your-new-password'
sudo -u www-data /usr/local/bin/drush user:create alice --mail=alice@example.com --password='her-password'
sudo -u www-data /usr/local/bin/drush user:role:add administrator alice

Using your own domain and HTTPS

The image serves plain HTTP on port 80 so that it works immediately on any address. For a production storefront, point your domain at the instance and terminate TLS. The quickest route is Certbot with the Apache plugin:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-apache
sudo certbot --apache -d shop.example.com

Because no address is baked into the site configuration, the storefront starts answering on your domain as soon as DNS resolves - there is nothing to reconfigure in Drupal. Alternatively, place an Application Load Balancer with an AWS Certificate Manager certificate in front of the instance.

For a production site you should also tighten the trusted host pattern. The image ships a permissive pattern so the storefront works on any address out of the box; once you have settled on your domain, edit /var/www/html/drupal/sites/default/settings.php and replace it:

$settings['trusted_host_patterns'] = ['^shop\.example\.com$'];

Keeping Drupal and Commerce up to date

Both Drupal core and Drupal Commerce are pinned to the versions verified at build time, so the image you launched is the image that runs. Apply upstream updates with Composer when you are ready:

cd /var/www/html/drupal
sudo -u www-data composer update drupal/core-recommended --with-all-dependencies
sudo -u www-data composer update drupal/commerce --with-all-dependencies
sudo -u www-data /usr/local/bin/drush updatedb -y
sudo -u www-data /usr/local/bin/drush cache:rebuild

Always take a backup first (see below), and test updates outside production.

Backup and maintenance

Back up the database and the site files:

cd /var/www/html/drupal
sudo -u www-data /usr/local/bin/drush sql:dump --result-file=/var/backups/drupalcommerce-$(date +%F).sql
sudo tar czf /var/backups/drupal-files-$(date +%F).tar.gz /var/www/html/drupal/sites/default/files

The operating system receives unattended security upgrades. Check for pending updates with:

apt-get -s -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade 2>/dev/null | grep -c '^Inst ' || true

Licensing

Drupal core and Drupal Commerce are both distributed under the GNU General Public License, version 2 or later (GPL-2.0-or-later). The licence text ships in the image at /var/www/html/drupal/LICENSE.txt and /var/www/html/drupal/modules/contrib/commerce/LICENSE.txt. cloudimg charges only for the packaging, maintenance and support of the image; the software itself remains under its open source licence.

Support

Every cloudimg image includes 24/7 support. Contact support@cloudimg.co.uk with your AWS account ID and the instance ID.