Ss
Databases Azure

SQL Server 2025 Developer on Ubuntu 24.04 on Azure User Guide

| Product: SQL Server 2025 Developer on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and use of the Ubuntu 24.04 VM with SQL Server 2025 Developer image by cloudimg on the Azure Marketplace. The image runs Microsoft SQL Server 2025 Developer Edition on Ubuntu 24.04 LTS, installed from Microsoft's official package repository at packages.microsoft.com, with the sqlcmd and bcp command line tools.

On the first boot of every virtual machine, a one time service gives the sa login a new random password, creates a cloudimg database and a cloudimg login, and writes the details to a file only root can read. No two virtual machines share an SA password.

Everything below was checked on a virtual machine deployed from the public Marketplace image (cloudimg1647283583153:mssql-2025-developer-ubuntu-24-04:default:latest, image version 1.0.20260426), with every command run as azureuser.

Licensing

Developer Edition is licensed by Microsoft for development and testing only. It is not licensed for production. There is no product key to enter: the image is ready to use for development, prototyping, load testing and QA. In SQL Server 2025, Developer Edition reports itself as Enterprise Developer Edition, the Enterprise feature set for non-production use.

To run production workloads you need a SQL Server 2025 Standard or Enterprise licence of your own. The edition is changed with sudo /opt/mssql/bin/mssql-conf set-edition. SQL Server is a trademark of Microsoft Corporation.

What's included

  • Microsoft SQL Server 2025 (mssql-server 17.0.4035.5, CU4, on the image version above), running as the mssql-server systemd service
  • mssql-tools18 (18.6.2.1): sqlcmd and bcp in /opt/mssql-tools18/bin, which is on every user's PATH
  • SQL Server listening on TCP port 1433 on all interfaces, with an encrypted connection by default (a self generated certificate)
  • mssql-firstboot.service, which sets the per VM SA password and creates the cloudimg database and login on the first boot only, then records that it has run in /var/opt/mssql/.firstboot-done
  • The credentials file /stage/scripts/mssql-credentials.log, mode 600, owned by root
  • Microsoft's mssql-server-2025 and prod apt repositories, so SQL Server 2025 cumulative updates arrive through apt
  • Ubuntu 24.04 LTS with the Azure Linux Agent, chrony for time sync and Ubuntu's unattended security upgrades

Platform: Microsoft Azure (Gen2, Hyper-V) Default user: azureuser (or the administrator name you choose) SQL Server port: 1433 Credentials file: /stage/scripts/mssql-credentials.log

Prerequisites

Before deploying this image, ensure you have:

  1. An active Microsoft Azure subscription
  2. Access to the Azure Portal, or the Azure CLI on your workstation
  3. An SSH key pair for Linux virtual machine access
  4. A virtual network plan: which application subnets need to reach SQL Server on port 1433

Choose a virtual machine size that suits your workload. SQL Server sizes its memory use to the virtual machine, so give it enough memory for your working set.

Step 1: Deploy the Virtual Machine

Option A: Azure Portal

  1. Open the Ubuntu 24.04 VM with SQL Server 2025 Developer listing on the Azure Marketplace and choose Get it now, then Create
  2. Configure the basics:
  3. Subscription: Select your Azure subscription
  4. Resource Group: Create new or select existing
  5. Virtual Machine Name: Enter a name for your VM
  6. Region: Select your preferred Azure region
  7. Size: Select a size for your workload
  8. Under Administrator Account, select SSH public key and enter your key
  9. Under Inbound Port Rules, allow SSH (port 22), ideally from your own network only. Do not open port 1433 to the internet
  10. On the Networking tab, choose the virtual network your application servers use
  11. Click Review + Create, then Create

Option B: Azure CLI

The image carries a Marketplace purchase plan, so accept its terms once per subscription. Then create a network security group that allows SSH from your own network only, and create the virtual machine. Replace <your-ip> with your workstation's public address and <your-vm-size> with the size you chose:

az vm image terms accept --urn cloudimg1647283583153:mssql-2025-developer-ubuntu-24-04:default:latest

az network nsg create --resource-group myResourceGroup --name my-ssh-only-nsg
az network nsg rule create --resource-group myResourceGroup --nsg-name my-ssh-only-nsg \
  --name allow-ssh --priority 100 --protocol Tcp --destination-port-ranges 22 \
  --source-address-prefixes <your-ip>/32 --access Allow

az vm create \
  --resource-group myResourceGroup \
  --name my-mssql-vm \
  --image cloudimg1647283583153:mssql-2025-developer-ubuntu-24-04:default:latest \
  --size <your-vm-size> \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --nsg my-ssh-only-nsg \
  --public-ip-sku Standard \
  --storage-sku StandardSSD_LRS \
  --query publicIpAddress -o tsv

The command prints the virtual machine's public IP address. To keep the database server off the internet entirely, add --public-ip-address "" and reach it through Azure Bastion, a VPN or a jump host on the same virtual network instead. Step 9 shows how to let your application servers reach port 1433.

Step 2: Connect via SSH

Run this on your own workstation:

ssh azureuser@<vm-ip>

There is no password to enter. If SSH asks for one, the key your client is offering is not the key you supplied at create time.

The first boot finishes within a minute or two of the virtual machine starting. To wait for it:

sudo cloud-init status --wait

It prints status: done when the first boot has finished.

Step 3: Check the Server

Both services are active once the first boot has finished:

systemctl is-active mssql-server mssql-firstboot

Expected output:

active
active

The first boot service leaves a marker file when it has run:

sudo test -f /var/opt/mssql/.firstboot-done && echo FIRSTBOOT_DONE

SQL Server listens on port 1433 on all interfaces:

sudo ss -Htlnp '( sport = :1433 )' | awk '{print $4, $6}'

Expected output:

0.0.0.0:1433 users:(("sqlservr",pid=...,fd=...))
*:1433 users:(("sqlservr",pid=...,fd=...))

SQL Server also listens on the loopback address only, on ports 1431 and 1434, for its own local use. The network security group decides who can reach port 1433 from outside: the deployment above allows only SSH.

mssql-server.service active (running) with TCP 1433 bound on all interfaces

Step 4: The Credentials File

The first boot writes the logins to a file only root can read:

sudo stat -c '%a %U:%G %n' /stage/scripts/mssql-credentials.log

Expected output:

600 root:root /stage/scripts/mssql-credentials.log

To see the file, run sudo cat /stage/scripts/mssql-credentials.log. It holds SA_USER=sa, the SA password, CLOUDIMG_USER=cloudimg, the cloudimg password and CLOUDIMG_DATABASE=cloudimg. The comment lines at the top of the file mention a Standard licence key: they do not apply to this Developer Edition image (see Licensing above). Store the values in your secret store.

Step 5: Connect with sqlcmd

sqlcmd is already on your PATH. Load the SA password into the SQLCMDPASSWORD environment variable, which sqlcmd reads, so the password never appears on a command line or in the process list:

export SQLCMDPASSWORD="$(sudo awk -F= '$1=="SA_PASSWORD"{print $2}' /stage/scripts/mssql-credentials.log)"
sqlcmd -S localhost -U sa -C -Q "SELECT @@VERSION;"

Expected output (first lines):

Microsoft SQL Server 2025 (RTM-CU4) (KB5081495) - 17.0.4035.5 (X64)
    Mar 29 2026 15:22:28
    Copyright (C) 2025 Microsoft Corporation
    Enterprise Developer Edition (64-bit) on Linux (Ubuntu 24.04.4 LTS) <X64>

-C tells sqlcmd to trust the server's self generated certificate. sqlcmd 18 encrypts every connection by default, so without -C (or a certificate your clients trust) it stops with certificate verify failed:self-signed certificate.

Confirm the edition:

sqlcmd -S localhost -U sa -C -h -1 -W -Q "SELECT SERVERPROPERTY('Edition');"

Expected output:

Enterprise Developer Edition (64-bit)

Check that the connection is encrypted:

sqlcmd -S localhost -U sa -C -h -1 -W -Q "SELECT net_transport, encrypt_option, auth_scheme FROM sys.dm_exec_connections WHERE session_id = @@SPID;"

Expected output:

TCP TRUE SQL

Step 6: The cloudimg Database and Login

The first boot creates a cloudimg database (full recovery model, compatibility level 170) and a cloudimg login that is db_owner of it:

sqlcmd -S localhost -U sa -C -h -1 -W -Q "SELECT name, recovery_model_desc, compatibility_level FROM sys.databases WHERE name = 'cloudimg';"
sqlcmd -S localhost -U sa -C -d cloudimg -h -1 -W -Q "SELECT m.name, r.name FROM sys.database_role_members rm JOIN sys.database_principals r ON r.principal_id = rm.role_principal_id JOIN sys.database_principals m ON m.principal_id = rm.member_principal_id;"

Expected output:

cloudimg FULL 170

dbo db_owner
cloudimg db_owner

Before you first use the cloudimg login, set its password to the per VM value in the credentials file, as SA. On the current image the login does not yet accept that value, so this step is required. The password is passed to sqlcmd on standard input, never on a command line:

CL_PASS="$(sudo awk -F= '$1=="CLOUDIMG_PASSWORD"{print $2}' /stage/scripts/mssql-credentials.log)"
printf "ALTER LOGIN [cloudimg] WITH PASSWORD = N'%s';\nGO\n" "$CL_PASS" | sqlcmd -S localhost -U sa -C -b -i /dev/stdin
unset CL_PASS

Then run a round trip as the cloudimg login:

SQLCMDPASSWORD="$(sudo awk -F= '$1=="CLOUDIMG_PASSWORD"{print $2}' /stage/scripts/mssql-credentials.log)" \
  sqlcmd -S localhost -U cloudimg -C -d cloudimg -h -1 -W \
  -Q "CREATE TABLE ##probe (id INT, val NVARCHAR(64)); INSERT INTO ##probe VALUES (1, N'cloudimg-ok'); SELECT val FROM ##probe WHERE id = 1; DROP TABLE IF EXISTS ##probe;"

The output includes cloudimg-ok.

Step 7: Try the SQL Server 2025 Features

SQL Server 2025 adds a native VECTOR type with VECTOR_DISTANCE, regular expression functions such as REGEXP_LIKE, and JSON constructors such as JSON_OBJECT. A quick test in the cloudimg database:

sqlcmd -S localhost -U sa -C -d cloudimg -h -1 -W -Q "
CREATE TABLE dbo.docs (id INT PRIMARY KEY, title NVARCHAR(100), embedding VECTOR(3));
INSERT INTO dbo.docs VALUES (1, N'Azure VM guide', '[0.1,0.9,0.2]'), (2, N'SQL Server notes', '[0.8,0.1,0.3]'), (3, N'Ubuntu tips', '[0.2,0.8,0.1]');
SELECT TOP 2 id, title, CAST(VECTOR_DISTANCE('cosine', embedding, CAST('[0.1,0.9,0.1]' AS VECTOR(3))) AS DECIMAL(6,4)) AS dist FROM dbo.docs ORDER BY dist;
SELECT id, title FROM dbo.docs WHERE REGEXP_LIKE(title, '^(Azure|Ubuntu)');
SELECT JSON_OBJECT('id': id, 'title': title) FROM dbo.docs WHERE id = 2;
DROP TABLE dbo.docs;"

Expected output:

1 Azure VM guide .0058
3 Ubuntu tips .0089

1 Azure VM guide
3 Ubuntu tips

{"id":2,"title":"SQL Server notes"}

Step 8: Back Up a Database

SQL Server writes backups as the mssql user, so use a directory it owns, such as the default data directory:

sqlcmd -S localhost -U sa -C -Q "BACKUP DATABASE cloudimg TO DISK = N'/var/opt/mssql/data/cloudimg.bak' WITH INIT, COMPRESSION; RESTORE VERIFYONLY FROM DISK = N'/var/opt/mssql/data/cloudimg.bak';"

Expected output (last lines):

BACKUP DATABASE successfully processed ... pages in ... seconds (...).
The backup set on file 1 is valid.

Copy backups off the virtual machine, for example to Azure Blob Storage, so they survive the loss of the disk.

Step 9: Reach SQL Server from Your Applications

SQL Server listens on port 1433 on every interface, but the network security group from Step 1 allows only SSH. To let your application servers connect, add a rule for port 1433 from their subnet only:

az network nsg rule create --resource-group myResourceGroup --nsg-name my-ssh-only-nsg \
  --name allow-sql-from-apps --priority 110 --protocol Tcp --destination-port-ranges 1433 \
  --source-address-prefixes <app-subnet-cidr> --access Allow

Applications then connect to the virtual machine's private IP address on port 1433 with a SQL login, for example sqlcmd -S <private-ip>,1433 -U cloudimg -C -d cloudimg (with the password in SQLCMDPASSWORD). SQL Server Management Studio and other TDS clients connect the same way; they need to trust the server certificate, as -C does, or you can install a certificate of your own with mssql-conf set network.tlscert.

To work from your own workstation without opening port 1433, use an SSH tunnel and point your client at localhost,14330:

ssh -N -L 14330:localhost:1433 azureuser@<vm-ip>

Do not open port 1433 to the internet.

Step 10: Server Components

Component Path
SQL Server engine /opt/mssql/bin/sqlservr
Configuration tool /opt/mssql/bin/mssql-conf
Configuration file /var/opt/mssql/mssql.conf
sqlcmd and bcp /opt/mssql-tools18/bin/
Data directory /var/opt/mssql/data/
Error log /var/opt/mssql/log/errorlog
Service unit /usr/lib/systemd/system/mssql-server.service
First boot unit /etc/systemd/system/mssql-firstboot.service
First boot script /usr/local/sbin/mssql-firstboot.sh
First boot marker /var/opt/mssql/.firstboot-done
Credentials file /stage/scripts/mssql-credentials.log

The data and log files live on the OS disk. For larger databases, attach a data disk, mount it, and move the default data and log directories to it with mssql-conf set filelocation.defaultdatadir and filelocation.defaultlogdir.

SQL Server Agent is not enabled on this image. Turn it on with sudo /opt/mssql/bin/mssql-conf set sqlagent.enabled true followed by sudo systemctl restart mssql-server.

Check the installed package versions:

dpkg -l mssql-server mssql-tools18 | awk '/^ii/{print $2, $3}'

Step 11: Manage the Service

sudo systemctl status mssql-server --no-pager
sudo systemctl restart mssql-server
sudo tail -n 50 /var/opt/mssql/log/errorlog

Step 12: Keep It Updated

Ubuntu's unattended upgrades install Ubuntu security updates automatically. SQL Server comes from Microsoft's own repository, so update it yourself when a new cumulative update is out:

sudo apt-get update
apt-cache policy mssql-server
sudo apt-get install --only-upgrade mssql-server

The upgrade restarts SQL Server, so plan it for a quiet moment and take a backup first.

Step 13: Troubleshooting

sqlcmd says certificate verify failed:self-signed certificate

  • Add -C to trust the server's certificate, or install a certificate your clients trust (Step 9)

Login failed for user 'cloudimg'

  • Set the cloudimg password from the credentials file as SA (Step 6), then try again
  • The error log names the reason: sudo grep "Login failed" /var/opt/mssql/log/errorlog | tail -5

Cannot connect on port 1433 from another host

  • Check the service and listener (Step 3)
  • Check the network security group allows 1433 from your client's subnet (Step 9)

The first boot did not finish

  • sudo systemctl status mssql-firstboot --no-pager
  • sudo journalctl -u mssql-firstboot --no-pager -n 50

The server name

  • SELECT @@SERVERNAME reports the name of the machine the image was built on, while SERVERPROPERTY('MachineName') reports your virtual machine's name. Features that depend on the server name, such as replication, need it renamed first, following Microsoft's procedure for renaming a SQL Server host

Step 14: Security Recommendations

  • Keep port 1433 closed to the internet; allow it only from your application subnets
  • Set the cloudimg login's password (Step 6) before port 1433 is reachable from any other host
  • Create a login for each application with only the permissions it needs; do not connect applications as SA
  • Change the SA password on a schedule with ALTER LOGIN [sa] WITH PASSWORD = ..., or disable SA once named administrator logins exist
  • Use SQLCMDPASSWORD or a secret store rather than -P on the command line
  • Take regular backups and copy them off the virtual machine
  • When the passwords are in your secret store, you can delete the credentials file with sudo shred -u /stage/scripts/mssql-credentials.log

Support

cloudimg provides 24/7 support for this image, with a guaranteed 24 hour response.

  • Email: support@cloudimg.co.uk
  • Website: cloudimg.co.uk