Deployment guide

MatrixOne

Overview

MatrixOne is an open-source HTAP database. One engine serves transactional and analytical SQL, so the same system that records rows can answer aggregate queries over them without a separate warehouse.

It speaks the MySQL wire protocol, so the ordinary mysql client and standard MySQL drivers connect to it. It also adds vector search (a VECF32 column type with l2_distance) and full-text search (FULLTEXT indexes with MATCH ... AGAINST). This image runs MatrixOne 4.2.6 standalone. One mo-service process holds the log service, the transaction node and one compute node, and systemd starts it.

What you get:

  • No default login, and passwords unique to your VM. Upstream MatrixOne creates two built-in accounts, root and dump, both with the well-known password 111, unless you supply a different password at the moment the database is first created. So the image ships no database at all. First boot creates this VM's catalogue on the data disk with a 32-character root password generated on the VM. The password goes to the server on standard input, never on a command line, in a file, a log or the journal. First boot then gives dump its own password. The password 111 never exists on your VM, and first boot proves that both accounts refuse it. Both passwords are written to the root-only file /root/matrixone-credentials.txt.
  • It never starts on an empty data disk. MatrixOne would create a fresh catalogue with 111 on an empty store. So the service refuses to start until first boot has finished, and refuses to start on an empty or replaced data disk, with a clear message in the journal (see The data disk).
  • Every port bound to localhost. The MySQL-protocol port 6001 and every internal port MatrixOne opens (log service, transaction node, compute node, query service) listen on 127.0.0.1 only. MatrixOne's Prometheus endpoint, which upstream binds to every interface, is switched off. From the network, only SSH is reachable. You reach the database through an SSH tunnel (Step 8), or open it deliberately to one network (Step 9).
  • A MatrixOne built for this image. The MatrixOne 4.2.6 release binary was built with Go 1.26.4 and modules that now carry fixed security advisories. So SecureAnyCloud compiles MatrixOne 4.2.6 from its unmodified release source with Go 1.26.10 and the fixed module releases. The build's provenance is on the VM (/usr/share/doc/matrixone/BUILD-PROVENANCE.txt; Step 10).
  • Iceberg connector switched off. MatrixOne's Apache Iceberg connector reads Avro files with a library that has three security advisories with no fixed release (see Security notes). Upstream's default configuration turns the connector on. This image turns it off, so every Iceberg statement is refused before any Avro data is read.
  • Hardened service. MatrixOne runs as an unprivileged matrixone user with no capabilities, a read-only view of the system and no access to /root. MatrixOne and its first-boot service are blocked from the Azure Instance Metadata Service and the Azure WireServer, because MatrixOne can fetch a URL you name from SQL, for example when loading data. The Azure Linux Agent and cloud-init keep their access. The MatrixOne binaries and configuration are owned by root and read-only to the service.
  • Your data on its own disk. The store (/var/lib/matrixone) is on a dedicated 40 GiB Azure data disk that you can resize or snapshot on its own.
  • Fully patched. Ubuntu 24.04 LTS is fully patched at build time, with unattended security upgrades switched on.

What is on the image: MatrixOne 4.2.6 (/opt/matrixone, running as matrixone.service, configuration in /etc/matrixone/launch), the MariaDB mysql client from Ubuntu, a helper matrixone-mysql that signs the client in as root, a demo database with a vector column and a full-text index, Ubuntu 24.04 LTS, cloud-init and the Azure Linux Agent. There is no MySQL or MariaDB server on the image.

What this is not

This is a single-VM MatrixOne. The distributed deployment, with several compute nodes on shared object storage, is out of scope for this image. MatrixOne is compatible with a large part of MySQL 8.0 syntax but is not MySQL. Check Matrix Origin's documentation for the statements your application uses. The image does not claim Iceberg support, because the connector is off.

MatrixOne is built for the x86-64-v3 instruction set (AVX2), like upstream's own release. Current Azure x64 VM sizes have it. On a processor that does not, first boot stops with a clear message and MatrixOne stays stopped.

