WooCommerce 10.9 E-commerce Platform on WordPress on Ubuntu 24.04 on Azure User Guide
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.phpandreadme.htmlare 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

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.

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.

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.

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.

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.

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.

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

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);

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:3306and 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.phpandreadme.htmlreturn 403;xmlrpc.phpin 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.