Releases: heyvaldemar/mssql-server-traefik-letsencrypt-docker-compose
Release list
v1.6.2
Fixed
- CI had never run the restore script. The test restored with its own
copy of the commands. The script carried the backup directory and name
prefix as literals, found its container with a name filter that misses it
under any-pbutmssql, and could only be run by hand. It now reads both
from the running backups container, finds the container by its compose
labels, accepts the file name as an argument, and CI runs it: a row added
after a backup must be gone once that backup is restored.
Changed
- The README's Testing section describes the workflows as they are. It still listed the freshness check inside Deployment Verification after that check moved to its own workflow.
- The freshness check has its own workflow, Pin Freshness. It ran inside Deployment Verification, whose badge is the one at the top of this README. Across the fleet, nine red runs in ten were a pin one version behind - which the fleet's triage moves within the day - and a reader cannot tell that from a stack that does not boot. The badge now says whether the stack boots. The job itself is unchanged.
- Checked daily, as the security policy already said. This template's schedule was weekly while its SECURITY.md said the pins are re-resolved daily, and nothing recorded a reason for the difference. It now runs daily like the rest of the fleet.
v1.6.1
Security
traefik:3.7was rebuilt upstream; the pin moved fromsha256:f86a2cab1b5c…tosha256:24841fe2de73…. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.
Upgrading
git pull (or ./update.sh), then docker compose up -d. Containers on a refreshed image are recreated; data volumes and .env are untouched. This release was cut by fleet triage after the deploy job booted the stack on the refreshed images.
Full history in CHANGELOG.md.
v1.6.0
Changed
-
SQL Server 2022 CU27. A cumulative update, build 16.0.4295.3, KB5104824,
released 15 September 2026. It replaces CU26 from 16 July.Cumulative updates carry every fix from the ones before them, so this is one
hop from CU26 and not a chain. The upstream review could not read notes for
the range and said so rather than guessing; the build number and date above
come from Microsoft's own version-history page, and the deploy job started
the engine on this image and answered before it landed.A database engine update is still a database engine update: take a backup
before pulling it on a live deployment.
Upgrading
git pull (or ./update.sh), then docker compose up -d. A database engine update is still a database engine update: take a backup before pulling it on a live deployment. .env and the data volume are untouched.
Cut after the deploy job started the engine on this image and answered through Traefik on the pull request, and after the build number, KB and release date were checked against Microsoft's own version history.
Full history in CHANGELOG.md.
v1.5.1
Changed
update.shnames any new required variable before it moves. An update can add a required variable;docker compose upused to stop on it after the checkout, with the tree already on the new tag. The script now lists the variables that appeared in.env.examplesince your version and refuses, before anything has moved, when a required one is not in your.env. Names only, never values.
Upgrading
git pull (or ./update.sh). Nothing running changes: this release adds or extends the update script and touches no image pin.
Full history in CHANGELOG.md.
v1.5.0
Added
- Per-image version overrides. Every pin in the
x-imagesblock is
now${<PREFIX>_IMAGE_TAG:-repo:${<PREFIX>_IMAGE_VERSION:-tag@sha256:digest}}.
Set<PREFIX>_IMAGE_VERSIONin.envto run a different version of one
image while every other pin stays as tested (Compose pulls that tag
without a digest), or<PREFIX>_IMAGE_TAGto replace the whole
reference as before. A deployment that sets neither is unchanged. The
freshness job, the Trivy matrix and the fleet digest automation resolve
the nested default before reading a pin. Needs Docker Compose v2.5 or
newer (2022): v2.0 to v2.4 leave the inner${...}unexpanded and
docker compose upfails with an invalid reference instead of
deploying something unexpected.
Upgrading
git pull (or ./update.sh). A deployment that sets no _IMAGE_VERSION or _IMAGE_TAG variable renders exactly the same image references as before, so docker compose up -d recreates nothing. Requires Docker Compose v2.5 or newer.
Full history in CHANGELOG.md.
v1.4.0 — SQL Server backups proven restorable
Added
tests/e2e-backup-restore.sh — six scenarios against the live stack, run by CI on every push, on a database the tests create themselves (e2e_test): the required-variable guard fires, a backup of that database is produced, RESTORE VERIFYONLY passes on it, a SQL Server outage is reported as FAILED, restore genuinely replaces database state (RESTORE ... WITH REPLACE of the baseline removes a table created after it), and pruning removes only old files.
Fixed
The backup loop now logs Database backup FAILED when it cannot list the databases (SQL Server unreachable) instead of silently skipping the cycle.
Upgrading
git pull and docker compose up -d (only the backups container is recreated). Run the tests on a staging copy: the failure scenario stops SQL Server briefly.
Full details in CHANGELOG.md.
Security
Container hardening. Every service runs with security_opt: no-new-privileges:true. The Traefik and backups containers drop every Linux capability and add back only what their entrypoints need. The SQL Server container keeps the default capability set on purpose: the upstream image assumes it. CI boots the stack under these settings on every push.
v1.3.0 — verified SQL Server backups
Added
A backups service. This template persisted /var/opt/mssql and called that a day. A sidecar from the same image now runs BACKUP DATABASE ... WITH CHECKSUM for master, msdb, and every online user database on a loop, verifies each file with RESTORE VERIFYONLY, logs Database backup OK: <file> (<bytes> bytes) or FAILED per database (a failed file is kept as <file>.failed), and prunes only its own files. Backups land in the mssql-server-backups volume, shared with the server. Schedule knobs have defaults listed in .env.example. No COMPRESSION, which Express does not support.
mssql-restore-database.sh — interactive restore of a user database from a selected .bak (single-user mode, RESTORE ... WITH REPLACE, back to multi-user). System databases are refused with a pointer to Microsoft's procedure.
CI waits for the first backup cycle and runs RESTORE VERIFYONLY itself on the newest file.
Upgrading
git pull and docker compose up -d. The server gains a new volume mount (/var/opt/mssql/backup) and is recreated; data in mssql-server-data is untouched.
Full details in CHANGELOG.md.
v1.2.0 — resource limits you can tune from .env
Added
Resource limits on every service, as .env-overridable defaults. Each service now carries memory and CPU limits plus reservations (<SERVICE>_MEMORY_LIMIT, _CPU_LIMIT, _MEMORY_RESERVATION, _CPU_RESERVATION; the knobs and their defaults are listed in .env.example). Set any of them in .env and the override survives every git pull.
The defaults are what CI boots the stack under, so they are known to be enough for a fresh install. Under real load, a service that is OOM-killed shows OOMKilled=true in docker inspect — raise its _MEMORY_LIMIT and recreate.
Upgrading
git pull and docker compose up -d (containers are recreated with the new limits). If you already run on a small host and a service gets killed, set a higher limit in .env before recreating.
Full details in CHANGELOG.md.
v1.1.0 — unattended updates with update.sh
Added
update.sh — unattended updates to the newest tagged release, and nothing else. A tag is cut only after CI has booted the pinned images and passed the smoke tests, so "update to the latest tag" means "update to a combination a machine has already run" — the guarantee a floating latest can never give.
- refuses to cross a major version on its own (
--allow-majorafter you read the notes) - refuses a checkout with local modifications (your customization belongs in
.env, which updates never touch) --dry-runshows what would be applied
Put it on a timer for hands-off minor/patch updates:
17 5 * * * /opt/mssql-server-traefik-letsencrypt-docker-compose/update.sh >> /var/log/mssql-server-update.log 2>&1
It is deliberately a host-side script and not a container in the stack: an in-stack updater needs the Docker socket and turns "someone pushed to a repo" into "someone deployed to your machine" with no operator in the loop.
Full details in CHANGELOG.md.
v1.0.0 — fleet standard: routing and healthcheck fixed, digest pins, CI-verified deploy
First semver release, bringing this template to the fleet standard.
Fixed — the shipped configuration could not work as promised
- The Traefik TCP router pointed at port 11434 instead of 1433, so every connection through the published entrypoint went nowhere. CI now proves the routed path by running a query through the Traefik entrypoint on every push.
- The healthcheck could never succeed: it referenced
SQLSERVER_SA_PASSWORD(a variable that exists nowhere — the real one isMSSQL_SA_PASSWORD) and called/opt/mssql-tools/bin/sqlcmd, a path current images do not ship. It now uses/opt/mssql-tools18/bin/sqlcmd -Cand reads the password from the container environment at runtime.
Security — rotate if you deployed before this release
The previously tracked .env carried a literal sa password, and it remains in git history. If you deployed with it:
ALTER LOGIN sa WITH PASSWORD = '<new strong password>';then update .env and docker compose up -d --force-recreate.
Changed
- SQL Server 2022-CU26 (was 2022-CU10) and Traefik v3.7 (was 3.2), pinned by
tag@sha256:digestin the composex-imagesblock —git pullalone delivers the tested combination. - The freshness gate tracks cumulative updates within the 2022 line only: moving to SQL Server 2025 upgrades data files one-way on first attach, so that jump is reserved for a major release with explicit upgrade notes.
- Required variables fail fast with
${VAR:?…}guards;.envis no longer tracked (copy.env.example).
Upgrading from an earlier checkout
git pull, create .env from .env.example (rotate the sa password per above), then:
docker compose -f mssql-server-traefik-letsencrypt-docker-compose.yml -p mssql up -d --force-recreate
Your data in the mssql-server-data volume is untouched; 2022-CU10 → 2022-CU26 is an in-line cumulative update.
Full details in CHANGELOG.md.