Skip to main content
Version: v1.0.76.0

Ultimate Guide to SQL Server Backup in Kubernetes Using NFS Volumes

This guide explains how to create backups of a SQL Server Availability Group using the DxEnterpriseSqlAg custom resource. The recommended method is to attach a volume to the SQL Server container and direct SQL Server backups to write data to this volume.

Overview​

SQL Server supports writing database backups to any mounted filesystem path accessible inside the container. In Kubernetes, this is typically exposed via a volumeMount backed by an NFS share (or any other supported PV type).

DH2i recommends mounting an NFS export to /var/opt/mssql/backup, which is the SQL Server standard backup directory on Linux.

Prerequisites​

NFS limitations with Minikube's WSL2 driver

Minikube running with the WSL2 driver has known limitations with NFS-backed Kubernetes volumes:

  • The kubelet may present a different source IP than the Minikube VM.
  • WSL2 networking often rewrites ephemeral ports, causing:
    • mount.nfs: access denied by server
    • rpc.mountd: illegal port / unmatched host
  • Kubernetes mounts occur from the kubelet namespace, not the interactive minikube ssh session.

This can result in failed NFS volume mounts even when manual tests appear successful.

Use the Hyper-V driver instead:

  minikube start --driver=hyperv

Configure the NFS Server​

The following example demonstrates how to export /exports/sqlbackups from an RHEL or Ubuntu-based NFS server for use by Kubernetes.

  1. Install and enable NFS services
sudo dnf install -y nfs-utils
sudo systemctl enable --now nfs-server
  1. Create the export directory
sudo mkdir -p /exports/sqlbackups
sudo chmod 0777 /exports/sqlbackups
tip

In production, consider using a dedicated service account and more restrictive permissions. For testing and lab environments, 0777 is often acceptable.

  1. Configure /etc/exports

Configure an NFS export rule similar to the following to make the backup directory available to your Kubernetes nodes:

/exports/sqlbackups *(rw,sync,no_root_squash,no_subtree_check,insecure)
info

Explanation of options:

  • rw - allow read/write access.
  • sync - commit writes to disk before replying (safer for database backups).
  • no_root_squash - preserve root privileges from the client (required for many containerized SQL Server workloads).
  • no_subtree_check - improves reliability when exporting subdirectories.
  • insecure - allow mounts from high source ports; often required for Kubernetes nodes and Minikube.
  1. Apply the NFS export rule:
sudo exportfs -ra
sudo exportfs -v
  1. Open firewall ports for nfs, mountd, and rpc-bind.
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --permanent --add-service=mountd
sudo firewall-cmd --permanent --add-service=rpc-bind
sudo firewall-cmd --reload
  1. Enable read/write access to all exported NFS file systems if SELinux is in enforcing mode
sudo setsebool -P nfs_export_all_rw 1

Configure the DxEnterpriseSqlAg Custom Resource​

The following example mounts an NFS share exposed at /exports/sqlbackups to /var/opt/mssql/backup in each SQL Server pod.

---
apiVersion: dh2i.com/v1
kind: DxEnterpriseSqlAg
metadata:
name: dxesqlag
spec:
synchronousReplicas: 3
asynchronousReplicas: 0
configurationOnlyReplicas: 0
availabilityGroupListenerPort: 14033
availabilityGroupName: AG1
availabilityGroupOptions: null
availabilityGroupClusterType: EXTERNAL
createLoadBalancers: true
template:
metadata:
labels:
label: example
annotations:
annotation: example
spec:
# --- Add the NFS volume definition here ---
volumes:
- name: nfs-backup-volume
nfs:
server: 10.1.100.62
path: /exports/sqlbackups
dxEnterpriseContainer:
image: "docker.io/dh2i/dxe:latest"
imagePullPolicy: Always
acceptEula: true
clusterSecret: dxe
vhostName: VHOST1
joinExistingCluster: false
volumeClaimConfiguration:
storageClassName: null
resources:
requests:
storage: 1Gi
mssqlServerContainer:
image: "mcr.microsoft.com/mssql/server:latest"
imagePullPolicy: Always
mssqlSecret: mssql
acceptEula: true
mssqlPID: Developer
mssqlConfigMap: null
volumeClaimConfiguration:
storageClassName: null
resources:
requests:
storage: 2Gi
# --- Mount the NFS share into the SQL Server container ---
volumeMounts:
- name: nfs-backup-volume
mountPath: /var/opt/mssql/backup

Key points:

  • volumes[].nfs.server is the IP or DNS name of your NFS server.
  • volumes[].nfs.path is the exported directory on the NFS server.
  • mssqlServerContainer.volumeMounts[].mountPath is the path used for SQL Server backups inside the container.

Verify the NFS Backup Path Inside the SQL Container​

  1. After applying the DxEnterpriseSqlAg manifest and waiting for all pods to reach 2/2 Running:
kubectl get pods

Example:

NAME         READY   STATUS    RESTARTS   AGE
dxesqlag-0 2/2 Running 0 10m
dxesqlag-1 2/2 Running 0 10m
dxesqlag-2 2/2 Running 0 10m
  1. Check the mount inside the SQL Server container:
kubectl exec -it dxesqlag-0 -c mssql -- df -h /var/opt/mssql/backup

You should see the NFS export mounted:

Filesystem                        Size  Used Avail Use% Mounted on
10.1.100.62:/exports/sqlbackups 50G 6.4G 44G 13% /var/opt/mssql/backup
  1. Test write access from within the container:
kubectl exec -it dxesqlag-0 -c mssql -- bash -c "echo 'nfs-test' > /var/opt/mssql/backup/container-test.txt"

On the NFS server:

ls -l /exports/sqlbackups/container-test.txt

Performing a SQL backup to NFS​

Once the NFS mount is verified, you can back up databases from inside the SQL container directly to the mounted path.

Example using sqlcmd:

kubectl exec -it dxesqlag-0 -c mssql -- /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P '<YourSAPassword>' \
-Q "BACKUP DATABASE [master] TO DISK='/var/opt/mssql/backup/master_test.bak' WITH INIT"

On the NFS server:

ls -lh /exports/sqlbackups/master_test.bak

You should see a .bak file at the expected location.

References​