Prerequisites

  • An Azure subscription, an SSH key pair, and a virtual network and subnet in your region.
  • Standard_B4ms (4 vCPU, 16 GiB RAM) as the recommended starting size. Use a larger size for heavier analytical work.
  • A network security group that allows TCP 22 (SSH) from your admin network. MatrixOne listens on the loopback address only, so no database port needs to be open.
  • For remote SQL access, a MySQL client on your computer. MatrixOne uses the mysql_native_password authentication plugin, so use a MariaDB client or a MySQL 8.0.30+ client. MySQL 9.x clients removed that plugin and cannot connect.
1

Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for MatrixOne by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size (Standard_B4ms or larger). Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) only.

The image brings two disks: the OS disk and a 40 GiB data disk (LUN 0) for /var/lib/matrixone. You can make the data disk larger on the Disks tab. Then choose Review + create, and Create.

First boot takes about a minute once the VM is running. It creates the catalogue with your VM's passwords, then starts MatrixOne.

2

Deploy from the Azure CLI

Accept the Marketplace terms once per subscription, then create the VM with the Azure CLI.

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:matrixone-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name matrixone \
  --image sac1647283583153:matrixone-ubuntu-24-04:default:latest \
  --size Standard_B4ms \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
3

Connect to your VM

Connect over SSH as the administrator user you chose at deployment.

ssh azureuser@<vm-public-ip>

A welcome banner prints the most useful commands.

4

Check the services

Both the first-boot service and MatrixOne itself should be active.

systemctl is-active matrixone-firstboot.service matrixone.service
active active

Every MatrixOne port listens on the loopback address. From the network, only SSH (port 22) is reachable.

sudo ss -Htln | awk '{print $4}' | sort -V
0.0.0.0:22 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:19998 127.0.0.1:22000 127.0.0.1:22001 127.0.0.1:22002 127.0.0.1:32000 127.0.0.1:32001 127.0.0.1:32002 127.0.0.53%lo:53 127.0.0.54:53 [::]:22

6001 is the MySQL-protocol port. The others are MatrixOne's internal services: log service 32000 to 32002, transaction node 22000 to 22002, compute node 6002 and 6003, and query service 19998. The two port 53 lines are Ubuntu's local DNS resolver, also loopback only.

The store is on the dedicated data disk.

findmnt -no SOURCE,TARGET,FSTYPE,SIZE /var/lib/matrixone
/dev/sda /var/lib/matrixone ext4 39.1G

The device name (/dev/sda, /dev/sdc, ...) depends on the VM. The disk is always LUN 0 (/dev/disk/azure/scsi1/lun0) and is mounted by its file system UUID.

5

Get your passwords

Read the generated passwords from the credentials file.

sudo cat /root/matrixone-credentials.txt
# MatrixOne 4.2.6 on Ubuntu 24.04 - generated on first boot by matrixone-firstboot.service
# These passwords are unique to this VM; none was baked into the image. Keep this file secret. MATRIXONE_HOST=127.0.0.1 MATRIXONE_PORT=6001 MATRIXONE_ROOT_USER=root MATRIXONE_ROOT_PASSWORD=<MATRIXONE_ROOT_PASSWORD> MATRIXONE_DUMP_USER=dump MATRIXONE_DUMP_PASSWORD=<your-dump-password> MATRIXONE_SERVER_VERSION=8.0.30-MatrixOne-v4.2.6
# On the VM (the mysql client signed in as root; the password never goes on a command line):
#
sudo matrixone-mysql demo
# From your computer, through an SSH tunnel (MatrixOne listens on 127.0.0.1:6001 only):
# ssh -N -L 6001:127.0.0.1:6001 azureuser@<vm-public-ip>
# mysql -h 127.0.0.1 -P 6001 -u root -p
# User guide: https://secureanycloud.com/stacks/matrixone/

The file is mode 0600 and owned by root. root is MatrixOne's administrator in the sys tenant. dump is the second built-in account that upstream creates. It has its own password, so the two accounts never share one. Keep both passwords in your secrets manager.

6

Connect with the mysql client

