E-commerce Azure

Mercur on Ubuntu 24.04 on Azure User Guide

| Product: Mercur on Ubuntu 24.04 LTS on Azure

Overview

Mercur is an open source multi vendor marketplace platform built on the Medusa v2 commerce engine. Where a conventional store has one catalogue and one owner, Mercur adds the seller as a first class concept: vendors register themselves, the marketplace operator approves them, and each vendor then manages their own products, offers, promotions, stock and orders from a dedicated panel. The operator keeps central oversight of every store, order and payout. Because it is built on Medusa, the whole marketplace is also a headless REST API that any storefront, mobile app or point of sale client can consume.

The cloudimg image builds Mercur 2.2.1 and both of its web panels ahead of time and bakes them into the image, so the marketplace answers as soon as the VM finishes its first boot, with nothing to compile, download or configure. Backed by 24/7 cloudimg support.

What is included:

  • Mercur 2.2.1 (MIT licensed) with the marketplace REST API, the admin panel and the vendor panel, all served by one Node process
  • The admin panel at /dashboard for the marketplace operator, and the vendor panel at /seller for individual sellers, with seller self registration enabled
  • PostgreSQL 16 and Redis 7 on the same VM, both bound to the loopback interface
  • nginx as a reverse proxy on port 80, so the marketplace, both panels and the API share one origin
  • A unique administrator account, database password, token signing secret and cookie secret generated on the first boot of every VM
  • mercur-set-origin, a helper that points the appliance at your own domain or TLS terminator without a rebuild
  • Node.js 22 and a fully patched Ubuntu 24.04 LTS base with unattended security upgrades enabled

Key facts:

Item Value
Platform Ubuntu 24.04 LTS
Default SSH user azureuser
Marketplace API http://<vm-ip>/
Admin panel http://<vm-ip>/dashboard
Vendor panel http://<vm-ip>/seller
Application directory /opt/mercur
Credentials file /root/mercur-credentials.txt
Recommended VM size Standard_B2s (2 vCPU, 4 GiB)

Prerequisites

  1. An Azure subscription with permission to create virtual machines.
  2. An SSH key pair for the azureuser account.
  3. A network security group that allows inbound TCP 22 for SSH and TCP 80 for the marketplace.
  4. Recommended VM size: Standard_B2s (2 vCPU, 4 GiB). The whole stack runs comfortably inside that envelope.

Step 1 - Deploy from the Azure Marketplace

  1. In the Azure portal, choose Create a resource and search for Mercur on Ubuntu 24.04 LTS by cloudimg.
  2. Choose Create, then select your subscription, resource group and region.
  3. Set the VM size to Standard_B2s, choose SSH public key as the authentication type and set the username to azureuser.
  4. On the Networking tab, allow inbound SSH (22) and HTTP (80).
  5. Choose Review + create, then Create.

Step 2 - Deploy from the Azure CLI

Run these on your own workstation, where the Azure CLI is installed and signed in. They create the VM, open the marketplace port and print the public IP address:

az vm create \
  --resource-group my-resource-group \
  --name mercur-01 \
  --image cloudimg:mercur:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group my-resource-group --name mercur-01 --port 80

az vm show --resource-group my-resource-group --name mercur-01 \
  --show-details --query publicIps --output tsv

Step 3 - Connect to your VM

ssh azureuser@<vm-ip>

Step 4 - Confirm the marketplace is running

First boot generates every per VM secret and starts the marketplace. Confirm it has completed:

test -f /var/lib/cloudimg/mercur-firstboot.done && echo "first boot: complete"

Expected output:

first boot: complete

Check the services:

systemctl is-active mercur.service postgresql redis-server nginx

Expected output:

active
active
active
active

Terminal output showing mercur.service, PostgreSQL, Redis and nginx all reporting active, first boot complete, and the health endpoint returning HTTP 200

Check the health endpoint:

curl -s -o /dev/null -w 'health endpoint: HTTP %{http_code}\n' http://127.0.0.1/healthz

Expected output:

health endpoint: HTTP 200

Confirm both panels are being served:

