SQL Server on Linux crossed from curiosity to production-ready with the 2017 release, and by 2019 it had feature parity close enough to run serious workloads — OLTP, analytics, and even Always On Availability Groups — without a single Windows license. If your infrastructure already runs on RHEL or Ubuntu, deploying SQL Server on Linux eliminates the overhead of maintaining a separate Windows estate, reduces per-core licensing surface area, and slots neatly into existing Linux-native tooling for monitoring, backups, and automation. The tradeoffs are real but manageable: a handful of features remain Windows-only, and high availability requires Pacemaker instead of Windows Server Failover Clustering. This post walks through everything you need to run SQL Server on Linux in production — installation, performance tuning, Always On AG setup with Pacemaker, and monitoring.
- SQL Server 2017+ runs on RHEL 7/8/9 and Ubuntu 18.04/20.04/22.04 with near-full feature parity.
- Use
mssql-confto cap memory, set the edition, and tune I/O — critical on shared Linux hosts. - Always On AGs work on Linux via Pacemaker + Corosync; no Windows Server Failover Clustering required.
- SQL Agent, SSRS, and SSIS are absent or limited; use cron/elastic jobs, Power BI Report Server, and Azure Data Factory as replacements.
- Monitoring integrates naturally with Prometheus, Grafana, and
sys.dm_os_*DMVs.
SQL Server on Linux — What's Supported
Microsoft ships SQL Server for Linux as a native binary for x86-64 (ARM support arrived experimentally in 2022). The core database engine is identical to Windows: the same query optimizer, the same lock manager, the same TDS wire protocol. What changes is the platform abstraction layer — SQLOS on Linux uses a compatibility shim called SQLPAL that translates Windows kernel calls to Linux syscalls. From a DBA perspective, this is mostly invisible.
The following table summarizes feature availability as of SQL Server 2022:
| Feature | Linux Support | Workaround / Alternative |
|---|---|---|
| Always On Availability Groups | Full (Pacemaker) | — |
| SQL Server Agent | Partial (jobs, alerts; no multi-server) | Elastic Jobs, pg_cron-style cron wrappers |
| SSRS (Reporting Services) | Not supported | Power BI Report Server (Windows), Metabase, Grafana |
| SSIS (Integration Services) | Not supported | Azure Data Factory, dbt, custom Python ETL |
| Replication | Transactional & snapshot (subscriber only for merge) | Debezium CDC for full change capture |
| Full-Text Search | Full | — |
| In-Memory OLTP | Full | — |
| PolyBase | Full (2019+) | — |
| Machine Learning Services | Full (2019+, extensibility framework) | — |
| Distributed Transactions (MSDTC) | Supported (2017 CU15+) | — |
If your application relies on SQL Server Agent multi-server administration, SSRS paginated reports, or SSIS packages running directly on the SQL Server host, plan your migration carefully. These components require architectural changes — they cannot simply be installed on the same Linux box.
Installation on RHEL/Ubuntu
Microsoft publishes official RPM and DEB repositories. Installation is straightforward but requires accepting the EULA and choosing an edition before the service starts.
RHEL 8 / Rocky Linux 8:
# Add the Microsoft SQL Server repository
sudo curl -o /etc/yum.repos.d/mssql-server.repo \
https://packages.microsoft.com/config/rhel/8/mssql-server-2022.repo
# Install the server package
sudo yum install -y mssql-server
# Run the setup wizard: sets edition, SA password, and starts the service
sudo /opt/mssql/bin/mssql-conf setup
# Verify the service is running
systemctl status mssql-server --no-pager
# (Optional) Install command-line tools
sudo curl -o /etc/yum.repos.d/msprod.repo \
https://packages.microsoft.com/config/rhel/8/prod.repo
sudo yum install -y mssql-tools18 unixODBC-devel
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc
source ~/.bashrcUbuntu 22.04:
# Import the Microsoft GPG key and add the repository
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg
curl https://packages.microsoft.com/config/ubuntu/22.04/mssql-server-2022.list \
| sudo tee /etc/apt/sources.list.d/mssql-server-2022.list
sudo apt-get update
sudo apt-get install -y mssql-server
# Run setup
sudo /opt/mssql/bin/mssql-conf setup
# Install sqlcmd and bcp
curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list \
| sudo tee /etc/apt/sources.list.d/msprod.list
sudo apt-get update
sudo apt-get install -y mssql-tools18 unixodbc-devFor unattended/automated installs (CI, Ansible, Terraform), set ACCEPT_EULA=Y, MSSQL_SA_PASSWORD, and MSSQL_PID as environment variables before running mssql-conf setup. The setup script reads these variables and skips interactive prompts entirely.
After installation, open firewall port 1433 if remote connections are needed:
# firewalld (RHEL)
sudo firewall-cmd --permanent --add-port=1433/tcp
sudo firewall-cmd --reload
# ufw (Ubuntu)
sudo ufw allow 1433/tcpPerformance Tuning for Linux (mssql-conf)
On Linux, SQL Server uses the mssql-conf utility — a Python wrapper that writes to /var/opt/mssql/mssql.conf — to manage settings that on Windows live in the registry or SQL Server Configuration Manager.
Memory cap (most critical on shared hosts):
# Limit SQL Server to 8 GB — prevents OOM-killer on shared hosts
sudo /opt/mssql/bin/mssql-conf set memory.memorylimitmb 8000
# Confirm the setting
sudo /opt/mssql/bin/mssql-conf get memory
# Output: memory.memorylimitmb = 8000
sudo systemctl restart mssql-serverWithout this cap, SQL Server on Linux will claim as much RAM as the buffer pool can acquire — the same behavior as Windows — but on a Linux host running containers or other services, this leads to memory pressure across the entire machine.
Additional tuning commands:
# Set the number of dump threads (useful on high-core-count machines)
sudo /opt/mssql/bin/mssql-conf set sqlagent.dumpthreads 4
# Enable trace flag 1222 (deadlock details in error log) persistently
sudo /opt/mssql/bin/mssql-conf traceflag 1222 on
# Set default data and log directories
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultdatadir /data/mssql/data
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultlogdir /data/mssql/log
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultbackupdir /data/mssql/backup
sudo systemctl restart mssql-serverOS-level tuning:
# Disable transparent huge pages — same recommendation as on Windows bare metal
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
# Set swappiness low to avoid paging SQL Server buffer pool to disk
sudo sysctl -w vm.swappiness=10
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.d/99-mssql.conf
# Increase open file descriptor limit for the mssql user
echo "mssql soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "mssql hard nofile 65536" | sudo tee -a /etc/security/limits.confOn NVMe storage, set the I/O scheduler to none (or mq-deadline) for the SQL Server data volume. The kernel's CFQ scheduler adds unnecessary latency for database workloads: echo none | sudo tee /sys/block/nvme0n1/queue/scheduler. Make it persistent via udev rules.
Always On AG with Pacemaker (no Windows Cluster)
Always On Availability Groups on Linux use Pacemaker + Corosync as the cluster manager instead of Windows Server Failover Clustering. The SQL Server AG resource agent (ocf:mssql:ag) handles health checks and failover. This is a first-class supported configuration, not a hack.
High-level architecture: three nodes (primary + two secondaries), Pacemaker on each node, a virtual IP resource tied to the primary replica, automatic failover on primary failure.
# On ALL nodes: install Pacemaker, Corosync, and the SQL Server HA package
# RHEL:
sudo yum install -y pacemaker pcs corosync fence-agents-all
sudo yum install -y mssql-server-ha
# Ubuntu:
sudo apt-get install -y pacemaker pcs corosync
sudo apt-get install -y mssql-server-ha
# Enable and start pcsd
sudo systemctl enable --now pcsd
# Set hacluster user password (same on all nodes)
sudo passwd hacluster# On the PRIMARY node: authenticate cluster nodes
sudo pcs host auth node1 node2 node3 -u hacluster -p <password>
# Create the cluster
sudo pcs cluster setup mssql-ag-cluster node1 node2 node3
# Start the cluster
sudo pcs cluster start --all
sudo pcs cluster enable --allNext, create the AG inside SQL Server on the primary. Set CLUSTER_TYPE = EXTERNAL to signal that Pacemaker manages the cluster membership:
-- On PRIMARY replica
CREATE AVAILABILITY GROUP [ag1]
WITH (CLUSTER_TYPE = EXTERNAL)
FOR DATABASE [YourDB]
REPLICA ON
N'node1' WITH (
ENDPOINT_URL = N'tcp://node1:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = EXTERNAL,
SEEDING_MODE = AUTOMATIC),
N'node2' WITH (
ENDPOINT_URL = N'tcp://node2:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = EXTERNAL,
SEEDING_MODE = AUTOMATIC),
N'node3' WITH (
ENDPOINT_URL = N'tcp://node3:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = EXTERNAL,
SEEDING_MODE = AUTOMATIC);
-- Grant pacemaker login rights to manage the AG
GRANT ALTER, CONTROL, VIEW DEFINITION ON AVAILABILITY GROUP::[ag1] TO [pacemaker];
GRANT VIEW SERVER STATE TO [pacemaker];# On PRIMARY: register the AG resource with Pacemaker
sudo pcs resource create ag1 ocf:mssql:ag \
ag_name="ag1" meta failure-timeout=60s \
op start timeout=60s \
op stop timeout=60s \
op promote timeout=60s \
op demote timeout=10s \
op monitor timeout=60s interval=10s \
op monitor timeout=60s interval=11s role="Master" \
op monitor timeout=60s interval=12s role="Slave"
# Create a multi-state (master/slave) resource
sudo pcs resource promotable ag1 promoted-max=1 promoted-node-max=1 clone-max=3 clone-node-max=1
# Add a virtual IP for the listener
sudo pcs resource create virtualip ocf:heartbeat:IPaddr2 \
ip=10.0.0.50 cidr_netmask=24 \
op monitor interval=30s
# Collocate the VIP with the primary replica
sudo pcs constraint colocation add virtualip with master ag1-clone INFINITY with-rsc-role=Master
sudo pcs constraint order promote ag1-clone then start virtualipAlways configure a STONITH (fencing) resource in production Pacemaker clusters. Without fencing, Pacemaker cannot safely promote a secondary when the primary is unresponsive (network partition vs. node crash ambiguity). Use IPMI, iDRAC, iLO, or a cloud-native fencing agent. Running pcs property set stonith-enabled=false is acceptable only in a lab.
Monitoring with SQL Server on Linux
DMV-based monitoring works identically on Linux. The same queries that run against sys.dm_exec_requests, sys.dm_os_wait_stats, and sys.dm_io_virtual_file_stats on Windows run unchanged on Linux.
Prometheus + sql_exporter setup:
# Install sql_exporter (Burningalchemist fork — SQL Server compatible)
wget https://github.com/burningalchemist/sql_exporter/releases/download/0.14.3/sql_exporter-0.14.3.linux-amd64.tar.gz
tar -xzf sql_exporter-0.14.3.linux-amd64.tar.gz
sudo mv sql_exporter /usr/local/bin/
# Create a minimal config pointing at SQL Server
cat <<EOF | sudo tee /etc/sql_exporter/config.yml
global:
scrape_timeout_offset: 500ms
min_interval: 0s
target:
data_source_name: 'sqlserver://sa:YourPassword@localhost:1433'
collectors:
- mssql_standard
EOF
sudo systemctl enable --now sql_exporterKey DMV queries for a Linux health dashboard:
-- Top wait types (aggregate since last restart)
SELECT TOP 15
wait_type,
waiting_tasks_count,
wait_time_ms / 1000.0 AS wait_time_sec,
max_wait_time_ms / 1000.0 AS max_wait_sec,
signal_wait_time_ms / 1000.0 AS signal_wait_sec
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'SLEEP_TASK','BROKER_TO_FLUSH','BROKER_TASK_STOP',
'CLR_AUTO_EVENT','DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT','HADR_WORK_QUEUE',
'LAZYWRITER_SLEEP','LOGMGR_QUEUE','ONDEMAND_TASK_QUEUE',
'REQUEST_FOR_DEADLOCK_SEARCH','RESOURCE_QUEUE',
'SERVER_IDLE_CHECK','SLEEP_DBSTARTUP','SLEEP_DBRECOVER',
'SLEEP_MASTERDBREADY','SLEEP_MASTERMDREADY',
'SLEEP_MASTERUPGRADED','SLEEP_MSDBSTARTUP',
'SLEEP_SYSTEMTASK','SLEEP_TEMPDBSTARTUP',
'SNI_HTTP_ACCEPT','SP_SERVER_DIAGNOSTICS_SLEEP',
'SQLTRACE_BUFFER_FLUSH','WAITFOR','XE_DISPATCHER_WAIT',
'XE_TIMER_EVENT'
)
ORDER BY wait_time_ms DESC;
-- AG replica sync status
SELECT
ag.name AS ag_name,
ar.replica_server_name,
ars.role_desc,
ars.synchronization_health_desc,
ars.connected_state_desc,
adbrs.synchronization_state_desc,
adbrs.log_send_queue_size,
adbrs.redo_queue_size
FROM sys.dm_hadr_availability_replica_states ars
JOIN sys.availability_replicas ar ON ars.replica_id = ar.replica_id
JOIN sys.availability_groups ag ON ar.group_id = ag.group_id
LEFT JOIN sys.dm_hadr_database_replica_states adbrs ON ars.replica_id = adbrs.replica_id;For log-based alerting, SQL Server on Linux writes to /var/opt/mssql/log/errorlog. Tail it with journalctl -u mssql-server -f or ship it to a log aggregator via Filebeat/Fluentd using the standard JSON or plaintext pipeline.
The mssql-server-ha package installs a Pacemaker resource agent AND a custom health probe endpoint on port 5022. You can curl this endpoint from an external load balancer health check to route application traffic only to the current primary replica without a DNS TTL dependency.
Key Takeaways
- SQL Server 2019 and 2022 on RHEL/Ubuntu deliver production-grade performance with the same query engine as Windows — benchmark parity is within 2-5% for most OLTP workloads.
- Always run
mssql-conf set memory.memorylimitmbon every Linux SQL Server instance — uncontrolled memory growth will starve the OS and other services on the same host. - Always On AGs with Pacemaker + Corosync are the correct HA path on Linux; WSFC is not available and not needed.
- STONITH fencing is mandatory in any Pacemaker cluster that manages shared resources — never disable it in production.
- SQL Agent works for jobs and alerts; replace SSRS with Power BI Report Server or Grafana, and replace SSIS with Azure Data Factory, dbt, or a custom Python/Airflow pipeline.
- Monitoring integrates naturally into the Linux ecosystem: Prometheus + sql_exporter for metrics, Filebeat for logs, standard DMVs for query-level diagnostics.
- Container deployments (Docker/Kubernetes) work well for dev/test; for production HA, bare metal or VMs give you direct control over NUMA topology, storage scheduling, and Pacemaker cluster membership.
Working with JusDB on SQL Server
JusDB specializes in SQL Server deployments across Windows and Linux — from greenfield installations on bare metal RHEL clusters to migrating legacy Windows-only workloads to containerized Linux environments. Our team has configured Pacemaker-managed Always On AGs, tuned mssql-conf for large-memory production hosts, and built Prometheus/Grafana dashboards purpose-built for SQL Server on Linux. Whether you need a full production setup, a performance audit of an existing Linux SQL Server instance, or architectural guidance on replacing SSIS and SSRS in a Linux-first stack, we can help.
Explore JusDB SQL Server Consulting → | Talk to a DBA
Need hands-on SQL Server help?
JusDB's certified SQL Server DBAs handle performance tuning, Always On AG, migrations, and 24/7 operations.
SQL Server Consulting → | SQL Server Remote DBA → | 24/7 SQL Server Support →