Drupal Commerce on Ubuntu 24.04 on Azure User Guide
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 fully installed on Ubuntu 24.04 — 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 24.04 main and universe repositories with no third-party APT repositories. The site is installed onto your own VM 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_paymentandcommerce_tax - Apache 2.4 with
mod_rewrite, MySQL 8.0, and PHP 8.3 with themysql,gd,mbstring,xml,curl,zip,intlandbcmathextensions - Drush 13 at
/usr/local/bin/drushfor command line administration - A default store created at first boot, so products are immediately sellable
- Per-VM administrator, application database and MySQL root passwords, all generated on first boot and written to a root-only file — no shared or default credential ships in the image
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet with a subnet. Standard_B2s (2 vCPU, 4 GB RAM) is the recommended size and is sufficient for a low to medium traffic store; move to a D2s or D4s size for larger catalogs, heavy concurrent checkout traffic, or many contributed modules.
Allow inbound TCP 80 from the addresses that should reach the storefront, and TCP 22 for administration.
Step 1 — Deploy from the Azure Marketplace
In the Azure portal choose Create a resource, search for Drupal Commerce on Ubuntu 24.04 LTS by cloudimg, and select Create. Pick your subscription, resource group and region, choose the Standard_B2s size, supply your SSH public key for the azureuser account, and allow inbound ports 22 and 80.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group my-resource-group \
--name my-drupal-commerce \
--image cloudimg1702aborea:drupal-commerce-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Step 3 — Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 — Confirm the services are running
Both services that back Drupal Commerce should report active, the first-boot service should report inactive (it is a one-shot that has already completed), and the storefront answers HTTP 200:
systemctl is-active apache2 mysql drupal-commerce-firstboot
ss -tlnH | awk '{print $1, $4}' | grep -E ':80$|:3306$'
curl -s -o /dev/null -w 'storefront -> %{http_code}\n' http://127.0.0.1/
Expected: active, active, then inactive for the completed one-shot, the two listening sockets, and storefront -> 200. Note that MySQL listens on 127.0.0.1:3306 only — the database is not exposed to the network.

Step 5 — Retrieve your administrator credentials
On the first boot of every VM, a one-shot service (drupal-commerce-firstboot.service) generates credentials that are unique to that VM: a fresh MySQL root password, a fresh application database password, and a fresh Drupal administrator password. It then installs the site, enables the Commerce modules and creates the default store. No shared or default credential ships in the image.
sudo cat /stage/scripts/drupal-commerce-credentials.log
sudo stat -c '%a %U:%G %n' /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.

You can prove the database credentials really authenticate — and that a wrong password is genuinely rejected — from the VM's own shell:
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 6 — Visit the storefront
Browse to http://<vm-public-ip>/. The storefront is served by the Olivero front-end theme and is ready for your own content and products.

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 VM's public address, its private address, or a custom domain you point at it later — with no reconfiguration. You can see that for yourself:
PRIVATE_IP=$(hostname -I | awk '{print $1}')
curl -s -o /dev/null -w 'public request -> %{http_code}\n' -H "Host: shop.example.com" http://127.0.0.1/user/login
curl -s -o /dev/null -w 'private address -> %{http_code}\n' "http://${PRIVATE_IP}/user/login"
Expected: 200 for both.
Step 7 — Sign in as the administrator
Browse to http://<vm-public-ip>/user/login and sign in with the username cloudimg and the password from the credentials file.

Step 8 — The Commerce dashboard
After signing in, 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.

Step 9 — Review your store
A default store, cloudimg Demo Store, is created on first boot so that products are immediately sellable — Commerce requires a store before anything can be sold. Find it under Commerce → Configuration → Stores. Select Edit to set your own store name, email address, postal address, default currency and billing countries.

Step 10 — Create 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.

Step 11 — 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 12 — 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 VM it generates the per-VM 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.
systemctl status drupal-commerce-firstboot.service --no-pager | head -5
sudo test -f /var/lib/cloudimg/drupal-commerce-firstboot.done && echo "sentinel present — first boot completed"
Because every secret is generated on your own VM, no two cloudimg deployments of this image share a credential, and nothing sensitive exists in the published image.
Administration from the command line
Drush is installed for routine administration:
cd /var/www/html/drupal && sudo -u www-data /usr/local/bin/drush status | head -12
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 VM 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.
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 certified 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.
Two advisory notices appear on Drupal's status report on this platform, neither of which is a fault:
- HTML5 validation — the image explicitly sets
enable_html5_validationtoTRUEto preserve current form behaviour. Drupal notes that this setting is removed in Drupal 13; the notice is a forward-compatibility advisory, not an error. - GD library / AVIF — the PHP GD build shipped by Ubuntu 24.04 supports GIF, JPEG, PNG and WebP but not AVIF, because the distribution package is built without that codec. All common web image formats are supported.
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 | 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 Azure subscription ID and the VM name.