Local development lab for SQL Server 2025 Express calling Ollama over HTTPS through an internal nginx reverse proxy.
The stack is designed for DP-800 preparation on Windows 10/11 with WSL 2, Docker Desktop, and a NVIDIA GPU, but it is started with plain Docker Compose so the runtime commands are portable.
SQL client on host
-> localhost,1433
-> sql-server
SQL Server engine inside container
-> https://localhost:11434/*
-> nginx TLS proxy
-> http://localhost:11435/*
-> Ollama
The containers share the sql-server network namespace:
sql-serverpublishes only1433to the host.nginxlistens internally onhttps://localhost:11434.ollamalistens internally onhttp://localhost:11435.- nginx transparently proxies every path to Ollama.
This means SQL Server calls the normal Ollama API through HTTPS:
https://localhost:11434/api/tags
https://localhost:11434/api/embed
https://localhost:11434/v1/completions
.env.example
certs/
docker-compose.yml
nginx/nginx.conf
sql/
sqlserver.Dockerfile
The certificates under certs/ are part of the lab and are expected to be
present in the repository.
The lab was tested on this machine. These values are not minimum requirements; they are provided as a practical reference point.
Computer: HP ZBook
OS: Windows 10 22H2, build 19045.7417, 64-bit
CPU: 11th Gen Intel Core i7-11800H @ 2.30 GHz
RAM: 32 GB
GPU: NVIDIA T1200 Laptop GPU
VRAM: 4 GB
NVIDIA driver: 596.72 Quadro RTX Desktop/Notebook DCH WHQL
Docker Desktop: 4.79.0
WSL: WSL 2, Ubuntu 24.04.4 LTS
SQL Server image: mcr.microsoft.com/mssql/server:2025-latest
SQL Server version: Microsoft SQL Server 2025 (RTM-CU6) 17.0.4055.5, Express Edition, Linux
Default models: nomic-embed-text, gemma3:1b
Measured Docker disk usage with the default models:
Docker images:
sql-server-2025-express: 2.52 GB
ollama/ollama: 8.03 GB
nginx:1.27-alpine: 74.5 MB
Approximate lab image total: 10.6 GB
Docker volumes:
dp-800-sql-server-data: 74.5 MB
dp-800-ollama-data: 1.0 GB
Approximate lab volume total: 1.1 GB
Approximate lab total: 11.7 GB
Docker build cache, unrelated images, unrelated volumes, and Docker Desktop internal services are not included in the approximate lab total.
The commands in this section target Ubuntu/Debian WSL distributions, which are the recommended option for this lab. If you use a different WSL distribution, follow the official NVIDIA Container Toolkit instructions for that platform.
Install a recent NVIDIA Windows driver with WSL support. The CUDA driver layer for WSL comes from Windows; do not install a Linux NVIDIA kernel driver inside WSL.
Validate the GPU from Windows:
nvidia-smiValidate WSL and make sure the Docker-integrated distribution is WSL 2:
wsl --version
wsl --status
wsl -l -vIf needed:
wsl --set-version Ubuntu 2
wsl --updateBefore configuring Docker Desktop, open the Ubuntu/Debian WSL distribution and install NVIDIA Container Toolkit there.
Configure the NVIDIA apt repository in WSL:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.listInstall the toolkit in WSL:
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkitThen configure Docker Desktop, not the WSL Docker daemon. For Docker Desktop on
Windows, do not use nvidia-ctk runtime configure inside WSL to configure the
runtime; update Docker Desktop's Windows daemon configuration manually instead.
- Use the WSL 2 based engine.
- Enable WSL integration for the Ubuntu/Debian distribution.
- Use Linux containers.
Update Docker Desktop's Windows daemon runtime file manually:
%USERPROFILE%\.docker\windows-daemon.json
Example content:
{
"experimental": false,
"runtimes": {
"nvidia": {
"args": null,
"path": "nvidia-container-runtime"
}
}
}Restart WSL and Docker Desktop:
wsl --shutdownThen quit and reopen Docker Desktop.
Validate Docker and GPU access:
docker version
docker run --rm hello-world
docker run --rm --gpus=all nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smiInstall the NVIDIA driver for your Linux distribution first. Then install NVIDIA Container Toolkit.
Configure the NVIDIA apt repository:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.listInstall the toolkit:
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkitConfigure the Docker runtime:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart dockerIf your Linux environment does not use systemd, restart Docker with the
service command available in your distribution:
sudo service docker restartValidate Docker GPU access:
docker version
docker run --rm hello-world
docker run --rm --gpus=all nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smiFor distributions other than Ubuntu or Debian, follow the official NVIDIA Container Toolkit guide for your platform.
References:
- NVIDIA CUDA on WSL User Guide
- NVIDIA Container Toolkit Install Guide
- Docker Desktop settings and daemon configuration
Create .env from the example:
cp .env.example .envOn PowerShell:
Copy-Item .env.example .envEdit .env and set a strong SQL Server password:
MSSQL_SA_PASSWORD=Change_this_Strong_Password_123!
OLLAMA_MODELS=nomic-embed-text gemma3:1bOLLAMA_MODELS is used by the ollama-models one-shot Compose service to pull
the models into the shared Ollama volume.
From this directory:
docker compose up -d --buildWatch model downloads:
docker logs -f ollama-modelsCheck containers:
docker psExpected host port exposure:
sql-server 0.0.0.0:1433->1433/tcp
nginx no host port
ollama no host port
Stop the stack without deleting SQL data or downloaded models:
docker compose downStop the stack and delete SQL Server and Ollama volumes:
docker compose down --volumesValidate nginx -> Ollama from inside the SQL Server container:
docker exec sql-server curl -v https://localhost:11434/api/tagsExpected result:
HTTP/1.1 200 OK
At this point, nginx logs should show curl as the caller:
docker logs nginxExample:
127.0.0.1 - - "GET /api/tags HTTP/1.1" 200 ... "curl/8.5.0"
After running the T-SQL validation in sql/00-setup-database-and-test-tags.sql,
nginx logs should show SQL Server as the caller:
127.0.0.1 - - "GET /api/tags HTTP/1.1" 200 ... "Express Edition (64-bit)/17.0.4055.5"
Connect from SSMS, Azure Data Studio, or sqlcmd:
Server: localhost,1433
Login: sa
Password: value from MSSQL_SA_PASSWORD
Trust server certificate: enabled if your SQL client requires it
Run the scripts under sql/ in order:
sql/00-setup-database-and-test-tags.sql
sql/01-completions.sql
sql/02-embeddings.sql
sql/03-chunking-with-embeddings.sql
sql/04-vector-distance-search.sql
sql/05-vector-table-diskann-index.sql
sql/06-rag-with-vector-context.sql
The flow is:
00creates the[DP-800]database, enablessp_invoke_external_rest_endpoint, enables SQL Server 2025 preview features for vector indexes, creates the database master key if needed, and validateshttps://localhost:11434/api/tags.01calls Ollama's OpenAI-compatible completions endpoint.02creates theOllamaNomicEmbeddingsexternal model and runsAI_GENERATE_EMBEDDINGS.03createsdbo.DocumentChunks, runsAI_GENERATE_CHUNKS, and stores chunk embeddings.04runs exact semantic search withVECTOR_DISTANCE.05createsdbo.VectorDocuments, loads 100 lab rows from the chunk table, builds a DiskANN vector index, and compares exact KNN with ANN search.06runs a local RAG flow: retrieve relevant chunks from SQL Server, build a grounded prompt, and call Ollama completions.
DiskANN vector indexes require at least 100 rows with non-null vectors. The
05 script expands the small demo chunk table into 100 lab rows before creating
the index.
Compatibility note for ANN syntax: current documentation may mention
SELECT TOP (...) WITH APPROXIMATE for newer vector index syntax. On the tested
SQL Server 2025 RTM-CU6 build, that syntax fails with:
Incorrect syntax near 'APPROXIMATE'.
For this build, use the earlier VECTOR_SEARCH syntax with TOP_N:
FROM VECTOR_SEARCH
(
TABLE = dbo.VectorDocuments AS v,
COLUMN = embedding,
SIMILAR_TO = @question_embedding,
METRIC = 'cosine',
TOP_N = 3
) AS sConceptually, both forms request an approximate nearest-neighbor search. The lab
uses TOP_N because it is the syntax supported by the tested SQL Server 2025
container image.
If you run the lab with a non-admin SQL login, grant permission in the database where you run the lab:
GRANT EXECUTE ANY EXTERNAL ENDPOINT TO [USER];
GOThe first call to an embeddings or completions endpoint can take noticeably longer than later calls. Ollama may need to load the selected model into memory, initialize GPU/CPU execution, and perform the first inference pass.
The delay depends on the host hardware, available GPU memory, current system load, and the selected model size. Embeddings models usually warm up faster; completions models can take a few minutes on the first request, especially on laptops or GPUs with limited VRAM.
If the first request is slow but nginx logs show activity and the container is healthy, wait before assuming the request failed. Subsequent calls should be much faster while the model remains loaded.
The lab uses a local self-signed CA and localhost server certificate committed
under certs/.
nginx uses:
certs/localhost.pem
certs/localhost.key
The SQL Server image installs:
certs/localhost.ca.crt
certs/localhost.crt
into the Linux trust store and also copies them to:
/var/opt/mssql/security/ca-certificates
owned by mssql:mssql.
Important note for SQL Server on Linux: installing the CA only in the standard
Linux trust store is not enough for SQL Server's internal HTTP/REST clients.
The image still adds the certificates to the Linux store with
/usr/local/share/ca-certificates and update-ca-certificates, but SQL Server
uses SQLPAL for certificate validation.
For SQLPAL, additional trusted CAs must be available under:
/var/opt/mssql/security/ca-certificates
SQL Server reads certificates from that directory during startup and adds them to the PAL trust store. Microsoft documents this behavior for SQL Server on Linux and also documents a limit of 50 files in that location.
The certificates are for local lab use only.
Compose project: dp-800
Image: sql-server-2025-express
Container: sql-server
Container: nginx
Container: ollama
Container: ollama-models
Network: dp-800-network
Volume: dp-800-sql-server-data
Volume: dp-800-ollama-data
Check model pull logs:
docker logs ollama-modelsCheck nginx logs:
docker logs nginxCheck Ollama logs:
docker logs ollamaCheck SQL Server logs:
docker logs sql-serverCheck GPU use:
docker exec ollama nvidia-smiIf the CUDA test hangs or leaves a container in Created:
docker ps -a
docker rm <container-id>If port 1433 is already in use, change the host port:
ports:
- "51433:1433"Then connect to:
localhost,51433
If external REST endpoints are disabled, SQL Server can return errors like:
'sp_invoke_external_rest_endpoint' is disabled on this instance of SQL Server.
Use sp_configure 'external rest endpoint enabled' to enable it.
or:
'ai_generate_embeddings' is disabled on this instance of SQL Server.
Use sp_configure 'external rest endpoint enabled' to enable it.
Enable it with the SQL setup commands from sql/00-setup-database-and-test-tags.sql.
This lab is local-only:
- SQL Server is the only published service.
- nginx and Ollama are not published to the host.
- nginx terminates HTTPS on
localhost:11434inside the shared container namespace. - Ollama stays behind nginx on
localhost:11435. - Certificates are self-signed and intended only for this local lab.
- Do not reuse this certificate material in production.