matrixone-mysql runs the mysql client signed in as root, with the password from the credentials file. It passes the password to the client in a temporary root-only option file, not on the command line, where any user could read it in the process list.

sudo matrixone-mysql demo -e 'SELECT version(); SELECT current_user();'
version() 8.0.30-MatrixOne-v4.2.6 current_user() root@localhost

Run sudo matrixone-mysql demo with no -e for an interactive session. If you use the mysql client directly, let it prompt for the password (-p with nothing after it). Never type -p<password>, which puts the password on the command line.

The image's own check proves that both per-VM passwords work. It also proves that root and dump refuse 111, a blank password, and common or random passwords.

sudo /usr/local/sbin/matrixone-roundtrip.sh | tail -1
OK-matrixone-roundtrip
7

Explore the demo database

First boot created a small table, demo.articles, with a vector column (embedding VECF32(3)) and a full-text index on body. List its rows.

sudo matrixone-mysql demo -e 'SELECT id, title FROM articles;'
id title 1 Getting started 2 Vector search 3 Full-text search

Vector search

Find the rows whose embedding is nearest to a query vector.

sudo matrixone-mysql demo -e "SELECT id, title, l2_distance(embedding, '[0.85,0.8,0.75]') AS distance FROM articles ORDER BY distance LIMIT 2;"
id title distance 2 Vector search 0.07071065157651901 3 Full-text search 0.6964194178581238

Full-text search

Find the rows whose body matches a keyword.

sudo matrixone-mysql demo -e "SELECT id, title FROM articles WHERE MATCH(body) AGAINST('columns');"
id title 2 Vector search

Your own database

Ordinary MySQL-style SQL works for your own data, and the same engine that wrote the rows aggregates them.

sudo matrixone-mysql -e "CREATE DATABASE IF NOT EXISTS shop; CREATE TABLE IF NOT EXISTS shop.orders (id INT PRIMARY KEY, customer VARCHAR(50), amount DECIMAL(10,2)); INSERT INTO shop.orders VALUES (1,'alice',19.99),(2,'bob',5.00),(3,'alice',42.50) ON DUPLICATE KEY UPDATE amount=VALUES(amount); SELECT customer, count(*) AS orders, sum(amount) AS total FROM shop.orders GROUP BY customer ORDER BY customer;"
customer orders total alice 2 62.49 bob 1 5.00

For applications, create a dedicated account rather than using root. For example, run CREATE USER app IDENTIFIED BY '<password>'; followed by the grants your application needs. MatrixOne grants privileges through roles: CREATE ROLE, GRANT ... TO ROLE and GRANT <role> TO <user>.

Type such statements in an interactive sudo matrixone-mysql session, so the password is never on a command line.

8

Connect from your own computer through an SSH tunnel

MatrixOne listens on 127.0.0.1:6001 only. Forward the port over SSH from your computer, then point any MySQL client or driver at your local end of the tunnel.

ssh -N -L 6001:127.0.0.1:6001 azureuser@<vm-public-ip> mysql -h 127.0.0.1 -P 6001 -u root -p

Enter the root password from Step 5 when prompted. MatrixOne does not offer TLS in this configuration, so the SSH tunnel is what encrypts the connection. A client set to --ssl-mode=REQUIRED will fail.

9

Open port 6001 to one network (optional)

Only do this for a network you trust, for example your application subnet inside the same virtual network. The MySQL protocol is not encrypted here.

Change the address that the MySQL-protocol port listens on, and restart MatrixOne. The internal ports stay on loopback. Then allow 6001 in your network security group from that one address range.

sudoedit /etc/matrixone/launch/cn.toml
# in the [cn.frontend] table, change host = "127.0.0.1" to host = "0.0.0.0"
sudo systemctl restart matrixone az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-matrixone-6001 --priority 1010 --direction Inbound --access Allow \
  --protocol Tcp --destination-port-ranges 6001 --source-address-prefixes <your-cidr>

Never use * or Internet as the source. To close the port again, set host = "127.0.0.1" back, restart MatrixOne and delete the rule. While the port is open, matrixone-roundtrip.sh reports that 6001 is reachable off the VM. This is expected.

