C1
Databases Azure

CUBRID on Ubuntu 24.04 on Azure User Guide

| Product: CUBRID 11.4.6 on Ubuntu 24.04 LTS on Azure

Overview

CUBRID is an open source relational database management system built for high-concurrency transactional workloads. Its SQL is deliberately compatible with Oracle and MySQL, so a great deal of existing SQL, tooling and application code moves across with little or no rewriting, and it ships with native drivers for JDBC, ODBC, PHP, Python, Ruby, Perl and C.

The cloudimg image delivers CUBRID 11.4.6 (build 11.4.6.1963) on Ubuntu 24.04, with a unique DBA password generated on the first boot of your VM, and with the database deliberately closed to the network. Backed by 24/7 cloudimg support.

What is included:

  • CUBRID 11.4.6.1963 from the official vendor download channel, pinned and verified by both the vendor's published MD5 and a SHA-256 recorded when this image was built
  • A durable database area at /var/lib/cubrid/databases, owned by the unprivileged cubrid service account and kept outside the software tree
  • A per-VM DBA password written to /root/cubrid-credentials.txt, readable only by root
  • The database ports closed to the network by a packet filter, with SSH as the only way in
  • On-VM self-tests that prove the security posture at any time

A note on network exposure

This is the design decision most likely to surprise you, so it is worth stating plainly: the database is not reachable from outside your VM as shipped. That is deliberate, and it comes from two properties of CUBRID itself.

CUBRID offers no way to bind its listeners to a particular address — both the master process and the broker bind all interfaces, and there is no configuration parameter to change it. And the port its command line client uses, 1523, carries traffic in the clear. CUBRID does have a broker TLS option, but it does not complete a standard TLS handshake, the only certificate it ships is one whose private key is published in the upstream source repository, and it offers no minimum-protocol setting. So rather than publish a database port that cannot be properly secured, or claim an encryption that cannot be demonstrated, this image closes both database ports at the instance and gives you a better remote path: an SSH tunnel, which gives you modern, verified encryption that nothing in this image had to implement.

Step 8 shows the tunnel. Step 11 shows how to open the port directly if your deployment genuinely calls for it.

A note on the default credential

A stock CUBRID database is created with two accounts, DBA and PUBLIC, and both have an empty password. PUBLIC is the more dangerous of the two, because it is the account the client uses when you do not name one — so a stock database admits anyone who types nothing.

This image never creates them that way. No database exists in the image at all; on your VM's first boot a database is created with a password generated for that instance, set at the moment the account catalogue is created. Because the CUBRID tool that does this reports success even when it fails, first boot then proves on the live database that an empty password is refused, and refuses to finish if it is not.

Prerequisites

  • An Azure subscription
  • SSH access to the VM (port 22) — this is also how you reach the database
  • The CUBRID drivers on your application host if you plan to connect an application

Step 1: Deploy from the Azure Marketplace

Search the Azure Marketplace for CUBRID Database on Ubuntu 24.04 LTS by cloudimg, choose Standard_B2ms or larger, and allow inbound 22.

CUBRID's stock configuration asks for a 512 MB data buffer and a 256 MB log buffer, so Standard_B2ms (2 vCPU, 8 GiB) gives real headroom. Move up the memory-optimised sizes as your working set grows.

Step 2: Deploy from the Azure CLI

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

No extra ports are needed: you reach the database through the SSH connection you already have.

Step 3: Retrieve this instance's DBA password

Every VM launched from this image generates its own credentials on first boot and writes them to a root-only file. Nothing is shared between deployments.

sudo cat /root/cubrid-credentials.txt

You will see the DBA user, its password, the database name and this VM's public address.

CUBRID per-VM credentials file

The PUBLIC account was given the same per-instance password at creation, so that it is not an unauthenticated way in. If your application does not use it, give it its own password or revoke it.

Step 4: Confirm the database is running

systemctl is-active cubrid nftables
sudo -u cubrid env CUBRID=/opt/cubrid LD_LIBRARY_PATH=/opt/cubrid/lib /opt/cubrid/bin/cubrid_rel | head -2

Both units report active and the server reports CUBRID 11.4.6 (11.4.6.1963-0e7d3c1).

Check what is listening, and what is actually reachable:

sudo ss -Hltn | awk '{print $4}' | sort -u
sudo nft list table inet cloudimg_cubrid

CUBRID is listening on 1523 and 30000, but the packet filter drops both from anywhere except this VM itself. Only 22 is open to the network.

CUBRID service status and packet filter

Step 5: Run SQL on the VM

The image ships a small helper that runs SQL as DBA using this instance's own password, which it reads from the root-only credentials file — so your password never appears on a command line, where it would land in the shell history and the process list.

sudo /usr/local/sbin/cubrid-sql.sh "SELECT 'connected' AS status, CURRENT_USER AS whoami FROM db_root;"