curl -s -o /dev/null -w '/dashboard: HTTP %{http_code}\n' -L http://127.0.0.1/dashboard
curl -s -o /dev/null -w '/seller: HTTP %{http_code}\n' -L http://127.0.0.1/seller

Expected output:

/dashboard: HTTP 200
/seller: HTTP 200

Step 5 - Retrieve the administrator password

The image ships with no usable account. First boot creates a single administrator with a password unique to your VM and writes it to a root only file:

sudo stat -c '%a %U:%G' /root/mercur-credentials.txt

Expected output:

600 root:root

Read the credentials:

sudo cat /root/mercur-credentials.txt

The file contains your marketplace URLs, the administrator email address and the generated password.

Terminal output showing the credentials file is mode 600 root:root, listing the marketplace URLs and administrator email with the password masked, and confirming the token signing and cookie secrets are per VM values rather than the upstream default

Step 6 - Sign in to the admin panel

Open http://<vm-ip>/dashboard in a browser and sign in with the administrator email address and password from Step 5.

The Mercur admin panel sign in screen, showing the Welcome to Mercur heading with email and password fields and a Continue with Email button

After signing in you land on the marketplace console. The left hand navigation carries the ordinary commerce sections plus the two that make this a marketplace rather than a single store: Stores, the registry of every vendor, and Payouts, what each vendor is owed.

The Mercur admin panel after signing in, showing the Orders view with the marketplace navigation including Orders, Products, Inventory, Customers, Promotions, Price Lists, Stores and Payouts

Step 7 - Onboard your first vendor

Vendors register themselves through the vendor panel. Open http://<vm-ip>/seller in a browser and choose Create an account.

The Mercur vendor panel sign in screen, with email and password fields, a Log in button, and a Create an account link for sellers who do not yet have a store

You can also register a vendor from the command line. This creates a member account and then the store it owns:

TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"email":"vendor@example.com","password":"Choose-A-Strong-Password"}' \
  http://127.0.0.1/auth/member/emailpass/register | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
echo "member registered: ${TOKEN:+yes}"

Expected output:

member registered: yes

Create the store that member owns:

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"Harbourside Supply Co.","email":"vendor@example.com","handle":"harbourside-supply","currency_code":"gbp"}' \
  http://127.0.0.1/vendor/sellers | sed -n 's/.*"status":"\([^"]*\)".*/store status: \1/p'

Expected output:

store status: pending_approval

A newly registered store arrives as Pending and cannot trade until you approve it. That approval step is the marketplace operator's control over who sells on the platform.

Step 8 - Approve the store

In the admin panel choose Stores. The vendor you just registered appears in the registry with a Pending status.

The Stores view in the Mercur admin panel listing Harbourside Supply Co. with its email address and a Pending status, awaiting approval by the marketplace operator

Use the row actions to review and approve the store. Once approved, the vendor can sign in at /seller and start listing products. Each vendor sees only their own catalogue and orders, while you keep the full view across every store.

Products added by your vendors appear under Products, where Offers records which vendor sells which product and at what price. That is the mechanism that lets several independent sellers list against the same catalogue entry.

The Products view in the Mercur admin panel, showing the Products, Offers, Collections and Categories sections ready for your vendors first listings

Step 9 - Point the marketplace at your own domain

The appliance serves both panels from the same origin as the API, so it works on the VM address with no configuration. When you put it behind a domain name, a load balancer or a TLS terminator, tell it the public origin so that CORS and uploaded file URLs match:

sudo mercur-set-origin https://shop.example.com

The helper updates the marketplace configuration and restarts the service. Run it again at any time if the address changes. To return to the default behaviour where the panels follow whatever address the browser used:

sudo mercur-set-origin --same-origin http://127.0.0.1

Expected output ends with:

MERCUR_SET_ORIGIN_OK same-origin http://127.0.0.1

Security

The image contains no usable credential. Every secret is generated on the first boot of your VM:

sudo /usr/local/lib/mercur/mercur-start-guard.sh

Expected output:

mercur-start-guard: OK - per-VM secrets in effect

That guard runs before the marketplace starts, on every start. It refuses to run if the token signing secret or cookie secret is empty, is the value published in the upstream project, or still holds a build time value. Upstream's demo catalogue, which ships a published seller login, is never installed.