10

Security checks

MatrixOne and its first-boot service cannot reach the Azure Instance Metadata Service (169.254.169.254) or the WireServer (168.63.129.16), in either address form.

grep -h IPAddressDeny /etc/systemd/system/matrixone.service.d/50-sac-imds-deny.conf /etc/systemd/system/matrixone-firstboot.service.d/50-sac-imds-deny.conf
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128

First boot tested the block from inside its own service before it created the catalogue. A loopback connection is the positive control, and the Azure Linux Agent is not affected.

sudo journalctl -u matrixone-firstboot.service -o cat | grep -o 'EGRESS-PROBE.*'
EGRESS-PROBE matrixone-firstboot.service loopback=REACHABLE imds4=TimeoutError imds4mapped=TimeoutError imds6=ENETUNREACH wireserver=TimeoutError wireservermapped=TimeoutError verdict=BLOCKED

The MySQL-protocol port is pinned to loopback, and the Iceberg connector and the Prometheus endpoint are off.

grep -E '^(host|port|enable|enable-metric-to-prom) =' /etc/matrixone/launch/cn.toml
host = "127.0.0.1" port = 6001 enable = false host = "127.0.0.1" enable-metric-to-prom = false

MatrixOne's feature gate refuses Iceberg statements.

{
sudo matrixone-mysql -e 'SHOW ICEBERG CATALOGS' 2>&1 || true; } | grep -o 'ICEBERG_FEATURE_DISABLED.*'
ICEBERG_FEATURE_DISABLED: SHOW ICEBERG CATALOGS is disabled by configuration [account_id=0]

MatrixOne runs unprivileged.

ps -o user:12=,comm= -C mo-service
sudo grep -E '^(CapEff|NoNewPrivs)' /proc/$(systemctl show -p MainPID --value matrixone.service)/status
matrixone mo-service CapEff: 0000000000000000 NoNewPrivs: 1

Check how MatrixOne was built and what govulncheck found.

grep -E '^(toolchain|govulncheck|source change|unfixed) ' /usr/share/doc/matrixone/BUILD-PROVENANCE.txt
toolchain go1.26.10 linux/amd64, official tarball https://go.dev/dl/go1.26.10.linux-amd64.tar.gz toolchain sha256 ccfe7ae822d2c2f41f4fe7c956199967a4dac22d0ee0d2b2bfdb440aab3b20c5 source change none (upstream source, unmodified) govulncheck v1.9.0, source: Your code is affected by 3 vulnerabilities from 1 module. (GO-2026-5046 GO-2026-5047 GO-2026-5048); binary: Your code is affected by 3 vulnerabilities from 1 module. (GO-2026-5046 GO-2026-5047 GO-2026-5048) unfixed GO-2026-5046, GO-2026-5047, GO-2026-5048 (github.com/hamba/avro/v2: no fixed release exists). Imported only

Security notes

  • GO-2026-5046, GO-2026-5047, GO-2026-5048 are denial-of-service advisories (CPU exhaustion, integer overflow, unbounded memory) in the Avro decoder github.com/hamba/avro/v2, for which no fixed release exists. MatrixOne uses that library only in its Iceberg connector, to read Iceberg manifest files. The connector is off on this image, and every Iceberg statement is refused before any Avro data is read, as shown above. This code is therefore not reachable. If you turn the connector on ([cn.frontend.iceberg] enable = true in /etc/matrixone/launch/cn.toml), that code becomes reachable and there is no upstream fix yet. Point it only at storage you trust.
  • MatrixOne cannot reach the Azure metadata service, so loading data from Azure Blob Storage or S3 cannot use the VM's managed identity. Pass credentials in the statement or stage you create, as Matrix Origin's documentation describes.

The data disk

The whole store (/var/lib/matrixone/mo-data) lives on the data disk, so it survives reboots and you can resize or snapshot it on its own. First boot writes a marker on the disk when it creates the catalogue, and matrixone.service checks for it before every start.

