CtrlK
BlogDocsLog inGet started
Tessl Logo

alonso-skills/mssql-server

Writes, optimizes, and debugs T-SQL queries. Explains SQL Server internals, troubleshoots performance issues, and guides database administration tasks including backup/restore, high availability, security, and index design. Use when the user asks about T-SQL syntax, SQL Server administration, query performance, stored procedures, indexes, locking, transactions, backup/restore, high availability, security, or any MSSQL-related topic — even without saying 'SQL Server' explicitly. Also trigger on terms like SSMS, tempdb, bcp, sqlcmd, MSSQL, sp_executesql, NOLOCK, columnstore, Hekaton, RCSI, param sniffing, or execution plan.

100

Quality

100%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

54-linux-containers.mdreferences/

SQL Server on Linux and Containers

Complete reference for running SQL Server on Linux, in Docker containers, and in Kubernetes. Covers mssql-conf, Docker volume mounts, networking, Linux-specific limitations, and HA with Pacemaker-based Availability Groups.

Table of Contents

  1. When to Use
  2. Supported Linux Platforms
  3. Installation Overview
  4. mssql-conf Reference
  5. Key mssql-conf Settings
  6. Docker: Quick Start
  7. Docker: Environment Variables
  8. Docker: Volume Mounts
  9. Docker: Networking and Ports
  10. Docker: Multi-Container Compose
  11. Container Initialization Scripts
  12. mssql-tools and PATH
  13. Linux Limitations vs Windows
  14. File System and Permissions
  15. Performance Tuning on Linux
  16. TLS/SSL Configuration
  17. Active Directory Authentication on Linux
  18. HA on Linux: Pacemaker-Based AGs
  19. Kubernetes Deployment Patterns
  20. Monitoring and Diagnostics
  21. Backup and Restore on Linux
  22. Common Patterns
  23. Gotchas
  24. See Also
  25. Sources

When to Use

Load this file when the user asks about:

  • Running SQL Server on RHEL, Ubuntu, SLES, or other Linux distros
  • SQL Server in Docker containers or Kubernetes
  • mssql-conf settings and configuration
  • mssql-tools CLI tools path and usage on Linux
  • Linux-specific SQL Server limitations (no MSDTC, no FileStream, etc.)
  • Always On AG with Pacemaker on Linux
  • SQL Server Linux performance tuning
  • Active Directory / Kerberos auth on Linux SQL Server

Supported Linux Platforms

DistroSQL Server 2019SQL Server 2022
RHEL 8.x
RHEL 9.x
Ubuntu 18.04
Ubuntu 20.04
Ubuntu 22.04
SLES 12 SP5
SLES 15

[!NOTE] SQL Server 2022 RHEL 9 and Ubuntu 22.04 support added in SQL Server 2022 CU 10 and later versions. 1

Minimum hardware:

  • 2 GB RAM (8 GB recommended for production)
  • 10 GB disk space
  • x86-64 or ARM64 (limited platform support — check release notes)

Installation Overview

# Ubuntu 22.04 example
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/22.04/mssql-server-2022.list \
  | sudo tee /etc/apt/sources.list.d/mssql-server.list

sudo apt-get update
sudo apt-get install -y mssql-server

# Run setup wizard (sets SA password, edition, etc.)
sudo /opt/mssql/bin/mssql-conf setup

# Verify
systemctl status mssql-server

# Install mssql-tools (sqlcmd, 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-tools unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc

mssql-conf Reference

mssql-conf is the primary Linux configuration utility, replacing registry keys used on Windows. Settings are stored in /var/opt/mssql/mssql.conf (INI format).

# Get a setting
sudo /opt/mssql/bin/mssql-conf get sqlagent enabled

# Set a setting
sudo /opt/mssql/bin/mssql-conf set sqlagent.enabled true

# Bulk apply settings from stdin
sudo /opt/mssql/bin/mssql-conf set-collation SQL_Latin1_General_CP1_CI_AS

# View current configuration
cat /var/opt/mssql/mssql.conf

# Direct edit (requires service restart)
sudo nano /var/opt/mssql/mssql.conf

/var/opt/mssql/mssql.conf format:

[sqlagent]
enabled = true

[memory]
memorylimitmb = 4096

[network]
tcpport = 1433
tlsprotocols = 1.2
tlscert = /etc/ssl/certs/mssql.pem
tlskey = /etc/ssl/private/mssql.key

[EULA]
accepteula = Y

[coredump]
coredumptype = mini

[hadr]
hadrenabled = 1

[traceflag]
traceflag0 = 3226
traceflag1 = 1117

[!WARNING] Service restart required Most mssql-conf changes require restarting the SQL Server service: sudo systemctl restart mssql-server


Key mssql-conf Settings

Memory

# Limit SQL Server memory (in MB) — critical on shared Linux hosts
sudo /opt/mssql/bin/mssql-conf set memory.memorylimitmb 4096
SettingSectionDescriptionDefault
memorylimitmb[memory]Max server memory in MBUnlimited
memorylimitmb should leave 1–2 GB for OS

Network

sudo /opt/mssql/bin/mssql-conf set network.tcpport 1433
sudo /opt/mssql/bin/mssql-conf set network.tlsprotocols 1.2
sudo /opt/mssql/bin/mssql-conf set network.tlscert /etc/ssl/certs/mssql.pem
sudo /opt/mssql/bin/mssql-conf set network.tlskey /etc/ssl/private/mssql.key
sudo /opt/mssql/bin/mssql-conf set network.forceencryption 1