To use CUBRID's own csql client directly, become the service account and set its environment. Substitute the password from Step 3:

sudo -u cubrid env CUBRID=/opt/cubrid CUBRID_DATABASES=/var/lib/cubrid/databases \
  LD_LIBRARY_PATH=/opt/cubrid/lib:/opt/cubrid/cci/lib PATH=/opt/cubrid/bin:/usr/bin:/bin \
  /opt/cubrid/bin/csql -C -u dba -p "<CUBRID_DBA_PASSWORD>" cubriddb

Note that csql has no password file and no password environment variable — -p is the only non-interactive way to supply one. It does scrub the value out of its own process listing, but it will still reach your shell history, which is why the cubrid-sql.sh helper above exists.

Step 6: Create a table and insert data

CUBRID uses standard SQL for schema definition:

sudo /usr/local/sbin/cubrid-sql.sh "
DROP TABLE IF EXISTS quarterly_sales;
CREATE TABLE quarterly_sales (
  id       INTEGER PRIMARY KEY,
  sold_at  DATE,
  region   VARCHAR(32),
  product  VARCHAR(64),
  qty      INTEGER,
  amount   DECIMAL(12,2)
);"
sudo /usr/local/sbin/cubrid-sql.sh "
INSERT INTO quarterly_sales VALUES (1, DATE'2026-01-15', 'EMEA', 'widget',   10, 199.50);
INSERT INTO quarterly_sales VALUES (2, DATE'2026-01-16', 'AMER', 'widget',    4,  79.80);
INSERT INTO quarterly_sales VALUES (3, DATE'2026-01-16', 'EMEA', 'gadget',    7, 349.00);
INSERT INTO quarterly_sales VALUES (4, DATE'2026-02-02', 'APAC', 'widget',   12, 239.40);
INSERT INTO quarterly_sales VALUES (5, DATE'2026-02-11', 'EMEA', 'gadget',    3, 149.55);
INSERT INTO quarterly_sales VALUES (6, DATE'2026-02-19', 'AMER', 'sprocket', 20, 880.00);"

Confirm the rows really landed:

sudo /usr/local/sbin/cubrid-sql.sh "SELECT COUNT(*) AS rows_loaded FROM quarterly_sales;"

Step 7: Run a query

Aggregation with a window function over it — the kind of standard SQL that moves across from Oracle or MySQL unchanged:

sudo /usr/local/sbin/cubrid-sql.sh "
SELECT region,
       SUM(amount)                             AS revenue,
       SUM(qty)                                AS units,
       RANK() OVER (ORDER BY SUM(amount) DESC) AS rnk
FROM   quarterly_sales
GROUP  BY region
ORDER  BY revenue DESC;"

AMER totals 959.80, EMEA 698.05 and APAC 239.40.

CUBRID SQL workload

Transactions behave as you would expect. Pass -n to run the statements in one transaction that is not auto-committed, so the ROLLBACK is meaningful — autocommit is a csql setting rather than a SQL statement, so SET AUTOCOMMIT OFF will not do it:

sudo /usr/local/sbin/cubrid-sql.sh -n "
INSERT INTO quarterly_sales VALUES (99, DATE'2026-03-01', 'EMEA', 'probe', 1, 1.00);
ROLLBACK;
SELECT 'ROLLBACK_DISCARDED' AS verdict FROM db_root
 WHERE NOT EXISTS (SELECT 1 FROM quarterly_sales WHERE id = 99);"

The row is gone, so the query returns ROLLBACK_DISCARDED.

Step 8: Connect an application over an SSH tunnel

This is the supported remote path. From your workstation, forward the broker port over SSH and point your application at localhost:

ssh -L 30000:127.0.0.1:30000 azureuser@<CUBRID_PUBLIC_IP>

With that tunnel open, a JDBC application connects to the database as if it were local:

jdbc:cubrid:127.0.0.1:30000:cubriddb:dba:<CUBRID_DBA_PASSWORD>:

and a Python application using the CUBRID driver does the same:

import CUBRIDdb
conn = CUBRIDdb.connect('CUBRID:127.0.0.1:30000:cubriddb:::', 'dba', '<CUBRID_DBA_PASSWORD>')
cur = conn.cursor()
cur.execute("SELECT region, SUM(amount) FROM quarterly_sales GROUP BY region")
print(cur.fetchall())

Everything on that connection is encrypted by SSH, and no database port is ever exposed to the network.

Step 9: Verify the security posture

The image ships self-tests you can re-run at any time. Each individual check is itself proven against a known-bad input first, so a check that always passes cannot go unnoticed.

sudo /usr/local/sbin/cubrid-selftest.sh

Individually:

sudo /usr/local/sbin/cubrid-port-check.sh
sudo /usr/local/sbin/cubrid-daemon-check.sh
sudo /usr/local/sbin/cubrid-verify-auth.sh /root/cubrid-credentials.txt

