W1
E-commerce Azure

WooCommerce 10.9 E-commerce Platform on WordPress on Ubuntu 24.04 on Azure User Guide

| Product: WooCommerce 10.9 E-commerce Platform on WordPress on Ubuntu 24.04 LTS on Azure

Overview

WooCommerce is the most widely deployed open source e-commerce platform on the web. It is built as a WordPress plugin, so your store and your content live in one place, with full ownership of your data and no per transaction platform fees. It gives you a complete storefront: product catalog and inventory management, flexible shipping and tax handling, coupons and promotions, order and customer management, and an ecosystem of thousands of extensions and payment gateways including Stripe and PayPal.

The cloudimg image ships WooCommerce 10.9.4 on WordPress 7.0.1, served by Apache and PHP 8.3 on a hardened, fully patched Ubuntu 24.04 LTS base, together with a bundled local MariaDB server so the appliance is complete and useful on its own. The bundled MariaDB listens only on the loopback interface and keeps its data, together with your uploaded product images, on a dedicated Azure data disk. No administrator password, database password or WordPress authentication key exists anywhere in the image: WordPress and WooCommerce install themselves on your own instance at first boot, generating a unique administrator password, a unique database password and a unique full set of authentication salts for that instance. Backed by 24/7 cloudimg support.

What is included:

  • WordPress 7.0.1 and WooCommerce 10.9.4, both installed from the official upstream distributions and verified by checksum at build time
  • Apache 2.4 with PHP 8.3, OPcache and every extension WordPress and WooCommerce need (including Imagick for product imagery)
  • MariaDB bound to 127.0.0.1:3306, with its data directory on a dedicated 20 GiB data disk
  • Your uploaded product images and media library on that same dedicated data disk
  • A unique administrator password, database password and full set of WordPress salts and keys generated on the first boot of every VM
  • Pretty permalinks preconfigured, and the site URL derived per request so it travels with your VM
  • WordPress and WooCommerce scheduled events driven by a systemd timer rather than by visitor traffic
  • Application auto updates disabled so the certified image does not mutate itself, with a documented manual update path
  • Hardening past a stock install: PHP cannot execute from the writable uploads tree, and wp-config.php, xmlrpc.php and readme.html are denied from the web

Prerequisites

  • An Azure subscription and permission to create virtual machines
  • The Azure CLI installed and signed in (az login), if you deploy from the command line
  • An SSH key pair for the VM administrator account (azureuser)
  • Inbound TCP 80 (and 443 once you add TLS) open to your shoppers, plus TCP 22 for administration

Step 1 - Deploy from the Azure Marketplace

Find WooCommerce 10.9 E-commerce Platform on WordPress on Ubuntu 24.04 LTS by cloudimg in the Azure Marketplace and select Get It Now. Choose your subscription, resource group and region, keep the recommended Standard_B2s size or larger, set the administrator username to azureuser with your SSH public key, and allow inbound 80 and 22 (and 443 once you attach TLS). The image attaches its own 20 GiB data disk automatically.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group my-woocommerce-rg \
  --name my-woocommerce-vm \
  --image cloudimg:woocommerce-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group my-woocommerce-rg --name my-woocommerce-vm --port 80 --priority 1001

First boot installs your store for this VM: it creates the database, generates wp-config.php with a fresh set of authentication salts, installs WordPress with a single administrator account whose password is generated there and then, activates WooCommerce, and writes the credentials to a root only file. That takes roughly 15 to 30 seconds after the VM reports as running.

Step 3 - Confirm the services are running

SSH in as azureuser and confirm Apache and MariaDB are up, the scheduler is armed, and the ports are as expected. WooCommerce answers on port 80; MariaDB is bound to loopback only and is not reachable from outside the VM.

systemctl is-active apache2 mariadb woocommerce-firstboot
active
active
active
systemctl is-enabled woocommerce-cron.timer
enabled

The versions in the image are pinned; confirm them with WP-CLI:

sudo -u www-data wp --path=/var/www/wordpress core version
sudo -u www-data wp --path=/var/www/wordpress plugin get woocommerce --field=version
7.0.1
10.9.4

WooCommerce services, scheduler timer, listening ports and pinned versions on the cloudimg Azure image

Step 4 - Retrieve the per VM administrator password

No password ships in the image. Your instance generated its own on first boot and wrote it to a file only root can read.

sudo cat /root/woocommerce-credentials.txt

The file gives you WP_ADMIN_USER (which is cloudimg), WP_ADMIN_PASSWORD, the store URL, and the internal database credentials. The MariaDB root account authenticates through the local unix socket, so there is no root database password to ship or to manage; run sudo mysql when you need it.

The per VM WooCommerce credentials file, readable only by root, and the single administrator account

Step 5 - Sign in

Browse to http:// followed by your VM's public IP address or DNS name, then add /wp-admin/. Sign in with the username cloudimg and the password from Step 4.

The WordPress sign in page for the WooCommerce store