Terminal output showing the start guard reporting per VM secrets in effect, the upstream default password rejected with HTTP 401, no published demo seller in the database, and PostgreSQL and Redis listening only on the loopback interface

Recommended hardening:

  1. Restrict the network security group to the addresses that need it. Allow TCP 80 only from your users, and TCP 22 only from your administrators. The marketplace process also listens on port 9000 directly; leave that port closed in the network security group so all traffic arrives through nginx.
  2. Put a TLS certificate in front of the appliance and run mercur-set-origin https://your-domain. The session cookie automatically becomes a Secure cookie as soon as the proxy in front sets X-Forwarded-Proto: https, so no further change is needed.
  3. Change the administrator password after your first sign in, from the account menu in the admin panel.
  4. Take regular backups of the PostgreSQL database, which holds every store, product, order and payout record.

Maintenance

Restart the marketplace:

sudo systemctl restart mercur.service

Check the service state:

sudo systemctl status mercur.service --no-pager | head -4

To read the application log, use sudo journalctl -u mercur.service -n 50 --no-pager, or follow it live with sudo journalctl -u mercur.service -f.

Back up the marketplace database:

sudo -u postgres pg_dump mercur | gzip > ~/mercur-backup.sql.gz
ls -lh ~/mercur-backup.sql.gz

The base image applies security updates automatically through unattended upgrades.

Terminal output showing the first boot service enabled, unattended upgrades active, the running kernel, no packages held back from security updates, and the memory footprint of the whole stack

Server components

Component Version Purpose
Mercur 2.2.1 Multi vendor marketplace platform
Node.js 22.x Runtime for the marketplace API and panels
PostgreSQL 16 Stores, products, orders, payouts
Redis 7 Cache, event bus, workflow engine and locking
nginx 1.24 Reverse proxy on port 80

Scripts and log files

Path Purpose
/opt/mercur Application directory
/opt/mercur/packages/api/.medusa/server Built marketplace server and both panels
/opt/mercur/panels-pristine Pristine panel bundles used by mercur-set-origin
/root/mercur-credentials.txt Per VM administrator credentials
/usr/local/bin/mercur-set-origin Points the appliance at a public origin
/stage/scripts/mercur-firstboot.sh First boot provisioning script
/var/log/cloudimg-firstboot.log First boot log

On startup

mercur-firstboot.service runs once, on the very first boot of your VM. It generates the database password, the token signing secret, the cookie secret and the administrator password, points the panels at this VM, applies any outstanding database migrations, and only then writes the marker that permits mercur.service to start. It disables itself afterwards, so later reboots go straight to the marketplace.

Confirm it ran and then stood down:

test -f /var/lib/cloudimg/mercur-firstboot.done && echo "first boot: complete"
systemctl is-enabled mercur-firstboot.service || true

Expected output:

first boot: complete
disabled

disabled is the expected state here: the unit removes itself from the boot sequence once it has provisioned the VM. systemctl reports a non zero exit status for a disabled unit, which is why the command above ends with || true.

Read the first boot log with sudo cat /var/log/cloudimg-firstboot.log if you need to see what it did.

Troubleshooting

The panels bounce back to the sign in page. The marketplace signs users in with a session cookie. If you have placed a TLS terminator in front of the appliance, make sure it forwards X-Forwarded-Proto so the cookie is issued correctly, then run sudo mercur-set-origin https://your-domain.

The marketplace does not answer on port 80. Check that first boot finished and the service started:

systemctl is-active mercur.service
sudo journalctl -u mercur-firstboot.service -n 20 --no-pager

The service refuses to start. The start guard blocks startup if the per VM secrets are missing. Run it directly to see which check failed:

sudo /usr/local/lib/mercur/mercur-start-guard.sh

Uploaded product images do not display. The address baked into uploaded file URLs is set on first boot. If you have since moved the appliance to a domain name, run sudo mercur-set-origin https://your-domain.

Support

cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription ID and the VM name.

Mercur is open source software licensed under the MIT licence. cloudimg is not affiliated with or endorsed by Mercur or Medusa.