systemctl cat matrixone.service | grep -E '^(ExecStartPre|RequiresMountsFor)='
RequiresMountsFor=/var/lib/matrixone ExecStartPre=/usr/local/sbin/matrixone-precheck

If the data disk is missing, empty or replaced with one that this image's first boot did not create, MatrixOne does not start. Otherwise it would create a new catalogue with the upstream password 111. The journal says why.

matrixone-precheck: REFUSING TO START MatrixOne: /var/lib/matrixone holds no catalogue created by this image's first boot (an empty or unexpected data disk)

First boot itself refuses to touch a data disk that already holds data it did not create, so it never overwrites a disk you attached.

To move your data to another VM made from this image, follow these steps. Let the new VM finish its first boot, then stop MatrixOne (sudo systemctl stop matrixone). Replace the contents of /var/lib/matrixone with a copy of the old VM's, keeping the .sac-bootstrapped marker and matrixone:matrixone ownership. Alternatively, swap in a disk made from a snapshot of the old VM's data disk. Then start MatrixOne again.

The restored catalogue keeps the old VM's passwords. Put them in the new VM's credentials file (Step 11).

To grow the data disk, resize it in the portal (or with az disk update --size-gb). Then grow the file system once inside the VM: sudo resize2fs "$(findmnt -no SOURCE /var/lib/matrixone)".

11

Change the passwords

Type the new password into an interactive client session, so it is never on a command line. Then update the credentials file with an editor.

sudo MYSQL_HISTFILE=/dev/null matrixone-mysql mysql> ALTER USER root IDENTIFIED BY '<new-password>'; mysql> exit sudoedit /root/matrixone-credentials.txt
# replace the value of MATRIXONE_ROOT_PASSWORD with the new password

MYSQL_HISTFILE=/dev/null keeps the statement out of the client's history file. Use ALTER USER, because MatrixOne accepts SET PASSWORD but does not change the password with it.

Change dump the same way (ALTER USER dump IDENTIFIED BY '<new-password>';) and update MATRIXONE_DUMP_PASSWORD. Afterwards, run sudo /usr/local/sbin/matrixone-roundtrip.sh | tail -1. It should print OK-matrixone-roundtrip.

12

Backup, updates and maintenance

  • Back up by snapshotting the data disk, or by copying /var/lib/matrixone with the service stopped (sudo systemctl stop matrixone). MatrixOne also has its own backup tooling and mo-dump for logical dumps. See Matrix Origin's documentation.
  • Memory: MatrixOne sets its Go memory limit to about 70 percent of the VM's RAM each time it starts, so it adapts to the VM size you choose. The file service caches are capped at 128 MB (local) and 256 MB (shared) in /etc/matrixone/launch/*.toml. Raise them on large VMs for analytical work.
  • Logs: sudo journalctl -u matrixone.service -n 50.
  • Configuration lives in /etc/matrixone/launch/. Restart the service after editing it (sudo systemctl restart matrixone). Keep every listen-address and host on 127.0.0.1 unless you deliberately expose a port (Step 9). Never enable the Prometheus endpoint on a network you do not trust, because MatrixOne binds it to every interface.
  • Updates: Ubuntu security updates install automatically. MatrixOne is pinned to the version this image was built with. Upgrade it deliberately, after taking a snapshot of the data disk and reading Matrix Origin's upgrade notes for the release.

Support and licensing

MatrixOne is open-source software under the Apache License 2.0 (/usr/share/doc/matrixone/LICENSE). The jieba dictionary that MatrixOne uses for Chinese full-text tokenising is under the MIT licence (/usr/share/doc/matrixone/LICENSE.jieba-dict). The MatrixOne on this image is built from the unmodified upstream source.

SecureAnyCloud provides 24/7 technical support for this image: sales@secureanycloud.com.

MatrixOne is a trademark of Matrix Origin. MySQL is a trademark of Oracle Corporation. This image is provided by SecureAnyCloud and is not affiliated with or endorsed by Matrix Origin or Oracle Corporation.

Deploy on Azure

Launch MatrixOne 4.2.6 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure