Microsoft SQL Server Express on Linux: native and in Docker

Linux 20 min read

Microsoft SQL Server has been running on Linux since 2017, and the free Express edition can also be used in a production environment on Linux. If an application requires an MSSQL database, Windows Server is not strictly necessary: a Linux-based KVM VPS on which SQL Server runs as a system service or in a Docker container is sufficient.

This article outlines both approaches and compares Linux Express with the Windows version: what is the same, what is missing on Linux, and when Windows is actually the better option. You can read about SQL Server editions and operation in general in our MSSQL guide.

We tested these steps in November 2026 on an x86_64 virtual machine running AlmaLinux 10.2, using SQL Server 2025 RTM-CU10 (17.0.5035.5), both natively and under Docker CE 29.9. Run the commands using sudo or as the root user.

Before you begin

  • It runs only on x86_64 (Intel, AMD) processors. There are no packages or container images available for ARM processors (such as Ampere or Apple Silicon), and Microsoft does not support emulation (Rosetta, QEMU) either.
  • At least 2 GB of memory is required. It won’t even start with less than that. During testing, with 1 GB of memory, the service shut down immediately with this message:

    sqlservr: This program requires a machine with at least 2000 megabytes of memory.
    

    It started with 2 GB, but memory is tight for both the system and SQL Server. At least 4 GB of memory is recommended for the Express edition.

  • Supported operating systems. SQL Server 2025 is officially supported on Red Hat Enterprise Linux 9 and 10, as well as Ubuntu 22.04 and 24.04, on XFS or ext4 file systems. SUSE is no longer supported by version 2025. AlmaLinux and Rocky Linux are binary-compatible with RHEL; RHEL packages install and run on them without any issues, but Microsoft does not officially list them. If official support is important to you, choose Ubuntu 24.04, or run SQL Server in Docker, which is supported on any x86_64 Linux distribution.
  • Which version for which system? Only SQL Server 2025 can be installed on AlmaLinux 10 (there is no RHEL 10 repository for the 2022 version), whilst both the 2022 and 2025 versions can be installed on AlmaLinux 9.

Native or Docker?

  Native installation Docker container
Installation Microsoft repository, dnf install A docker run command
Update dnf update mssql-server Download a new image, recreate the container
Start, stop systemd service (mssql-server) Docker, with a restart rule
SELinux Custom policy package (mssql-server-selinux) Handled by Docker
Firewall The firewalld rules are in effect The published port bypasses firewalld (see below)
Disk space Approx. 2.5 GB (/opt/mssql) The image is 2.4 GB on the hard drive
What is it for? A database server for the long term Multiple versions running side by side, development, rapid replacement

If only SQL Server is running on the server, the native installation is simpler: it starts as a system service, is updated by dnf, and the firewall works as expected. Docker is preferable if applications are already running in containers on the machine, or if you want to run multiple versions of SQL Server side by side.

A) Native installation on AlmaLinux 10

1. The repository and the package

curl -fsSL https://packages.microsoft.com/config/rhel/10/mssql-server-2025.repo | sudo tee /etc/yum.repos.d/mssql-server-2025.repo
sudo dnf install -y mssql-server

For AlmaLinux 9, the address should read rhel/9 instead of rhel/10. For SQL Server 2022, you will need the mssql-server-2022.repo file; this can only be found under the rhel/9 path.

dnf will prompt you for the Microsoft package signing key during the initial installation; please accept it. The package is installed in the /opt/mssql directory, and the databases are placed under /var/opt/mssql.

2. Edition and sa password

sudo /opt/mssql/bin/mssql-conf setup

The program first asks for the edition:

Choose an edition of SQL Server:
  1) Evaluation (free, no production use rights, 180-day limit)
  2) Enterprise Developer (free, no production use rights)
  3) Standard Developer (free, no production use rights)
  4) Express (free)
  5) Standard (PAID)
  ...
Enter your edition(1-10):

For Express, enter 4, accept the licence terms, then enter the administrator (sa) password twice. The password must be at least 8 characters long and contain at least three different types of characters from the following: lower-case letters, upper-case letters, numbers and punctuation marks. If it does not meet these requirements, the setup will fail.

It can also be set up from a script, without prompts:

sudo MSSQL_PID=Express ACCEPT_EULA=Y MSSQL_SA_PASSWORD='Hosszu-Egyedi-Jelszo-1' /opt/mssql/bin/mssql-conf -n setup

The password will be recorded in the command history; once you have completed the setup, please delete it from there or use the interactive version.

Once the configuration is complete, the service will start, and it will also start at system boot:

systemctl status mssql-server --no-pager

3. Command-line tools

sqlcmd and bcp are available as separate packages from Microsoft’s general repository:

curl -fsSL https://packages.microsoft.com/config/rhel/10/prod.repo | sudo tee /etc/yum.repos.d/mssql-release.repo
sudo ACCEPT_EULA=Y dnf install -y mssql-tools18 unixODBC-devel
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc
source ~/.bashrc

Test the connection:

sqlcmd -S localhost -U sa -C -Q "SELECT @@VERSION"

The -C switch is mandatory: mssql-tools18 requires an encrypted connection, whilst SQL Server uses a self-signed certificate after installation. Without it, the following error occurs:

SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate]

The response in our test:

Microsoft SQL Server 2025 (RTM-CU10) (KB5125471) - 17.0.5035.5 (X64)
    Express Edition (64-bit) on Linux (AlmaLinux 10.2 (Lavender Lion)) <X64>

sqlcmd prompts for the password (Password:) or retrieves it from the SQLCMDPASSWORD environment variable. The -P jelszo switch also works, but the password will then be visible in the process list.

4. SELinux

On AlmaLinux, SQL Server runs by default as an unrestricted (unconfined_service_t) process. Microsoft adds a separate SELinux policy which restricts it to its own, narrower domain (mssql_server_t):

sudo dnf install -y mssql-server-selinux
sudo systemctl restart mssql-server
ps -eZ | grep sqlservr

During testing, everything ran without errors with the policy in place, including the backup shown below. However, the policy does not permit writing outside the /var/opt/mssql directory: a backup attempt to an /srv/mssql-backup directory failed with the error “Operating system error 5 (Access is denied.)”, even though the directory owner was the mssql user. Assign the SQL Server SELinux label to such a directory:

sudo semanage fcontext -a -t mssql_var_t "/srv/mssql-backup(/.*)?"
sudo restorecon -Rv /srv/mssql-backup

5. Memory limit

On Linux, SQL Server by default treats 80 per cent of the physical memory as its own. If other applications are running on the server (such as a web application), set a lower limit:

sudo /opt/mssql/bin/mssql-conf set memory.memorylimitmb 3072
sudo systemctl restart mssql-server

The Express edition uses a maximum of 1410 MB for the data buffer pool anyway, but the process as a whole takes up more than this, so do not set the limit below 2 GB. If memory runs out, the kernel will terminate the largest process, which is usually SQL Server (see: Out of memory: OOM killer and swap file).

mssql-conf settings are stored in the /var/opt/mssql/mssql.conf file and take effect after a restart.

From version 2025 onwards, the Express edition also includes the features previously available in ‘Express with Advanced Services’, including full-text search. On Linux, this is a separate package:

sudo dnf install -y mssql-server-fts
sudo systemctl restart mssql-server

In the test, the CONTAINS search also worked in the Express edition.

B) SQL Server Express in a Docker container

This requires Docker to be installed on the server. On AlmaLinux, Docker CE is installed from Docker’s own repository (https://download.docker.com/linux/rhel/docker-ce.repo, packages: docker-ce, docker-ce-cli, containerd.io).

Getting started

docker volume create mssql-data

docker run -d --name sql1 --hostname sql1 \
  -e ACCEPT_EULA=Y \
  -e MSSQL_PID=Express \
  -e MSSQL_SA_PASSWORD='Hosszu-Egyedi-Jelszo-1' \
  -p 127.0.0.1:1433:1433 \
  -v mssql-data:/var/opt/mssql \
  --restart unless-stopped \
  mcr.microsoft.com/mssql/server:2025-latest
  • MSSQL_PID=Express: without this, the container will start with the Developer edition, which is not licensed for production use.
  • -v mssql-data:/var/opt/mssql: the databases are stored in a Docker volume, so they are retained even after the container is deleted and recreated. Without a volume, the data will be lost along with the container.
  • -p 127.0.0.1:1433:1433: the port is only accessible from within the server. The reason for this is explained in the following section.
  • --restart unless-stopped: the container will start even after the server has been restarted.

The container started up in under 10 seconds during the test. You can check the edition in the log:

docker logs sql1 2>&1 | grep -A1 "Microsoft SQL Server 2025"
Microsoft SQL Server 2025 (RTM-CU10) (KB5125471) - 17.0.5035.5 (X64)
    Express Edition (64-bit) on Linux (Ubuntu 24.04.5 LTS) <X64>

The image is Ubuntu-based and includes sqlcmd:

docker exec -it sql1 /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -C

Docker bypasses the firewall

Docker opens the published ports of containers using its own rules, and these take precedence over firewalld’s rules. In the test, the container started with the -p 1433:1433 switch was accessible from outside, even though there was no rule for port 1433 in firewalld. You should therefore always bind the port to the local address (-p 127.0.0.1:1433:1433) and use an SSH tunnel for remote access (see below). If another server needs to access the database directly, opt for the native installation instead, where the firewalld rule applies.

A folder instead of a volume

If you mount a folder instead of a Docker volume (-v /srv/mssql:/var/opt/mssql), the folder must be owned by user 10001, as SQL Server runs within the container under this user ID (mssql), not as root. If the folder is owned by root, the container will crash immediately:

/opt/mssql/bin/sqlservr: Error: The system directory [/.system] could not be created. ... Permission denied

The solution:

sudo mkdir -p /srv/mssql
sudo chown 10001:10001 /srv/mssql

Memory limit in the container

You can limit the container’s memory using the Docker --memory switch or the SQL Server MSSQL_MEMORY_LIMIT_MB variable. In the test, with --memory 2g, SQL Server detected 1639 MB (80 per cent of 2 GB), whilst with -e MSSQL_MEMORY_LIMIT_MB=2048, it detected exactly 2048 MB.

Shutdown: the docker stop command does not shut down properly

In the container, process 1 is a startup script (launch_sqlservr.sh) which does not pass the shutdown signal on to SQL Server. The docker stop command therefore waits for the timeout period (10 seconds by default) and then terminates the process (exit code: 137). In the test, this did not cause any data corruption: upon restart, SQL Server restored the databases from the transaction log, and all committed transactions were preserved. However, a proper shutdown results in a faster restart and eliminates the need for recovery.

If you wish to perform a proper shutdown, start the container directly with SQL Server using the --entrypoint /opt/mssql/bin/sqlservr switch (in the docker run command above, before the image name). In the test, docker stop shut down the server immediately and correctly. In this case, the image’s startup script does not run, nor does the folder permission check: if a folder has the wrong owner, you will only receive SQL Server’s own error message.

Update

For containers, updating involves replacing the image: download the new image, delete the container, and restart it with the same volume.

docker pull mcr.microsoft.com/mssql/server:2025-latest
docker stop sql1
docker rm sql1
# majd a fenti docker run parancs, ugyanazzal a kötettel

In the test, a SQL Server 2022 container volume was migrated to the 2025 version without any issues, and the databases remained accessible. This does not work in the opposite direction: a database opened with the 2025 version cannot be opened with the 2022 version. Always perform a backup before updating.

Remote access

The native installation listens on port 1433 on every network address. Never open the database port to the wider internet: SQL Server is constantly under attack from password-guessing attempts.

SSH tunnel (recommended): set up a tunnel from your own computer to the server, and connect to localhost,1433 using SQL Server Management Studio (SSMS) or another client:

ssh -L 1433:127.0.0.1:1433 felhasznalo@szerver-ip

No ports other than SSH need to be opened on the server for this. You can read more about SSH in our article on Linux Secure Shell (SSH).

Allowing access from a specific IP address: if another server accesses the database (in the case of a native installation), only allow that address in firewalld:

sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="1433" protocol="tcp" accept'
sudo firewall-cmd --reload

General information on firewall configuration can be found in the article Configuring the firewall on a VPS. If the connection is not over a local network, enable encryption on the client and replace the self-signed certificate with a valid one, or use an SSH tunnel.

Backup and restore

Scheduled backups using the systemd timer

According to Microsoft, the Express edition does not include SQL Server Agent, so scheduled backups are handled by the Linux scheduler. During testing on Linux, the Agent did start up on the Express edition (using the command mssql-conf set sqlagent.enabled true) , and a backup job ran successfully with it; however, as Microsoft’s edition comparison states that the Express edition does not include it, you should not rely on it in a production environment.

The script below creates a full backup of all user databases and deletes those older than 7 days. It is designed for native installation; when using Docker, the sqlcmd command must be run within the container (docker exec sql1 /opt/mssql-tools18/bin/sqlcmd ...), and the backups are stored on the volume.

The password is stored in a file that can only be read by the root user:

echo 'SQLCMDPASSWORD=Hosszu-Egyedi-Jelszo-1' | sudo tee /etc/mssql-backup.env
sudo chmod 600 /etc/mssql-backup.env
sudo mkdir -p /var/opt/mssql/backup
sudo chown mssql:mssql /var/opt/mssql/backup

The script: /usr/local/bin/mssql-backup

#!/bin/bash
# Minden felhasználói adatbázis teljes mentése
set -euo pipefail
SQLCMD="/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -C -b -h -1 -W"
NAP=$(date +%F)
for DB in $($SQLCMD -Q "SET NOCOUNT ON; SELECT name FROM sys.databases WHERE database_id > 4 AND state_desc = 'ONLINE'"); do
  $SQLCMD -Q "BACKUP DATABASE [$DB] TO DISK = N'/var/opt/mssql/backup/${DB}_${NAP}.bak' WITH INIT, CHECKSUM"
done
find /var/opt/mssql/backup -name "*.bak" -mtime +7 -delete

The script does not work with a database name containing spaces. Make it executable: sudo chmod 755 /usr/local/bin/mssql-backup.

The service: /etc/systemd/system/mssql-backup.service

[Unit]
Description=SQL Server adatbázisok mentése
After=mssql-server.service

[Service]
Type=oneshot
EnvironmentFile=/etc/mssql-backup.env
ExecStart=/usr/local/bin/mssql-backup

The timer: /etc/systemd/system/mssql-backup.timer

[Unit]
Description=SQL Server mentés minden éjjel 2-kor

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enabling it, and a test backup:

sudo systemctl daemon-reload
sudo systemctl enable --now mssql-backup.timer
sudo systemctl start mssql-backup.service
journalctl -u mssql-backup.service -n 5

Because Persistent=true, any missed backups (for example, because the server was not running at the time) will run the next time the system starts up. If you would prefer to use cron, the script can also be run from there (see: Crontab syntax and scheduled tasks on Linux).

You should regularly copy your backups to another machine, for example using rsync (see: Copying files between servers): a backup that is lost along with the server offers no protection whatsoever.

The Express edition cannot create compressed backups. When using the WITH COMPRESSION switch, you will receive the following error:

Msg 1844, Level 16, State 1
BACKUP DATABASE WITH COMPRESSION is not supported on Express Edition (64-bit).

Compress the resulting .bak files afterwards, for example using zstd or gzip.

Restoring a Windows backup on Linux

The format of backup files is the same on Windows and Linux, so a .bak file created from a Windows SQL Server can also be restored on Linux, provided the Linux version is the same or newer. However, the backup contains Windows paths (C:\Program Files\...), so the files must be moved using the WITH MOVE switch. First, list the logical names of the files in the backup:

RESTORE FILELISTONLY FROM DISK = N'/var/opt/mssql/backup/AdventureWorksLT2022.bak';

Then restore it using the logical names:

RESTORE DATABASE AdventureWorksLT
FROM DISK = N'/var/opt/mssql/backup/AdventureWorksLT2022.bak'
WITH MOVE 'AdventureWorksLT2022_Data' TO '/var/opt/mssql/data/AdventureWorksLT.mdf',
     MOVE 'AdventureWorksLT2022_Log'  TO '/var/opt/mssql/data/AdventureWorksLT_log.ldf';

In the test, the AdventureWorksLT2022 sample database created on Microsoft Windows was restored without any errors. Without the WITH MOVE clause, the following error occurs:

Directory lookup for the file "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\AdventureWorksLT2022.mdf" failed with the operating system error 2(The system cannot find the file specified.).

The backup file must be readable by the mssql user. For a native installation, copy it to the /var/opt/mssql/backup folder and set the ownership to the mssql user (sudo chown mssql:mssql fajl.bak). With Docker, the docker cp command copies the file into the container with root ownership, in which case the restore process will fail with the error “Operating system error 5 (Access is denied.)”. After copying, transfer the file:

docker cp AdventureWorksLT2022.bak sql1:/var/opt/mssql/backup/
docker exec -u 0 sql1 chown mssql:mssql /var/opt/mssql/backup/AdventureWorksLT2022.bak

The same applies in reverse: a backup created on Linux can be restored on Windows, to the same or a newer version.

Limitations of Express on Linux and Windows

The limitations of the Express edition are independent of the operating system; they are the same on Linux as they are on Windows (SQL Server 2025):

Limit Express
Processor 1 socket or 4 cores, whichever is fewer
Buffer pool 1410 MB
Columnstore and memory-optimised data 352 MB per database
Database size 50 GB per database (10 GB for versions 2022 and earlier)
SQL Server Agent None
Compressed backup None
Database Mail None

The 50 GB limit applies to data files; the transaction log is not included in this limit. During the test, the creation of a 51,201 MB database was halted due to this error:

Msg 1827, Level 16, State 4
CREATE DATABASE or ALTER DATABASE failed because the resulting cumulative database size would exceed your licensed limit of 51200 MB per database.

In practice, memory is usually the bottleneck: if frequently used data does not fit into the 1410 MB cache, Express reads from disk and slows down, regardless of the server’s size. For this reason, it is not worth renting a server with more than 8 GB of memory for Express if only the database is running on it.

Features not available on Linux

The SQL Server engine on Linux is the same as on Windows: T-SQL, stored procedures, triggers, views, full-text search and the backup format are all identical. Most .NET, PHP, Python or Node.js applications do not even notice that the database is running on Linux. However, some features, mainly those specific to Windows, are missing (based on Microsoft’s list, SQL Server 2025):

  • FILESTREAM and FileTable: storing files alongside the database, in a Windows file system.
  • CLR assemblies with EXTERNAL_ACCESS or UNSAFE permissions: SAFE assemblies run, whilst .NET code that accesses the file system or the network does not.
  • System-level extended stored procedures, such as xp_cmdshell.
  • Linked servers to data sources other than SQL Server, such as Access, Excel or other ODBC sources. Linked servers to SQL Server work.
  • Merge replication.
  • Analysis Services and Reporting Services: these do not run on Linux (instead of Reporting Services, Microsoft recommends Power BI Report Server, which only runs on Windows).
  • Windows authentication using local Windows accounts: On Linux, by default, access is only possible via SQL Server users. Active Directory authentication can be set up (the server must be joined to the domain using a Kerberos key), but this requires additional work.
  • LocalDB: the developer version of Express is only available for Windows.
  • SQL Server Management Studio (SSMS) on the server: SSMS only runs on Windows. From a Windows machine, you can use it to connect to a Linux server in exactly the same way as you would to a Windows server; on Linux and macOS, you can use the MSSQL extension for Visual Studio Code or sqlcmd.

If your application does not use any of these, SQL Server works in exactly the same way on Linux.

Advantages and disadvantages compared to Windows Express

Arguments in favour of Linux:

  • There is no Windows Server licence. The Express version is free on both systems, but on Windows the operating system licence costs money. A 4 GB KVM VPS (AMD VPS 4) costs €13.06 per month, whilst the smallest Windows VPS (Win VPS Start, 8 GB) costs €22.85 per month.
  • Fewer resources. Without a graphical user interface, Linux uses significantly less memory and disk space than Windows Server, leaving more of the server’s resources available for the database.
  • Simple updates and automation. Updating SQL Server involves either a dnf update or an image swap; the settings can be scripted, and the container can be migrated to another server in a matter of minutes.
  • Shared server with Linux applications. If the web application (PHP, Python, Node.js, .NET) also runs on Linux, the database can be located on the same machine and does not need to be accessed externally.

In favour of Windows:

  • Windows applications and features. If your program uses Windows authentication, FILESTREAM, xp_cmdshell, a linked server pointing to an Access or Excel source, Reporting Services or a custom component running as a Windows service, you should remain on Windows.
  • Graphical management on the server. SSMS and Windows tools run directly on the server via Remote Desktop; on Linux, everything is done via the command line or from another machine.
  • A familiar environment. If the team is familiar with Windows administration, introducing Linux will require some learning.
  • Vendor support. SQL Server is officially supported on every edition of Windows Server, but on Linux only on the listed distributions (not on AlmaLinux or Rocky Linux).

Troubleshooting

The service fails to start, displaying the error ‘Start request repeated too quickly’. The mssql-server service may start a maximum of three times within a two-minute period. If you have restarted it multiple times whilst experimenting with the settings, systemd will block the start. Unblock it, then restart it:

sudo systemctl reset-failed mssql-server
sudo systemctl start mssql-server

Why isn’t it starting? The service log is journalctl -u mssql-server, whilst SQL Server’s own log is /var/opt/mssql/log/errorlog (which can only be read by root and the mssql user). In Docker, both are visible in the output of the docker logs sql1 command.

Express does not allow a dedicated administrator connection (DAC). This feature is disabled by default in the Express edition; according to the log, it can be enabled using the trace flag 7806.

Which package should you choose?

SQL Server Express runs smoothly on Linux on a KVM VPS with at least 4 GB of RAM (AMD VPS 4: 2 vCPUs, 4 GB). If you wish to utilise all 4 processor cores of Express, or if a web application is also running on the machine alongside the database, the AMD VPS 8 (4 vCPU, 8 GB) is the better choice. For Windows-dependent applications, our Windows VPS plans, which come with a licensed copy of Windows Server, are suitable. If you are unsure which one is right for your application, please write to us and we will help you decide.

Back: Linux articles (63)