SQL Agent

sudo /opt/mssql/bin/mssql-conf set sqlagent.enabled true
# Restart required
sudo systemctl restart mssql-server

HADR (Always On)

sudo /opt/mssql/bin/mssql-conf set hadr.hadrenabled 1
sudo systemctl restart mssql-server

File Paths

# Move data, log, and backup default directories
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultdatadir /mnt/data
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultlogdir  /mnt/log
sudo /opt/mssql/bin/mssql-conf set filelocation.defaultbackupdir /mnt/backup
sudo /opt/mssql/bin/mssql-conf set filelocation.masterdatafile /mnt/data/master.mdf
sudo /opt/mssql/bin/mssql-conf set filelocation.masterlogfile /mnt/log/mastlog.ldf

[!WARNING] Moving master database files requires careful procedure — the service must be pointed to the new paths before moving the files, or it will fail to start. Always test in a non-production environment first.

Trace Flags

# Set trace flags (restarts automatically clear these unless in mssql.conf)
sudo /opt/mssql/bin/mssql-conf traceflag 3226 on   # suppress backup success messages
sudo /opt/mssql/bin/mssql-conf traceflag 1117 on   # uniform file growth (pre-2016 default)

Collation

# Set server collation (destructive — wipes and recreates system DBs)
sudo /opt/mssql/bin/mssql-conf set-collation SQL_Latin1_General_CP1_CI_AS

[!WARNING] set-collation destroys all system databases. Only run on fresh installs.

Edition

# Set edition using PID (product key) or edition name
sudo /opt/mssql/bin/mssql-conf set-edition Enterprise
# Valid: Enterprise, Standard, Web, Developer, Express, Evaluation

Core Dumps

sudo /opt/mssql/bin/mssql-conf set coredump.coredumptype full   # mini, full, filtered
sudo /opt/mssql/bin/mssql-conf set coredump.captureminiandfull true

TempDB Configuration

# Set number of TempDB data files (default = 8, can be 1-128)
sudo /opt/mssql/bin/mssql-conf set sqltempdbfilecount 8
sudo /opt/mssql/bin/mssql-conf set sqltempdbfilesize 512       # initial size MB
sudo /opt/mssql/bin/mssql-conf set sqltempdbfilegrowth 64      # growth in MB
sudo /opt/mssql/bin/mssql-conf set sqltempdblogfilesize 128
sudo /opt/mssql/bin/mssql-conf set sqltempdblogfilegrowth 64

Docker: Quick Start

# SQL Server 2022
docker run -e "ACCEPT_EULA=Y" \
           -e "MSSQL_SA_PASSWORD=YourStrong@Passw0rd" \
           -e "MSSQL_PID=Developer" \
           -p 1433:1433 \
           --name sql1 \
           --hostname sql1 \
           -d \
           mcr.microsoft.com/mssql/server:2022-latest

# Connect with sqlcmd inside the container
docker exec -it sql1 /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U SA -P "YourStrong@Passw0rd"

# Or from the host if mssql-tools is installed
sqlcmd -S localhost,1433 -U SA -P "YourStrong@Passw0rd"

Official Image Tags

TagDescription
2022-latestLatest SQL Server 2022 CU
2022-CU12-ubuntu-22.04Specific CU on Ubuntu 22.04
2019-latestLatest SQL Server 2019 CU
2022-previewPreview builds

[!NOTE] Always pin to a specific CU tag in production (2022-CU12-ubuntu-22.04), not latest, to ensure reproducible builds.


Docker: Environment Variables

VariableRequiredDescriptionExample
ACCEPT_EULAYesAccept license agreementY
MSSQL_SA_PASSWORDYes (or SA_PASSWORD)SA password (min 8 chars, complexity)MyP@ssw0rd
SA_PASSWORDLegacyAlias for MSSQL_SA_PASSWORD
MSSQL_PIDNoEdition or product keyDeveloper, Express, Standard, Enterprise, Web, Evaluation
MSSQL_LCIDNoLocale ID for collation1033 (en-US)
MSSQL_COLLATIONNoServer collationSQL_Latin1_General_CP1_CI_AS
MSSQL_MEMORY_LIMIT_MBNoMax server memory in MB4096
MSSQL_TCP_PORTNoTCP port inside container1433
MSSQL_IP_ADDRESSNoBind address0.0.0.0
MSSQL_AGENT_ENABLEDNoEnable SQL Agenttrue
MSSQL_ENABLE_HADRNoEnable HADR for AG support1
MSSQL_DATA_DIRNoDefault data file path/var/opt/mssql/data
MSSQL_LOG_DIRNoDefault log file path/var/opt/mssql/log
MSSQL_BACKUP_DIRNoDefault backup path/var/opt/mssql/backup
TZNoContainer timezone (affects GETDATE())America/New_York

[!WARNING] SA_PASSWORD complexity The SA password must meet SQL Server complexity requirements: at least 8 chars, containing uppercase, lowercase, digit, and symbol. Weak passwords cause the container to start then immediately exit — check docker logs sql1.


Docker: Volume Mounts

Without volume mounts, all data is lost when the container is removed.