cubrid-verify-auth.sh confirms that DBA and PUBLIC with empty passwords are both refused, that weak and guessable values are refused, and that this VM's own password returns real rows.

CUBRID security posture self-tests

You can confirm directly that the empty-password default is refused:

sudo /usr/local/sbin/cubrid-sql.sh -u dba -p "" "SELECT 'should not appear' FROM db_root;" 2>&1 | grep -c "Incorrect or missing password" || true

That prints 1 — CUBRID's own refusal.

The CUBRID Manager administration service, which can create and delete databases and browse your data, is never started on this image. You can confirm nothing is listening on its port:

sudo ss -Hltn | awk '{print $4}' | grep -c ':8001$' || true

That prints 0.

Step 10: Back up and restore

cubrid backupdb writes a consistent backup of a running database:

sudo -u cubrid env CUBRID=/opt/cubrid CUBRID_DATABASES=/var/lib/cubrid/databases \
  LD_LIBRARY_PATH=/opt/cubrid/lib PATH=/opt/cubrid/bin:/usr/bin:/bin \
  /opt/cubrid/bin/cubrid backupdb -D /var/lib/cubrid/databases cubriddb && \
  sudo ls -lh /var/lib/cubrid/databases/cubriddb_bk* 2>/dev/null | head -3

To restore, stop the database, run cubrid restoredb against the backup, and start it again. Copy backups off the VM (for example to Azure Blob Storage) as part of your own retention policy.

Step 11: Open the database port deliberately

If your deployment genuinely needs the broker port reachable from another host, open it in two places — the instance's packet filter and the Azure network security group — and read the caveats first.

Before you do this, understand what you are exposing. CUBRID's broker traffic is not encrypted unless you enable its own TLS and install your own certificate; the certificate CUBRID ships has a private key that is published in the upstream source repository, so it protects nothing. Restrict the source address range, and prefer the SSH tunnel in Step 8 wherever you can.

# 1. On the VM: allow the broker port from your address range only.
sudo nft insert rule inet cloudimg_cubrid input ip saddr 203.0.113.0/24 tcp dport 30000 accept

# Make it survive a reboot by adding the same rule to:
#   /etc/nftables.d/cloudimg-cubrid.nft

# 2. In Azure: allow it inbound to the VM, again scoped to your range.
az vm open-port --resource-group my-resource-group --name my-cubrid \
  --port 30000 --priority 1001

# 3. Permit the client address in CUBRID's own broker ACL, which denies
#    every remote address by default:
#      /opt/cubrid/conf/cubrid_broker_acl
#    then reload:
sudo systemctl restart cubrid

Step 12: Rotate the DBA password

A password change must refuse a weak value, and CUBRID silently refuses anything over 31 bytes while still reporting success — so this block checks both before it changes anything.

sudo bash -c '
set -u
CREDS=/root/cubrid-credentials.txt
OLDPW=$(grep "^CUBRID_DBA_PASSWORD=" "$CREDS" | cut -d= -f2-)

reject() {
  case "$1" in
    ""|dba|DBA|admin|password|changeme|cubrid) return 0 ;;
  esac
  [ ${#1} -lt 16 ] && return 0
  [ ${#1} -gt 31 ] && return 0
  return 1
}

# 1. Weak values, and an over-length value, must all be REFUSED.
for candidate in "" "dba" "password" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; do
  if reject "$candidate"; then
    echo "REFUSED an unacceptable password as expected"
  else
    echo "PROBLEM: an unacceptable password was accepted"; exit 1
  fi
done

# 2. A real rotation with a strong value inside CUBRID s 31-byte limit.
NEWPW=$(openssl rand -base64 33 | tr -d "\n/+=" | cut -c1-28)
if reject "$NEWPW"; then echo "PROBLEM: generated password judged unacceptable"; exit 1; fi

/usr/local/sbin/cubrid-sql.sh "ALTER USER dba PASSWORD '"'"'$NEWPW'"'"';" >/dev/null

# 3. Record the new password so the VM stays consistent.
sed -i "s|^CUBRID_DBA_PASSWORD=.*|CUBRID_DBA_PASSWORD=$NEWPW|" "$CREDS"

# 4. Prove the new credential works.
/usr/local/sbin/cubrid-sql.sh "SELECT '"'"'rotated'"'"' AS status FROM db_root;"
echo "password rotation complete"
'

Step 13: Clean up the example table

sudo /usr/local/sbin/cubrid-sql.sh "DROP TABLE IF EXISTS quarterly_sales;"

Support

cloudimg provides 24/7 support for this image: deployment, upgrades, network exposure and TLS, schema design, data migration from Oracle and MySQL, backup strategy, query tuning and scaling.

CUBRID is a registered trademark of CUBRID Corporation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.