You land in the WordPress dashboard, with the WooCommerce, Products, Payments, Analytics and Marketing menus in the sidebar. The dashboard notes that the site is not yet using a secure connection; Step 10 covers attaching a custom domain and a Let's Encrypt certificate. Your new store also starts in Coming soon mode, shown by the badge in the toolbar, so the public storefront shows a launch pending page until you are ready to open for business.

The WordPress dashboard with the WooCommerce menus, signed in as the per VM administrator

Change your administrator password once you are in: open the user menu at the top right, choose Edit Profile, and set a new password. The password from Step 4 is a bootstrap credential unique to your instance, and replacing it with your own is good practice.

Step 6 - Run the store setup wizard

The first time you open WooCommerce it offers a short guided setup that captures your store address, the currency you sell in, and the kind of products you sell. Work through it, or choose Skip guided setup and configure things by hand later.

The WooCommerce store setup wizard on first run

Step 7 - Add your products

Select Products then Add new product to build your catalog. Each product carries a name, description, price, SKU, stock level, images and categories; WooCommerce supports simple products, variable products with options such as size and colour, grouped products and downloadable or virtual products.

The WooCommerce products list with three products in stock

Product images you upload are stored on the dedicated data disk, alongside your database, so your catalog and its media survive independently of the operating system disk (Step 12).

Step 8 - Configure payments, shipping and tax

Open WooCommerce then Settings to set your store address, selling locations, currency, shipping zones and tax rules. The Payments tab is where you connect gateways such as Stripe or PayPal so you can take real orders; the Shipping tab defines the rates and zones you deliver to.

The WooCommerce settings screen with the store management menu

When your catalog, payments and shipping are ready, turn off Coming soon mode from WooCommerce then Settings then Site visibility (or the toolbar badge) to open your store to the public.

Step 9 - The site URL travels with your VM

WordPress normally stores its site address in the database, which is what makes a WordPress site painful to move between addresses. This image does it differently: the site URL is derived on every request from the host you browse to. Stopping and starting the VM (which changes its public IP), attaching a custom domain, or putting a load balancer or TLS proxy in front all work with no reconfiguration, and old addresses are never left baked into your pages or your emails.

You can see this in action. Ask the login page what host it thinks it is on, using two different Host headers, and each answer reflects the header you sent:

curl -s -H 'Host: shop.example-a.test' http://127.0.0.1/wp-login.php | grep -o 'http://shop.example-a.test' | head -1
curl -s -H 'Host: shop.example-b.test' http://127.0.0.1/wp-login.php | grep -o 'http://shop.example-b.test' | head -1
http://shop.example-a.test
http://shop.example-b.test

The host is validated against a strict pattern before it is used, so a malformed or spoofed Host header cannot poison your links; command line contexts such as the scheduler fall back to the VM's own current address.

Step 10 - Attach a custom domain and TLS

The store runs over plain HTTP out of the box so it is reachable the moment it boots. For a real shop you want your own domain and HTTPS. Point a DNS A record at the VM's public IP, open port 80 and 443, then install a Let's Encrypt certificate:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-apache
sudo certbot --apache -d <your-domain>

Certbot obtains the certificate, configures Apache to serve HTTPS, sets up the HTTP to HTTPS redirect and renews automatically. Because the site URL is derived from the request host, WooCommerce immediately starts serving links as https://<your-domain>/ with nothing else to change. WordPress detects the HTTPS request (including via the X-Forwarded-Proto header when you sit behind a load balancer), so your store, cart and checkout all use secure URLs.

Step 11 - No known or default credentials, and hardening

This image contains no administrator account, no database and no authentication salts at all. Rather than shipping a preinstalled store whose secrets would have to be rotated, WordPress and WooCommerce install themselves on your instance at first boot and generate everything there: the administrator password, the database password, and the full set of eight WordPress salts and keys that sign your login cookies. A shared salt across every VM would let one store forge another's login sessions, so none is ever created at build time in the first place.

You can prove the result on your own VM. Exactly one account exists, and it is an administrator:

sudo -u www-data wp --path=/var/www/wordpress user list --fields=user_login,roles
user_login  roles
cloudimg    administrator

The credential round trip signs in against the real login form with the password from the credentials file, confirms the login cookie is host only (so it works on a bare IP address, which a domain scoped cookie would not), and confirms that blank and common guessed passwords are rejected:

sudo /usr/local/sbin/woocommerce-cred-roundtrip.sh
negative probes rejected (blank/admin/password)
PASS: per-VM credential authenticates; cookies host-only + HttpOnly; weak guesses rejected

Sensitive paths are denied by the web server, and PHP cannot execute from the writable uploads tree where a compromised plugin might try to drop a web shell:

for p in /wp-config.php /xmlrpc.php /readme.html; do
  printf '%-18s HTTP %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1$p)"
done
/wp-config.php     HTTP 403
/xmlrpc.php        HTTP 403
/readme.html       HTTP 403

Proof that the WooCommerce credential round trip passes, the login cookie is host only, and the protected paths are denied