# Create named volumes
docker volume create sqldata
docker volume create sqllog
docker volume create sqlbackup

# Run with volumes mounted
docker run -e "ACCEPT_EULA=Y" \
           -e "MSSQL_SA_PASSWORD=YourStrong@Passw0rd" \
           -p 1433:1433 \
           --name sql1 \
           -v sqldata:/var/opt/mssql/data \
           -v sqllog:/var/opt/mssql/log \
           -v sqlbackup:/var/opt/mssql/backup \
           -d mcr.microsoft.com/mssql/server:2022-latest

# Or bind-mount host directories
docker run -e "ACCEPT_EULA=Y" \
           -e "MSSQL_SA_PASSWORD=YourStrong@Passw0rd" \
           -p 1433:1433 \
           --name sql1 \
           -v /mnt/sqldata:/var/opt/mssql/data \
           -v /mnt/sqllog:/var/opt/mssql/log \
           -v /mnt/sqlbackup:/var/opt/mssql/backup \
           -d mcr.microsoft.com/mssql/server:2022-latest

[!WARNING] File ownership SQL Server in the container runs as UID 10001 (mssql user). Host-mounted directories must be writable by UID 10001:

sudo chown -R 10001:0 /mnt/sqldata /mnt/sqllog /mnt/sqlbackup

Failure to set ownership causes SQL Server to fail to start or create databases.

Default Container Paths

PurposeContainer Path
Data files (.mdf, .ndf)/var/opt/mssql/data/
Log files (.ldf)/var/opt/mssql/log/
Error log/var/opt/mssql/log/errorlog
Backups/var/opt/mssql/backup/
mssql.conf/var/opt/mssql/mssql.conf
Secrets/var/opt/mssql/secrets/

Docker: Networking and Ports

# Default: map container 1433 to host 1433
-p 1433:1433

# Named instance or alternate port (not a Windows-style named instance)
-p 1434:1433 -e MSSQL_TCP_PORT=1433

# Expose to localhost only (security best practice)
-p 127.0.0.1:1433:1433

# Multiple containers on same host — use different host ports
docker run ... -p 1434:1433 --name sql2 ...
docker run ... -p 1435:1433 --name sql3 ...

# Container-to-container (same network) — use container name as hostname
docker network create sqlnet
docker run ... --network sqlnet --name sql1 ...
# Connect from another container: Server=sql1,1433

[!NOTE] SQL Server on Linux does not support named instances in the Windows sense (e.g., HOSTNAME\INSTANCE). Each instance is a separate process listening on a specific TCP port. Use port-based connection strings: Server=host,1434.


Docker: Multi-Container Compose

# docker-compose.yml
version: "3.8"
services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04
    container_name: sql1
    hostname: sql1
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "${SA_PASSWORD}"
      MSSQL_PID: "Developer"
      MSSQL_AGENT_ENABLED: "true"
      MSSQL_MEMORY_LIMIT_MB: "4096"
    ports:
      - "127.0.0.1:1433:1433"
    volumes:
      - sqldata:/var/opt/mssql/data
      - sqllog:/var/opt/mssql/log
      - sqlbackup:/var/opt/mssql/backup
      - ./init:/docker-entrypoint-initdb.d   # custom init scripts
    healthcheck:
      test: /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "$$MSSQL_SA_PASSWORD"
             -Q "SELECT 1" -b -o /dev/null
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    restart: unless-stopped

volumes:
  sqldata:
  sqllog:
  sqlbackup:
# .env file (never commit to source control)
SA_PASSWORD=YourStrong@Passw0rd

# Start
docker compose up -d

# View logs
docker compose logs -f sqlserver

Container Initialization Scripts

The official SQL Server container image does not have a built-in init script mechanism like Postgres. Use a wrapper approach:

#!/bin/bash
# entrypoint.sh — run alongside sqlservr
set -e

# Wait for SQL Server to be ready
wait_for_sql() {
  local -r max_attempts=30
  local attempt=1
  while ! /opt/mssql-tools/bin/sqlcmd \
    -S localhost -U SA -P "$MSSQL_SA_PASSWORD" \
    -Q "SELECT 1" -b -o /dev/null 2>/dev/null; do
    echo "Waiting for SQL Server... attempt $attempt/$max_attempts"
    sleep 2
    ((attempt++))
    if [[ $attempt -gt $max_attempts ]]; then
      echo "SQL Server failed to start" >&2
      exit 1
    fi
  done
  echo "SQL Server is ready."
}

# Start SQL Server in background
/opt/mssql/bin/sqlservr &
SQL_PID=$!

wait_for_sql

