MSSQL Custom Resource Reference
The operator exposes two deployment CRDs that share a common spec:
MSSQLInstance— one or more independent standalone SQL Server instances.MSSQLAvailabilityGroup— an Always-On Availability Group across replicas. A dedicated CRD, decoupled fromMSSQLInstancewith noenabledtoggle: it is always an AG, adds anavailabilityGroupsection, and requiressize >= 2.
Three additional CRDs manage backup and restore operations against those
deployments — MSSQLBackup, MSSQLBackupSchedule, and MSSQLRestore. See
backups.md.
Minimal example (MSSQLInstance)
apiVersion: mssql.solanica.io/v1alpha1
kind: MSSQLInstance
metadata:
name: my-mssql
spec:
size: 1
acceptEULA: true
saPasswordSecret: my-mssql-secret
storage:
size: 8Gi
Full example (MSSQLAvailabilityGroup)
apiVersion: mssql.solanica.io/v1alpha1
kind: MSSQLAvailabilityGroup
metadata:
name: my-mssql
spec:
size: 3
acceptEULA: true
saPasswordSecret: my-mssql-secret
# image: mcr.microsoft.com/mssql/server:2025-latest
# port: 1433
# podManagementPolicy: Parallel
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "2"
memory: 2Gi
storage:
size: 8Gi
# storageClassName: standard
# --- OR use separate volumes (immutable after creation) ---
# volumes:
# data:
# storageClassName: fast-ssd
# resources:
# requests:
# storage: 50Gi
# log:
# storageClassName: fast-ssd
# resources:
# requests:
# storage: 20Gi
# tempdb:
# emptyDir:
# sizeLimit: 10Gi
expose:
enabled: true
type: ClusterIP
# type: LoadBalancer
# annotations:
# service.beta.kubernetes.io/aws-load-balancer-type: nlb
# loadBalancerSourceRanges:
# - 10.0.0.0/8
tls:
enabled: true
forceEncryption: true
# certificateSecret: my-tls-certs
availabilityGroup:
name: mssql-ag
availabilityMode: synchronous
# endpointPort: 5022
# databases:
# - mydb
The referenced SA password Secret must contain MSSQL_SA_PASSWORD:
apiVersion: v1
kind: Secret
metadata:
name: my-mssql-secret
type: Opaque
stringData:
MSSQL_SA_PASSWORD: "YourStrong@Passw0rd"
Note: SQL Server enforces a strong password policy — at least 8 characters including uppercase, lowercase, digit, and symbol.
Spec fields
Core
| Field | Type | Default | Description |
|---|---|---|---|
size | int | 1 | Number of SQL Server instances (StatefulSet replicas) |
paused | bool | false | Suspend the cluster by scaling the StatefulSet to 0 replicas while keeping all other resources (PVCs, Secrets, ConfigMaps, Services). Set back to false to resume at size. See paused (stop / start) |
acceptEULA | bool | — | Required. Must be true to accept the SQL Server EULA |
saPasswordSecret | string | — | Required. Name of a Secret containing key MSSQL_SA_PASSWORD |
image | string | mcr.microsoft.com/mssql/server:2025-latest | SQL Server container image |
imagePullSecrets | []LocalObjectReference | — | Secret names used to pull images from a private/air-gapped registry. Applies to every operator-managed container (SQL Server, monitoring exporter, AG sidecar). See private / air-gapped registries |
imagePullPolicy | string | Kubernetes default | Pull policy for operator-managed containers: Always, IfNotPresent, or Never. When unset, Kubernetes defaults apply (Always for the :latest tag, IfNotPresent otherwise) |
edition | string | Developer | SQL Server edition (MSSQL_PID): Developer, Express, Standard, Enterprise, or EnterpriseCore. Developer and Express are free. Express cannot be used with MSSQLAvailabilityGroup (no Always-On) and lacks SQL Server Agent and backup compression |
port | int | 1433 | SQL Server listen port |
podManagementPolicy | string | Parallel | StatefulSet pod management: Parallel or OrderedReady |
resources | ResourceRequirements | — | CPU/memory requests and limits |
storage.size | Quantity | — | PVC size per instance (single-PVC mode). Ignored when volumes is set |
storage.storageClassName | string | cluster default | Storage class (single-PVC mode). Ignored when volumes is set |
storage.accessModes | []string | [ReadWriteOnce] | PVC access modes (single-PVC mode). Ignored when volumes is set |
paused (stop / start)
spec.paused lets you stop a cluster without deleting it. When set to true the operator scales the underlying StatefulSet to 0 replicas, so every SQL Server pod is terminated and the compute is released. Everything else is preserved:
- PersistentVolumeClaims (your databases) — StatefulSets never delete PVCs on scale-down, so all data is retained.
- Secrets (SA password, TLS certificates), ConfigMaps (
mssql.conf, monitoring config). - Services (headless, exposed, and AG primary/replicas), so DNS names stay stable.
spec.size is not changed while paused — it is remembered so that clearing the flag restores the previous replica count.
spec:
size: 3
paused: true # scale to 0 pods, keep storage + config; resume later with paused: false
While paused:
status.stateispaused(thePausedcolumn showstrue); theAvailableandProgressingconditions report reasonPaused.- For
MSSQLAvailabilityGroup, AG reconciliation is skipped (no pods to talk to) and the last-known AG status snapshot is preserved. On resume, the operator re-establishes the Availability Group as the replicas come back.
To start the cluster again, set paused: false (or remove the field). The StatefulSet scales back to size on the same PersistentVolumeClaims.
edition and image (MSSQL_PID mechanics)
Microsoft ships one SQL Server container image (mcr.microsoft.com/mssql/server); the edition is selected at container start via the MSSQL_PID environment variable, not by using a different image. There is no separate "Standard image" or "Enterprise image".
The operator passes spec.edition straight through to MSSQL_PID (and always sets ACCEPT_EULA=Y and MSSQL_SA_PASSWORD from the referenced Secret). So a mixed fleet of editions runs from the same image tag — only spec.edition differs:
spec:
edition: Standard # -> MSSQL_PID=Standard
# image: mcr.microsoft.com/mssql/server:2025-latest # same image for every edition
spec.edition | MSSQL_PID | Licensed | Notes |
|---|---|---|---|
Developer (default) | Developer | Free (non-production) | Full Enterprise feature set; not for production use |
Express | Express | Free | Feature-limited: no Always-On AG, no SQL Server Agent, no backup compression; capped memory/CPU/DB size. Standalone MSSQLInstance only |
Standard | Standard | Paid | Supports Always-On AG |
Enterprise | Enterprise | Paid | Full feature set |
EnterpriseCore | EnterpriseCore | Paid | Enterprise, core-based licensing |
Only these named editions are accepted (CRD enum). Supplying a 25-character product key via MSSQL_PID is not currently supported; for the Linux container the edition name alone activates the edition, and licensing/compliance is your responsibility — the operator does not enforce or validate entitlements.
Edition-driven behavior the operator sets automatically:
MSSQL_AGENT_ENABLED=1on every edition except Express.MSSQL_ENABLE_HADR=1only forMSSQLAvailabilityGroupresources.Expressis rejected for AGs at admission.MSSQL_MEMORY_LIMIT_MBis capped to ~80% of the container memory limit (when a limit is set) so SQL Server respects the cgroup instead of the node's total RAM.
Version note: the default image is 2025-latest. You may pin an older tag via spec.image (e.g. 2022-latest or 2019-latest), but S3 backups (BACKUP ... TO URL with s3://) and Contained Availability Groups require SQL Server 2022 (16.x) or later. The image is Linux-only.
Private / air-gapped registries
Clusters without direct access to the public registries (mcr.microsoft.com, Docker Hub) can pull every image from a private or mirrored registry and authenticate with an image pull Secret.
1. Mirror the images into your registry and point each image field at it. The operator uses up to three images per SQL Server pod:
| Image | Field to override | Public default |
|---|---|---|
| SQL Server | spec.image | mcr.microsoft.com/mssql/server:2025-latest |
| Monitoring exporter | spec.monitoring.image | burningalchemist/sql_exporter:0.16.0 |
| AG sidecar (= operator image) | spec.availabilityGroup.sidecarImage, or the OPERATOR_IMAGE env var on the manager | operator image |
2. Create a pull Secret in the same namespace as the resource:
kubectl create secret docker-registry registry-creds \
--docker-server=registry.internal \
--docker-username=<user> \
--docker-password=<password> \
--namespace=<cr-namespace>
3. Reference it on the CR. imagePullSecrets is pod-level, so a single list covers all containers; imagePullPolicy is applied to the operator-managed containers (useful when nodes pre-load images):
spec:
image: registry.internal/mssql/server:2025-latest
imagePullSecrets:
- name: registry-creds
imagePullPolicy: IfNotPresent
monitoring:
image: registry.internal/burningalchemist/sql_exporter:0.16.0
The AG sidecar runs the operator image inside the SQL Server pod, so it is pulled using
spec.imagePullSecrets— no extra configuration is needed for it. The operator's own Deployment (the manager pod) must be given its pull Secret separately at install time (via your Kustomize/Helm values forconfig/manager), since that is deploy-time configuration rather than part of the CR.
storage.volumes
Optional. When set, the operator creates separate PVCs for data, log, and tempdb instead of a single PVC. Immutable after creation.
| Field | Type | Default | Description |
|---|---|---|---|
storage.volumes.data.storageClassName | string | cluster default | Storage class for data PVC |
storage.volumes.data.accessModes | []string | [ReadWriteOnce] | Access modes for data PVC |
storage.volumes.data.resources.requests.storage | Quantity | — | Required. Size of data PVC |
storage.volumes.log.storageClassName | string | cluster default | Storage class for log PVC |
storage.volumes.log.accessModes | []string | [ReadWriteOnce] | Access modes for log PVC |
storage.volumes.log.resources.requests.storage | Quantity | — | Required. Size of log PVC |
storage.volumes.tempdb.persistentVolumeClaim | VolumeSpec | — | PVC config for tempdb (mutually exclusive with emptyDir) |
storage.volumes.tempdb.emptyDir | EmptyDirVolumeSource | — | Ephemeral emptyDir for tempdb (mutually exclusive with persistentVolumeClaim) |
See storage.md for full details.
expose
| Field | Type | Default | Description |
|---|---|---|---|
expose.enabled | bool | false | Create an additional non-headless Service for client access |
expose.type | string | ClusterIP | Service type: ClusterIP, LoadBalancer, or NodePort |
expose.annotations | map | — | Annotations merged onto the exposed Service (e.g. load balancer provider config) |
expose.loadBalancerSourceRanges | []string | — | CIDR ranges allowed to reach a LoadBalancer Service |
tls
| Field | Type | Default | Description |
|---|---|---|---|
tls.enabled | *bool | true | Enable TLS encryption for client connections |
tls.forceEncryption | *bool | true | Require all clients to use TLS |
tls.certificateSecret | string | — | User-provided Secret with tls.crt and tls.key (optionally ca.crt) |
See tls.md for full details.
configuration
Declarative SQL Server startup configuration merged into the operator-managed
mssql.conf. Because these settings are read when the SQL Server process
starts, changing them triggers a rolling restart of the pods.
| Field | Type | Default | Description |
|---|---|---|---|
configuration.traceFlags | []int | — | SQL Server trace flag numbers enabled at startup, rendered into the [traceflag] section (e.g. 1117, 1118, 3226) |
configuration.mssqlConf | map[string]map[string]string | — | Free-form mssql.conf settings keyed by INI section then setting name. Merged with the operator's own sections |
spec:
configuration:
traceFlags:
- 1117
- 3226
mssqlConf:
memory:
memorylimitmb: "4096" # overrides the operator's automatic ~80% memory cap
sqlagent:
enabled: "true" # start SQL Server Agent
telemetry:
customerfeedback: "false"
Merge and precedence. The final mssql.conf is the union of the operator's
managed sections and your mssqlConf. You may add new sections ([sqlagent],
[tempdb], [telemetry], …) or additional keys to a section the operator also
writes. Section and setting names must be lowercase INI identifiers
([a-z0-9][a-z0-9._-]*) and values may not contain newlines.
Note:
mssql.conf[tempdb]settings such asdatafilecountanddatafilesizeare applied by SQL Server at first-time initialization, but the container image may auto-provision tempdb files based on the CPU count and override them. Trace flags and settings likesqlagent.enabledare honoured deterministically.
Reserved keys. Keys the operator owns are rejected by the admission webhook — configure them through their dedicated spec fields instead:
| Section / key | When reserved | Use instead |
|---|---|---|
[network] tlscert, tlskey, tlsprotocols, forceencryption | always | spec.tls |
[network] tcpport | always | spec.port |
[filelocation] defaultdatadir, defaultlogdir, defaultdumpdir, errorlogfile | when spec.storage.volumes is set | (managed automatically) |
[hadr] hadrenabled | on MSSQLAvailabilityGroup | (managed automatically) |
[traceflag] (whole section) | always | spec.configuration.traceFlags |
Note:
configuration.mssqlConf.memory.memorylimitmbis not reserved. Setting it takes precedence over the operator's automaticMSSQL_MEMORY_LIMIT_MBcap (~80% of the container memory limit), which is then omitted.
availabilityGroup
Only available on the MSSQLAvailabilityGroup CRD. Requires size >= 2.
| Field | Type | Default | Description |
|---|---|---|---|
availabilityGroup.name | string | <cr-name>-ag | AG name inside SQL Server. Immutable |
availabilityGroup.databases | []string | — | Databases to add to the AG (automatic seeding) |
availabilityGroup.availabilityMode | string | synchronous | synchronous (zero data loss) or asynchronous (faster, possible data loss). Immutable |
availabilityGroup.readableSecondaries | string | all | Secondary read access exposed via the <cr-name>-replicas Service: all, readOnly, or no |
availabilityGroup.contained | bool | false | SQL Server 2022 Contained AG (replicates master/msdb). Immutable |
availabilityGroup.endpointPort | int | 5022 | Database mirroring endpoint port. Immutable |
See availability-groups.md for full details, including failover tuning (allowDataLoss, lease timings), the health-check sidecar, and the preStop failover hook.
backup
Optional. Declares named S3-compatible backup destinations on an MSSQLInstance
or MSSQLAvailabilityGroup. Backups, schedules, and restores are then driven by
the separate MSSQLBackup, MSSQLBackupSchedule, and MSSQLRestore CRDs.
| Field | Type | Default | Description |
|---|---|---|---|
backup.engine | string | native | Backup engine. Only native (SQL Server BACKUP ... TO URL) is supported |
backup.targets | map | — | Named destinations. Each entry has type: s3, an s3 block (endpoint, bucket, prefix, region, urlStyle), a credentialSecretRef, and an optional caBundleSecretRef for self-signed TLS |
See backups.md for full details and CR examples.
Status fields
status:
ready: 3 # number of ready instances
size: 3 # desired size
state: ready # initializing | ready | error
message: "3/3 instances ready"
availabilityGroup: # only on MSSQLAvailabilityGroup
state: healthy # healthy | degraded | pending | configuring | error
primary: my-mssql-0.my-mssql.default.svc.cluster.local
synchronizedReplicas: 3
message: "3/3 replicas synchronized"