xmlrpc.php is blocked because it is WordPress's classic brute force and pingback amplification surface and the modern REST API supersedes it. If you run an integration that genuinely needs XML-RPC, remove the <Files "xmlrpc.php"> deny block from /etc/apache2/sites-available/woocommerce.conf and reload Apache.

Step 12 - The scheduled events

WordPress and WooCommerce rely on scheduled events: WooCommerce's Action Scheduler drains order emails, subscription renewals and other background work through them. On a default WordPress install these fire on visitor traffic, which is unreliable and exposes a public endpoint. This image disables that (DISABLE_WP_CRON) and runs the scheduler from a systemd timer every five minutes instead, as the www-data user.

systemctl is-enabled woocommerce-cron.timer
enabled

Run a pass by hand at any time:

sudo systemctl start woocommerce-cron.service

Step 13 - Where your store data lives

Both the database and your uploaded product images live on the dedicated data disk, not the operating system disk, so they can be snapshotted and grown independently:

findmnt -no SOURCE,TARGET /var/lib/mysql
findmnt -no SOURCE,TARGET /var/www/wordpress/wp-content/uploads
df -h /data | tail -1
/dev/sdc[/mysql] /var/lib/mysql
/dev/sdc[/wordpress-uploads] /var/www/wordpress/wp-content/uploads
/dev/sdc         20G  197M   19G   2% /data

The application itself is frozen so the certified image does not silently rewrite itself. Core, plugin and theme auto updates are disabled in wp-config.php:

sudo grep -E "AUTOMATIC_UPDATER_DISABLED|WP_AUTO_UPDATE_CORE|DISABLE_WP_CRON" /var/www/wordpress/wp-config.php
define('AUTOMATIC_UPDATER_DISABLED', true);
define('WP_AUTO_UPDATE_CORE', false);
define('DISABLE_WP_CRON', true);

The WooCommerce data disk layout for the database and media library, plus the image immutability settings

Your operating system keeps receiving security updates normally through unattended upgrades; only the application is frozen, and you update it deliberately (next section).

Updating WordPress and WooCommerce

Because application auto updates are disabled, you control when WordPress and WooCommerce move to a new version. Always take a backup first (next section), then update with WP-CLI:

sudo -u www-data wp --path=/var/www/wordpress plugin update woocommerce
sudo -u www-data wp --path=/var/www/wordpress core update
sudo -u www-data wp --path=/var/www/wordpress core update-db

Updating from the WordPress admin under Dashboard then Updates works too. Test on a snapshot of the VM before updating a production store.

Backing up

Everything that matters is the MariaDB database plus the uploads tree, and both are on /data. The simplest protection is an Azure snapshot of the data disk. For a logical dump of the database:

sudo mariadb-dump --protocol=socket -uroot --single-transaction wordpress > ~/woocommerce-backup.sql

Combine that with a copy of /data/wordpress-uploads to capture your product images, or snapshot the whole data disk to capture both at once.

Security notes

  • No credential ships in the image. WordPress and WooCommerce install on your instance at first boot, so there is no default account, no shipped password hash and no database to inherit one from.
  • No baked authentication salts. The eight WordPress keys and salts that sign your login cookies are generated per VM at first boot, so no two stores share session signing material.
  • The database is loopback only. MariaDB listens on 127.0.0.1:3306 and is not reachable from outside the VM; the root account uses unix socket authentication, so no root password exists.
  • No PHP execution in the uploads tree. Product images land in a writable directory, so the web server refuses to execute PHP from it, closing the common plugin compromise web shell path.
  • Sensitive paths are denied. wp-config.php, xmlrpc.php and readme.html return 403; xmlrpc.php in particular removes the classic brute force amplification surface.
  • The image does not mutate after you deploy it. Application auto updates are off, so the store you launch is the store that keeps running until you update it deliberately.
  • Scheduled events are not web reachable. They run from a systemd timer via WP-CLI, not on visitor traffic.
  • Restrict inbound 22 to your administration network, add a Let's Encrypt certificate before taking real orders, and consider a login rate limiting plugin.

Trademark

WordPress is a trademark of the WordPress Foundation, and WooCommerce and Woo are trademarks of Automattic Inc. cloudimg is not affiliated with, endorsed by, or sponsored by the WordPress Foundation or Automattic. This image packages the unmodified open source WordPress release (distributed under the GNU General Public License version 2 or later) and the unmodified open source WooCommerce plugin (distributed under the GNU General Public License version 3 or later), together with cloudimg's configuration and hardening. The WordPress and WooCommerce names are used here in their plain text form solely to identify the open source software included in this image, and no WordPress or WooCommerce logo is used.

Support

cloudimg provides 24/7 technical support for this image by email (support@cloudimg.co.uk) and live chat, with a one hour average response time for critical issues. We help with store deployment, the setup wizard, payment gateway and shipping configuration, custom domains and TLS with Let's Encrypt, theme and extension installation, performance tuning, WordPress and WooCommerce updates, backup and restore, and MariaDB administration.