# Run init scripts
for f in /docker-entrypoint-initdb.d/*.sql; do
  echo "Running $f..."
  /opt/mssql-tools/bin/sqlcmd \
    -S localhost -U SA -P "$MSSQL_SA_PASSWORD" \
    -i "$f" -b
done

# Wait for SQL Server process
wait $SQL_PID
FROM mcr.microsoft.com/mssql/server:2022-latest
COPY entrypoint.sh /
COPY init/*.sql /docker-entrypoint-initdb.d/
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

mssql-tools and PATH

# mssql-tools installs sqlcmd and bcp to:
/opt/mssql-tools/bin/sqlcmd
/opt/mssql-tools/bin/bcp

# Add to PATH permanently
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc

# Or for all users
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' | \
  sudo tee /etc/profile.d/mssql-tools.sh

# Newer mssql-tools18 path (2022+ packages)
/opt/mssql-tools18/bin/sqlcmd

[!NOTE] mssql-tools18 SQL Server 2022 ships with mssql-tools18 (using ODBC Driver 18) which enforces TLS encryption by default. Connection strings may need -N (encrypt) and -C (trust server certificate) flags:

sqlcmd -S localhost -U SA -P "$SA_PASSWORD" -N -C

sqlcmd Quick Reference on Linux

# Interactive session
sqlcmd -S localhost -U SA -P "MyPass" -d MyDatabase

# Run a script
sqlcmd -S localhost -U SA -P "MyPass" -i /path/to/script.sql -o /path/to/output.log -b

# One-liner query
sqlcmd -S localhost -U SA -P "MyPass" -Q "SELECT @@VERSION" -h -1 -W

# With variable substitution
sqlcmd -S localhost -U SA -P "MyPass" \
  -v DbName="Production" \
  -i deploy.sql

# Using Kerberos (AD auth)
sqlcmd -S hostname.domain.com -E    # -E = trusted connection / AD auth

Linux Limitations vs Windows

Features Not Available on Linux

FeatureStatusNotes / Alternative
MSDTC (Distributed Transactions)LimitedAvailable from SQL Server 2017 CU 16+ with configuration; not default 2
FileStreamNot supportedUse FILESTREAM alternatives or store BLOBs externally
FileTableNot supportedDepends on FileStream
Windows Authentication (Kerberos)Supported with setupRequires adutil, keytab, and /etc/krb5.conf configuration
SQL Server BrowserNot availableNot needed — use explicit port connections
Named instancesNot supportedUse port-based connections instead
Machine Learning Services (R/Python)SupportedSeparate package install required
PolyBaseSupportedAdditional configuration needed
Full-Text SearchSupportedmssql-server-fts package required
SQL Server Reporting ServicesNot supportedUse Windows host or Power BI Report Server
SQL Server Analysis ServicesNot supportedWindows only
SQL Server Integration ServicesSupportedmssql-server-is package, Linux-compatible packages only
ActiveX scripting job stepsNot supportedDeprecated feature
ReplicationSupportedTransactional, snapshot (not merge in all versions)
Database MirroringNot supportedDeprecated; use Always On AG
Log ShippingSupportedFull support
Backup to tapeNot supportedUse disk or S3/Azure Blob
WMI ProviderNot supportedUse T-SQL or PowerShell alternatives

Features Available but Different

FeatureLinux Difference
SQL Server AgentRuns as separate mssqlagent process; enabled via mssql-conf
Event Viewer / Windows Event LogLogs go to syslog or journald; read with journalctl -u mssql-server
Perfmon countersUse sys.dm_os_performance_counters or collectd
Windows ACLsUse Linux file permissions; SQL Server runs as mssql user
TempDBUses tmpfs if configured; same behavior otherwise
Case sensitivityFilesystem is case-sensitive on Linux — script filenames carefully

File System and Permissions

SQL Server on Linux runs as the mssql system user (UID 10001 in containers). All data directories must be owned by this user.

# Check current ownership
ls -la /var/opt/mssql/

# Fix permissions for custom directories
sudo chown -R mssql:mssql /mnt/sqldata /mnt/sqllog /mnt/sqlbackup
sudo chmod 700 /mnt/sqldata /mnt/sqllog /mnt/sqlbackup

# In Docker: UID 10001 maps to mssql inside container
sudo chown -R 10001:0 /mnt/sqldata
sudo chmod -R 770 /mnt/sqldata

Recommended File System Choices

FilesystemData FilesNotes
XFS✓ PreferredBest performance, supports sparse files for snapshots
ext4✓ GoodSolid choice, slightly lower throughput than XFS
tmpfsTempDB onlyMemory-backed; fast but volatile
NFS✗ AvoidHigh latency; not supported for SQL Server data files
CIFS/SMB✗ AvoidNot supported
Azure Premium SSDFor Azure VMs; use P30+ for data
io_uring2022+Async I/O interface on newer Linux kernels; no SQL Server-specific documentation confirms native io_uring integration
# Check filesystem type
df -T /var/opt/mssql/

# Recommended mount options for XFS (in /etc/fstab)
# noatime reduces unnecessary metadata writes
/dev/sdb1  /mnt/sqldata  xfs  defaults,noatime  0  2

Performance Tuning on Linux

Huge Pages / Transparent Huge Pages

# Check THP status
cat /sys/kernel/mm/transparent_hugepage/enabled
# Output: always [madvise] never
# SQL Server prefers: madvise (allows SQL Server to request THPs selectively)

# Set madvise
echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

# Make permanent (RHEL/CentOS via grub, Ubuntu via rc.local or systemd)
# Add to /etc/rc.local:
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

Swappiness

# SQL Server needs predictable memory — reduce swappiness
echo 10 | sudo tee /proc/sys/vm/swappiness

# Permanent: add to /etc/sysctl.conf
vm.swappiness = 10

CPU Scheduler

# Use performance CPU governor (not powersave)
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
  echo performance | sudo tee "$cpu"
done

Open Files Limit

# SQL Server may need many file descriptors; check limits
cat /proc/$(pgrep sqlservr)/limits | grep "Open files"

# Increase in /etc/security/limits.conf
mssql soft nofile 65536
mssql hard nofile 65536

NUMA Topology

# Verify NUMA nodes
numactl --hardware

# SQL Server automatically detects and uses NUMA on Linux (same as Windows)
# Check via DMV
SELECT scheduler_id, cpu_id, node_id, status
FROM sys.dm_os_schedulers
WHERE status = 'VISIBLE ONLINE';

TLS/SSL Configuration

# Generate self-signed cert (dev/test only)
openssl req -x509 -nodes -newkey rsa:2048 \
  -subj "/CN=$(hostname)" \
  -keyout /etc/ssl/private/mssql.key \
  -out /etc/ssl/certs/mssql.pem \
  -days 365

# Set ownership
sudo chown mssql:mssql /etc/ssl/private/mssql.key /etc/ssl/certs/mssql.pem
sudo chmod 600 /etc/ssl/private/mssql.key

# Configure via mssql-conf
sudo /opt/mssql/bin/mssql-conf set network.tlscert /etc/ssl/certs/mssql.pem
sudo /opt/mssql/bin/mssql-conf set network.tlskey  /etc/ssl/private/mssql.key
sudo /opt/mssql/bin/mssql-conf set network.tlsprotocols 1.2,1.3
sudo /opt/mssql/bin/mssql-conf set network.forceencryption 1
sudo systemctl restart mssql-server

[!NOTE] mssql-tools18 defaults mssql-tools18 enables encryption by default. When connecting to a server with a self-signed cert, trust it explicitly:

sqlcmd -S server -U SA -P pass -N -C   # -C = TrustServerCertificate

Active Directory Authentication on Linux

SQL Server 2019+ supports Windows Authentication on Linux via Kerberos.

# Install adutil (Microsoft's AD integration tool)
# RHEL/CentOS:
sudo yum install -y adutil

# Join the domain (using realm or sssd)
sudo realm join --user=Administrator CONTOSO.COM

# Create SQL Server AD user and SPN
adutil user create --name sqluser --password 'P@ssw0rd' --ou 'OU=Service Accounts,DC=contoso,DC=com'
adutil spn createauto -n sqluser -s MSSQLSvc -H sql-linux.contoso.com -p 1433 -P 'P@ssw0rd'

# Create keytab
adutil keytab createauto -k /var/opt/mssql/secrets/mssql.keytab \
  -p 1433 -H sql-linux.contoso.com -P 'P@ssw0rd' -s MSSQLSvc

# Configure SQL Server to use keytab
sudo /opt/mssql/bin/mssql-conf set network.kerberoskeytabfile \
  /var/opt/mssql/secrets/mssql.keytab
sudo systemctl restart mssql-server

# Connect with Windows Authentication from Linux client
sqlcmd -S sql-linux.contoso.com -E    # requires kinit first
# Test Kerberos ticket
kinit administrator@CONTOSO.COM
klist    # verify ticket
sqlcmd -S sql-linux.contoso.com -E

HA on Linux: Pacemaker-Based AGs

Always On AGs on Linux use Pacemaker + Corosync instead of Windows Server Failover Clustering (WSFC).

[!NOTE] SQL Server 2017+ Pacemaker-based AG support introduced in SQL Server 2017 on Linux. 3

Architecture Comparison

AspectWindows (WSFC)Linux (Pacemaker)
Cluster softwareWindows Server Failover ClusteringPacemaker + Corosync
QuorumWindows quorumCorosync/CMAN quorum
Resource agentBuilt-inmssql-server-ha package
Health monitoringWindows Health ServicePacemaker health agent
Virtual IPWindows Cluster IPPacemaker IPaddr2 resource
FencingWindows storage fencingSBD (Storage-Based Death) or IPMI
External cluster onlyNoYes (CLUSTER_TYPE=EXTERNAL)

Cluster Types

CLUSTER_TYPEWhen to use
WSFCWindows Server Failover Clustering
EXTERNALLinux Pacemaker (automatic failover supported)
NONENo cluster manager; manual failover only (read-scale AGs)

Setup Steps (High Level)

# 1. Enable HADR on each node
sudo /opt/mssql/bin/mssql-conf set hadr.hadrenabled 1
sudo systemctl restart mssql-server

# 2. Install Pacemaker packages (RHEL)
sudo yum install -y pacemaker pcs fence-agents-all

# 3. Install SQL Server HA extension
sudo yum install -y mssql-server-ha

# 4. Configure Pacemaker cluster
sudo pcs cluster auth node1 node2 node3 -u hacluster -p hapassword
sudo pcs cluster setup --name sqlcluster node1 node2 node3
sudo pcs cluster start --all
sudo pcs cluster enable --all

# 5. Disable STONITH (fencing) for testing only — ENABLE in production
sudo pcs property set stonith-enabled=false   # NOT for production

# 6. Create AG via T-SQL (CLUSTER_TYPE = EXTERNAL)
# (run on primary node)
-- Step 6: Create AG on primary (Linux Pacemaker)
CREATE AVAILABILITY GROUP [ag1]
WITH (CLUSTER_TYPE = EXTERNAL)
FOR 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
  );
GO

-- Grant cluster permissions
GRANT ALTER, CONTROL, VIEW DEFINITION
  ON AVAILABILITY GROUP::ag1 TO [NT AUTHORITY\SYSTEM];
GRANT VIEW SERVER STATE TO [NT AUTHORITY\SYSTEM];
-- Run on secondaries
ALTER AVAILABILITY GROUP [ag1] JOIN WITH (CLUSTER_TYPE = EXTERNAL);
ALTER AVAILABILITY GROUP [ag1] GRANT CREATE ANY DATABASE;
# 7. Register AG as Pacemaker resource
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=60s \
  op monitor timeout=60s interval=10s \
  op monitor timeout=60s interval=11s role="Master" \
  op monitor timeout=60s interval=12s role="Slave"

sudo pcs resource master ag1-master ag1 master-max=1 master-node-max=1 \
  clone-max=3 clone-node-max=1 notify=true

# 8. Configure virtual IP resource
sudo pcs resource create virtualip ocf:heartbeat:IPaddr2 ip=192.168.1.100 \
  cidr_netmask=24 op monitor interval=30s

# 9. Colocation and ordering constraints
sudo pcs constraint colocation add virtualip ag1-master INFINITY with-rsc-role=Master
sudo pcs constraint order promote ag1-master then start virtualip

Read-Scale AG (No Cluster Manager)

-- For read-scale only (Linux or Windows), no automatic failover
CREATE AVAILABILITY GROUP [read_ag]
WITH (CLUSTER_TYPE = NONE)
FOR REPLICA ON
  N'primary' WITH (
    ENDPOINT_URL = N'tcp://primary:5022',
    AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
    FAILOVER_MODE = MANUAL,
    SEEDING_MODE = AUTOMATIC
  ),
  N'secondary' WITH (
    ENDPOINT_URL = N'tcp://secondary:5022',
    AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
    FAILOVER_MODE = MANUAL,
    SEEDING_MODE = AUTOMATIC
  );

Kubernetes Deployment Patterns

[!WARNING] SQL Server is stateful — production K8s deployments require careful storage class configuration. Use this as a starting point, not a production blueprint.

# StatefulSet example (simplified — not production-ready)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mssql
spec:
  serviceName: mssql
  replicas: 1
  selector:
    matchLabels:
      app: mssql
  template:
    metadata:
      labels:
        app: mssql
    spec:
      containers:
      - name: mssql
        image: mcr.microsoft.com/mssql/server:2022-latest
        ports:
        - containerPort: 1433
        env:
        - name: ACCEPT_EULA
          value: "Y"
        - name: MSSQL_SA_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mssql-secret
              key: sa-password
        - name: MSSQL_PID
          value: "Developer"
        - name: MSSQL_MEMORY_LIMIT_MB
          value: "4096"
        volumeMounts:
        - name: mssqldb
          mountPath: /var/opt/mssql
        livenessProbe:
          exec:
            command:
            - /bin/sh
            - -c
            - /opt/mssql-tools/bin/sqlcmd -S localhost
              -U SA -P "$MSSQL_SA_PASSWORD" -Q "SELECT 1" -b
          initialDelaySeconds: 30
          periodSeconds: 15
        readinessProbe:
          exec:
            command:
            - /bin/sh
            - -c
            - /opt/mssql-tools/bin/sqlcmd -S localhost
              -U SA -P "$MSSQL_SA_PASSWORD" -Q "SELECT 1" -b
          initialDelaySeconds: 10
          periodSeconds: 10
  volumeClaimTemplates:
  - metadata:
      name: mssqldb
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "premium-ssd"    # use fast storage class
      resources:
        requests:
          storage: 100Gi
---
apiVersion: v1
kind: Service
metadata:
  name: mssql
spec:
  selector:
    app: mssql
  ports:
  - port: 1433
    targetPort: 1433
  type: ClusterIP
# Create secret for SA password
kubectl create secret generic mssql-secret \
  --from-literal=sa-password='YourStrong@Passw0rd'

[!NOTE] For production K8s deployments consider using the SQL Server Operator for Kubernetes (preview) or deploy SQL Server on Azure SQL Managed Instance for Kubernetes (Arc-enabled SQL MI). 4


Monitoring and Diagnostics

Log Locations

# Error log (most important)
cat /var/opt/mssql/log/errorlog
tail -f /var/opt/mssql/log/errorlog

# systemd journal
journalctl -u mssql-server -f
journalctl -u mssql-server --since "1 hour ago"

# SQL Agent log
cat /var/opt/mssql/log/sqlagent.out

# Dump files (crashes)
ls /var/opt/mssql/log/*.mdmp
ls /var/opt/mssql/log/*.log

DMV Diagnostics (same as Windows)

-- Check SQL Server version and platform
SELECT @@VERSION;
SELECT SERVERPROPERTY('Platform');   -- 'Linux'
SELECT SERVERPROPERTY('Edition');

-- OS-level metrics
SELECT physical_memory_in_use_kb, page_fault_count
FROM sys.dm_os_process_memory;

-- Scheduler/CPU
SELECT scheduler_id, cpu_id, node_id, status, is_online
FROM sys.dm_os_schedulers
WHERE status = 'VISIBLE ONLINE';

-- I/O by file
SELECT DB_NAME(database_id) AS db,
       physical_name,
       io_stall_read_ms / NULLIF(num_of_reads, 0) AS avg_read_ms,
       io_stall_write_ms / NULLIF(num_of_writes, 0) AS avg_write_ms
FROM sys.dm_io_virtual_file_stats(NULL, NULL) f
JOIN sys.master_files mf ON f.database_id = mf.database_id
                         AND f.file_id = mf.file_id
ORDER BY io_stall_read_ms + io_stall_write_ms DESC;

Crash Dump Configuration

# Configure automatic crash dumps
sudo /opt/mssql/bin/mssql-conf set coredump.coredumptype filtered
sudo /opt/mssql/bin/mssql-conf set coredump.captureminiandfull true

# Location of dump files
ls /var/opt/mssql/log/*.mdmp

# Send dump to Microsoft Support
# Use mssqlsupport tool (must be installed separately)

Backup and Restore on Linux

Backup and restore T-SQL syntax is identical to Windows. Key differences:

# Paths use forward slashes
BACKUP DATABASE MyDB
  TO DISK = '/var/opt/mssql/backup/MyDB.bak'
  WITH FORMAT, COMPRESSION, STATS = 10;

RESTORE DATABASE MyDB
  FROM DISK = '/var/opt/mssql/backup/MyDB.bak'
  WITH MOVE 'MyDB'     TO '/var/opt/mssql/data/MyDB.mdf',
       MOVE 'MyDB_log' TO '/var/opt/mssql/log/MyDB_log.ldf',
       RECOVERY;
# Shell-level backup via sqlcmd
sqlcmd -S localhost -U SA -P "$SA_PASSWORD" -Q \
  "BACKUP DATABASE [MyDB] TO DISK='/var/opt/mssql/backup/MyDB_$(date +%Y%m%d).bak'
   WITH FORMAT, COMPRESSION"

# Copy backup out of container
docker cp sql1:/var/opt/mssql/backup/MyDB.bak ./MyDB.bak

# Copy backup into container
docker cp ./MyDB.bak sql1:/var/opt/mssql/backup/MyDB.bak

[!NOTE] SQL Server 2022 — S3-Compatible Backup S3-compatible backup works identically on Linux and Windows:

BACKUP DATABASE MyDB
  TO URL = 's3://bucket/path/MyDB.bak'
  WITH FORMAT, COMPRESSION;

See references/44-backup-restore.md for full S3 backup coverage.

Cross-Platform Restore Considerations

ScenarioSupportedNotes
Windows backup → Linux restore✓ YesFull support for SQL Server 2017+
Linux backup → Windows restore✓ YesFull support
Cross-version restorePartialCan only restore to same or newer version
FileStream database✗ NoFileStream not supported on Linux

Common Patterns

Pattern 1: CI/CD Database Deployment with Docker

#!/bin/bash
# deploy-test.sh — spin up SQL Server, run migrations, run tests, tear down

set -e

SA_PASSWORD="TestP@ssw0rd$(date +%s)"

# Start SQL Server
docker run -d --name ci-sql \
  -e ACCEPT_EULA=Y \
  -e MSSQL_SA_PASSWORD="$SA_PASSWORD" \
  -e MSSQL_PID=Developer \
  -p 1433:1433 \
  mcr.microsoft.com/mssql/server:2022-latest

# Wait for readiness
until docker exec ci-sql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U SA -P "$SA_PASSWORD" -Q "SELECT 1" -b -o /dev/null 2>/dev/null; do
  sleep 2
done

# Run migrations
sqlpackage /Action:Publish \
  /SourceFile:./database.dacpac \
  /TargetServerName:localhost \
  /TargetDatabaseName:AppDb \
  /TargetUser:SA \
  /TargetPassword:"$SA_PASSWORD"

# Run integration tests
dotnet test --filter Category=Integration

# Cleanup
docker rm -f ci-sql

Pattern 2: Scheduled Backup via Cron (No SQL Agent)

#!/bin/bash
# /usr/local/bin/sql-backup.sh
SA_PASSWORD=$(cat /etc/sqlsecret/sa-password)
BACKUP_DIR="/mnt/backup"
DB_LIST=$(sqlcmd -S localhost -U SA -P "$SA_PASSWORD" -h -1 -W \
  -Q "SET NOCOUNT ON; SELECT name FROM sys.databases WHERE database_id > 4")

for DB in $DB_LIST; do
  FILENAME="$BACKUP_DIR/${DB}_$(date +%Y%m%d_%H%M%S).bak"
  sqlcmd -S localhost -U SA -P "$SA_PASSWORD" -Q \
    "BACKUP DATABASE [$DB] TO DISK='$FILENAME' WITH FORMAT, COMPRESSION, STATS=25"
done

# Crontab: 2 AM daily
# 0 2 * * * /usr/local/bin/sql-backup.sh >> /var/log/sql-backup.log 2>&1

Pattern 3: Health Check Endpoint

#!/bin/bash
# health-check.sh — return 0 if SQL Server is healthy, 1 otherwise
/opt/mssql-tools/bin/sqlcmd \
  -S localhost -U SA -P "$MSSQL_SA_PASSWORD" \
  -Q "SELECT 1" -b -o /dev/null 2>/dev/null
exit $?

Pattern 4: mssql.conf Template for Production Linux

[EULA]
accepteula = Y

[memory]
memorylimitmb = 28672    # Leave ~4 GB for OS on 32 GB host

[network]
tcpport = 1433
forceencryption = 1
tlsprotocols = 1.2,1.3
tlscert = /etc/ssl/certs/mssql.pem
tlskey = /etc/ssl/private/mssql.key

[sqlagent]
enabled = true

[hadr]
hadrenabled = 0    # Set to 1 for AG members

[coredump]
coredumptype = filtered
captureminiandfull = true

[traceflag]
traceflag0 = 3226    # Suppress successful backup log messages

[filelocation]
defaultdatadir = /mnt/data
defaultlogdir = /mnt/log
defaultbackupdir = /mnt/backup

Gotchas

  1. latest image tag moves — always pin to a specific CU tag (e.g., 2022-CU12-ubuntu-22.04) in production. latest can unexpectedly pick up a new CU on docker pull.

  2. UID 10001 in containers — the mssql user inside the container is UID 10001. Host-mounted volumes must be owned by UID 10001 (chown -R 10001:0). Forgetting this is the #1 cause of "container exits immediately" issues.

  3. mssql-conf changes need restarts — most settings require sudo systemctl restart mssql-server. Editing /var/opt/mssql/mssql.conf directly has the same requirement.

  4. No named instances — connect by port, not HOST\INSTANCE. Use Server=host,port in connection strings.

  5. Case-sensitive filesystem — Linux filesystems (ext4, XFS) are case-sensitive. .sql script filenames, paths in T-SQL, and init script names must match exactly. Windows backup file extensions .BAK and .bak are different filenames on Linux.

  6. TLS by default in mssql-tools18sqlcmd from mssql-tools18 requires encryption. Self-signed certs need -C (trust server certificate) or configure a proper cert. Older scripts using mssql-tools (without 18) may not encrypt by default.

  7. FileStream databases cannot be restored on Linux — attempting to restore a FileStream-enabled database will fail. Must disable FileStream on Windows before migrating.

  8. MSDTC requires explicit configuration — distributed transactions across SQL Server on Linux require setting up MSDTC (available from SQL Server 2017 CU 16+). Not enabled or installed by default. 2

  9. SQL Server Browser not available — without SQL Server Browser, connections must specify the port explicitly. Dynamic port discovery (used by Windows clients when connecting to named instances) does not work.

  10. Pacemaker fencing is mandatory in production — running Pacemaker AGs without STONITH/fencing enabled (stonith-enabled=false) is only acceptable in test environments. In production, split-brain scenarios without fencing can result in data corruption or dual-primary situations.

  11. Container TZ affects GETDATE() — if the container timezone (TZ environment variable) differs from expectations, GETDATE() will return a different local time. Use GETUTCDATE() or SYSUTCDATETIME() for consistency, or set TZ=UTC on the container.

  12. Set memorylimitmb on shared hosts — without this setting, SQL Server claims as much memory as possible, crowding out the OS and other processes. Always set memory.memorylimitmb on Linux hosts that share memory with other processes.


See Also

  • references/43-high-availability.md — Always On AG architecture details
  • references/44-backup-restore.md — Backup/restore including S3 (2022+)
  • references/49-configuration-tuning.md — sp_configure and server tuning
  • references/47-cli-bulk-operations.md — sqlcmd, bcp, sqlpackage CLI tools
  • references/15-principals-permissions.md — logins, users, permissions
  • references/16-security-encryption.md — TLS/TDE/Always Encrypted

Sources

Footnotes

  1. What's New for SQL Server 2022 on Linux — RHEL 9 and Ubuntu 22.04 support (CU 10+), SLES 15 SP4 support (CU 4+), and other SQL Server 2022 Linux-specific updates

  2. How to Configure MSDTC on Linux — MSDTC configuration on Linux including RPC endpoint mapping, firewall rules, and port routing; supported from SQL Server 2017 CU 16+ 2

  3. Availability Groups for SQL Server on Linux — overview of Always On availability group characteristics and differences between Linux (Pacemaker) and Windows (WSFC) deployments

  4. Create a SQL Managed Instance Enabled by Azure Arc — deploying Arc-enabled SQL Managed Instance on Kubernetes using Azure CLI and arcdata extension

references

01-syntax-ddl.md

02-syntax-dql.md

03-syntax-dml.md

04-ctes.md

05-views.md

06-stored-procedures.md

07-functions.md

08-indexes.md

09-columnstore-indexes.md

10-partitioning.md

11-custom-data-types.md

12-custom-defaults-rules.md

13-transactions-locking.md

14-error-handling.md

15-principals-permissions.md

16-security-encryption.md

17-temporal-tables.md

18-in-memory-oltp.md

19-json-xml.md

20-full-text-search.md

21-graph-tables.md

22-ledger-tables.md

23-dynamic-sql.md

24-string-date-math-functions.md

25-null-handling.md

26-collation.md

27-cursors.md

28-statistics.md

29-query-plans.md

30-query-store.md

31-intelligent-query-processing.md

32-performance-diagnostics.md

33-extended-events.md

34-tempdb.md

35-dbcc-commands.md

36-data-compression.md

37-change-tracking-cdc.md

38-auditing.md

39-triggers.md

40-service-broker-queuing.md

41-replication.md

42-database-snapshots.md

43-high-availability.md

44-backup-restore.md

45-linked-servers.md

46-polybase-external-tables.md

47-cli-bulk-operations.md

48-database-mail.md

49-configuration-tuning.md

50-sql-server-agent.md

51-2022-features.md

52-2025-features.md

53-migration-compatibility.md

54-linux-containers.md

SKILL.md

tile.json