I was messing around performing investigative work on a pod running SQL Server 2025 in Kubernetes the other day and noticed something…the sqlservr process is no longer PID 1 in its container.
Instead there is: –
Hmm, ok we have a script /opt/mssql/bin/launch_sqlservr.sh and then the sqlservr binary is called.
I swear this wasn’t always the case, have I seen that before? Started to doubt myself so spun up a pod running an older version of SQL (2019 CU5) and took a look: –
Ahh ok, there has been a change. Now those two processes there are expected, one is essentially a watcher process and the other is sql server (full details here: –
I went and had a look at a 2022 image and that script is there as well…so there has been a change at some point to execute that script first in the container (not sure when and I’m not going back to check all the different images
)
Right, but what is that script doing?
Now this is a bit of a rabbit hole but from what I can work out, that script calls three other scripts: –
/opt/mssql/bin/permissions_check.sh
Checks the location and ownership of the master database.
/opt/mssql/bin/init_custom_setup.sh
Determines whether one-time SQL Server initialization should run on first startup.
/opt/mssql/bin/run_custom_setup.sh
If initialisation is enabled, wait for SQL Server to be ready, then use environment variables and the setup-scripts directory to perform a custom setup.
Oooooh, OK…custom setup available? Let’s have a look at that.
Essentially it comes down to whether or not SQL is spinning up for the first time (so we haven’t persisted data from one container to another) and if certain environment variables are set…these are: –
MSSQL_DB – used to create a database
MSSQL_USER – login/user for that database
MSSQL_PASSWORD – password for that login
MSSQL_SETUP_SCRIPTS_LOCATION – location for custom scripts
Nice…so let’s have a go at using those!
Here’s a SQL Server 2025 Kubernetes manifest using the first three: –
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mssql-statefulset-test
spec:
serviceName: "mssql"
replicas: 1
podManagementPolicy: Parallel
selector:
matchLabels:
name: mssql-pod
template:
metadata:
labels:
name: mssql-pod
spec:
securityContext:
fsGroup: 10001
containers:
- name: mssql-container-test
image: mcr.microsoft.com/mssql/server:2025-RTM-ubuntu-22.04
ports:
- containerPort: 1433
name: mssql-port
env:
- name: ACCEPT_EULA
value: "Y"
- name: MSSQL_SA_PASSWORD
value: "Testing1122"
- name: MSSQL_DB
value: "testdatabase"
- name: MSSQL_USER
value: "testuser"
- name: MSSQL_PASSWORD
value: "Testing112233"
Then if we look at the logs for SQL in that pod (I’ve stripped out the normal startup messages): –
Creating database testdatabase 2026-01-23 10:56:38.48 spid51 [DBMgr::FindFreeDatabaseID] Next available DbId EX locked: 5 2026-01-23 10:56:38.56 spid51 Starting up database 'testdatabase'. 2026-01-23 10:56:38.59 spid51 Parallel redo is started for database 'testdatabase' with worker pool size [2]. 2026-01-23 10:56:38.60 spid51 Parallel redo is shutdown for database 'testdatabase' with worker pool size [2]. Creating login testuser with password defined in MSSQL_PASSWORD environment variable Changed database context to 'testdatabase'.
There it is creating the database! Cool!
But what about the last environment variable, the custom scripts location?
From the startup scripts, this has a default value of /mssql-server-setup-scripts.d so let’s drop a script in there and see what happens.
To do this I created a simple T-SQL script to create a test database: –
CREATE DATABASE testdatabase2;
And then created a configmap in Kubernetes referencing that script: –
kubectl create configmap mssql-setup-scripts --from-file=./create-database.sql
Now we can reference that in our SQL manifest: –
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mssql-statefulset-test
spec:
serviceName: "mssql"
replicas: 1
podManagementPolicy: Parallel
selector:
matchLabels:
name: mssql-pod
template:
metadata:
labels:
name: mssql-pod
spec:
securityContext:
fsGroup: 10001
containers:
- name: mssql-container-test
image: mcr.microsoft.com/mssql/server:2025-RTM-ubuntu-22.04
ports:
- containerPort: 1433
name: mssql-port
env:
- name: ACCEPT_EULA
value: "Y"
- name: MSSQL_SA_PASSWORD
value: "Testing1122"
volumeMounts:
- name: setup-scripts
mountPath: /mssql-server-setup-scripts.d
readOnly: true
volumes:
- name: setup-scripts
configMap:
name: mssql-setup-scripts
And now we have these entries in the SQL startup log: –
Executing custom setup script /mssql-server-setup-scripts.d/create-database.sql 2026-01-23 11:08:52.08 spid60 Starting up database 'testdatabase2'.
Ha, and there’s our script being executed and the database created!
I had a look around and couldn’t see this documented anywhere (it may be somewhere though) but hey, another way of customising SQL Server in a container.
Although in reality I’d probably be using a custom image for SQL Server but this was fun to dive into 
Thanks for reading!

