Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- **An index pushed again over the same build is no longer reported as drift.** The daily pin check compared each pin with the digest the tag's index has now and failed on any difference, so a publisher re-pushing the index over the very same images (a platform added, an attestation rewritten) turned it red for an image nobody rebuilt, and no pin would ever move to turn it green. It now also compares the linux/amd64 build under both digests, the one CI runs and proves: the same build passes with a notice naming both digests, a new build fails as drift exactly as before, and a build that cannot be read still fails.

## [1.10.12] - 2026-11-07

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:933463a6c8eb…` to `sha256:f9a7b9ebf6f5…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.11] - 2026-11-06

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:92627e6a99f7…` to `sha256:f4bb143d1135…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.10] - 2026-11-06

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:68ae5fe5b64c…` to `sha256:92627e6a99f7…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.9] - 2026-11-06

### Changed

- **`mattermost/mattermost-team-edition:12.0.1` moved to `mattermost/mattermost-team-edition:12.0.2`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:5db6dac2ef5d…` to `sha256:933463a6c8eb…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.8] - 2026-11-04

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:e9338a01a433…` to `sha256:68ae5fe5b64c…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.7] - 2026-11-01

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:1beaa3084eee…` to `sha256:5db6dac2ef5d…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.6] - 2026-10-31

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:1336e85aaed2…` to `sha256:e9338a01a433…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.5] - 2026-10-28

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:9e2cf6287f25…` to `sha256:1beaa3084eee…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.4] - 2026-10-27

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:c8a121e4bb13…` to `sha256:1336e85aaed2…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.3] - 2026-10-23

### Changed

- **`mattermost/mattermost-team-edition:12.0.0` moved to `mattermost/mattermost-team-edition:12.0.1`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:22a759fb803a…` to `sha256:c8a121e4bb13…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.2] - 2026-10-22

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:6d3e069ee484…` to `sha256:22a759fb803a…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.
- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:a902560563d3…` to `sha256:9e2cf6287f25…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.1] - 2026-10-21

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:0f6150202bc1…` to `sha256:6d3e069ee484…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.
- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:ca0bd484cb98…` to `sha256:a902560563d3…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.10.0] - 2026-10-18

### Changed

- **`mattermost/mattermost-team-edition:11.11.1` moved to `mattermost/mattermost-team-edition:12.0.0`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

### Upgrading

Mattermost 12 is a major release. What it asks of a deployment like this one, from [its changelog](https://docs.mattermost.com/product-overview/mattermost-v12-changelog.html):

- **PostgreSQL 15 or newer.** This stack runs `postgres:16`, so nothing to do.
- **A migration takes a brief `ACCESS EXCLUSIVE` lock** while it raises the maximum message length, and the post full-text index is rebuilt afterwards; search runs without it until then. On a large instance, upgrade in a quiet hour.
- **Web plugins not rebuilt for v12 (React 19) may not activate.** Update community plugins first.
- **Removed settings are ignored** if left in `config.json`; `ExperimentalEnableHardenedMode` is now `EnableHardenedMode`.

Before it landed, the deploy job booted the stack on 12.0.0, and the clean-machine drill restored 1.9.5's backups onto it and read its marker row back.

## [1.9.5] - 2026-10-16

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:575fa15b1350…` to `sha256:0f6150202bc1…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

### Added

- **CI fails on an anonymous volume.** A `VOLUME` an image declares and no mount covers becomes an anonymous volume: never backed up, and replaced by an empty one on `down`/`up`. The deployment verification now inspects every container it starts and fails on one, unless `.github/anonymous-volumes` lists it with the reason. GLPI's data lived in exactly such a volume from 2026-08-31 to 2026-10-11.

## [1.9.4] - 2026-10-09

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:b588cb566045…` to `sha256:575fa15b1350…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.9.3] - 2026-10-08

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:65b16a8b326e…` to `sha256:ca0bd484cb98…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.9.2] - 2026-10-07

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:24841fe2de73…` to `sha256:b588cb566045…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.9.1] - 2026-10-06

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:1a6ab3f5345e…` to `sha256:65b16a8b326e…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

### Fixed

- **`update.sh` no longer stops without a word when a release adds a variable and no compose file requires one.** The search for `${VAR:?}` came back empty, and under `pipefail` that empty result ended the script with status 1 right after it listed the new variables.
- **`update.sh` stops on a `.env` it cannot read, before the checkout.** It used to fall through: every new required variable read as "not set", or, with none, the tree moved to the new tag and `docker compose up` failed on the permission. Now it names the file, its owner and mode, and changes nothing.

## [1.9.0] - 2026-09-26

### Added

- **Traefik's timeouts on the HTTPS entry point can be set from `.env`.**
  `TRAEFIK_READ_TIMEOUT`, `TRAEFIK_WRITE_TIMEOUT` and `TRAEFIK_IDLE_TIMEOUT`
  default to Traefik's own values (60s, 0s, 180s), so nothing changes unless
  you set them. Traefik reads its static configuration from one source, here
  the command in the compose file, and an override file can only replace that
  command whole; a variable is the way to tune it and keep taking updates.
  The same change was asked for in the [Keycloak template](https://github.com/heyvaldemar/keycloak-traefik-letsencrypt-docker-compose), and every template in the fleet gets it at once.

## [1.8.8] - 2026-09-25

### Changed

- **`mattermost/mattermost-team-edition:11.11.0` moved to `mattermost/mattermost-team-edition:11.11.1`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:a3b7f434b2dc…` to `sha256:1a6ab3f5345e…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.8.7] - 2026-09-23

### Fixed

- **The restore scripts had never been run by CI, and could not have been.**
  They carried the database name, user and backup directory as literals, found
  their containers with a name filter that misses them under any `-p` but the
  default. The data restore ran `docker exec -it`, which refuses to start
  without a terminal, and cleared the directory with `rm -rf dir/*`, which
  leaves every dotfile of the newer state in place. Both scripts now take every
  path, name and credential from the running backups container, accept the
  backup file name as an argument, stop the application while they work and
  start it again whatever happens, and CI runs them: a marker written after a
  backup must be gone once that backup is restored, for the database and for
  the application data.

### Changed

- **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.

## [1.8.6] - 2026-09-21

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:1c32e7c36820…` to `sha256:24841fe2de73…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.8.5] - 2026-09-19

### Security

- **`postgres:16` was rebuilt upstream**; the pin moved from `sha256:f1c3376c26f2…` to `sha256:a3b7f434b2dc…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.8.4] - 2026-09-18

### Security

- **`traefik:3.7` was rebuilt upstream**; the pin moved from `sha256:f86a2cab1b5c…` to `sha256:1c32e7c36820…`. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

## [1.8.3] - 2026-09-16

### Changed

- **`mattermost/mattermost-team-edition:11.10.2` moved to `mattermost/mattermost-team-edition:11.11.0`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

## [1.8.2] - 2026-09-15

### Changed

- **`mattermost/mattermost-team-edition:11.10.1` moved to `mattermost/mattermost-team-edition:11.10.2`.** The freshness check reported the lag; the deploy job booted the stack on the new image before this landed.

## [1.8.1] - 2026-09-10

### Fixed

- **The archive is read back before it is called a backup.** The library
  tarball was renamed into place on tar's exit code alone. That code has never
  been a statement about whether the file it produced opens, and the gap is not
  theoretical: an archive truncated after tar had already exited still carries
  exit status 0, and the old condition promoted it, logged `Data backup OK`, and
  pruned older archives around it. Measured on a bench in the image this sidecar
  actually runs — a good archive passes, a blocked destination is refused by
  either condition, and a truncated one is caught only by the read-back.

  One `tar -tzf` between the write and the rename now decides it. The database
  dump is untouched: it is written as `pg_dump | gzip` under `set -o pipefail`,
  where a failure at either end of the pipe already fails the pipeline and
  nothing is renamed.

  Found by a new rule in [fleet-ops](https://github.com/heyvaldemar/fleet-ops)
  that asserts this property across every repository, written after one loop
  turned out to be missing it. Eighteen were.

## [1.8.0] - 2026-09-07

### Added

- **`update.sh`: move between release tags on purpose.** It updates to the latest release (a combination this repository's CI has booted and smoke-tested), refuses to cross a major version unattended, refuses to run over local changes, and names any new required variable before anything has moved. `--dry-run` says what would happen.

  finished backup would have, and it is the newest one, which is exactly what
  the restore script and the end-to-end test pick. Every backup is now written
  to `<name>.partial` and renamed only after the dump succeeds, so the real
  name never exists unless the file behind it is complete. Verified by killing
  a dump in flight: before, the restore path selected a file that failed
  `gzip -t`; after, it finds nothing to select.

## [1.6.0] - 2026-09-04

### Added

- **A shutdown grace period for PostgreSQL.** Docker stops a container with
  SIGTERM and ten seconds, then SIGKILL. That default is not always enough:
  PostgreSQL has a checkpoint to write, MariaDB has InnoDB to flush, and Redis
  saves its dataset on the way out. Killed halfway, the next start does crash
  recovery, and a Redis holding another application's file locks leaves them
  behind for a person to clear by hand. Sixty seconds now, overridable per
  service with `<PREFIX>_STOP_GRACE_PERIOD` in `.env`. The backup sidecar is
  deliberately left alone: its failure mode is a truncated dump file, which a
  longer grace period does not fix.

## [1.5.0] - 2026-09-03

### Added

- **Per-image version overrides.** Every pin in the `x-images` block is
  now `${<PREFIX>_IMAGE_TAG:-repo:${<PREFIX>_IMAGE_VERSION:-tag@sha256:digest}}`.
  Set `<PREFIX>_IMAGE_VERSION` in `.env` to run a different version of one
  image while every other pin stays as tested (Compose pulls that tag
  without a digest), or `<PREFIX>_IMAGE_TAG` to 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 up` fails with an invalid reference instead of
  deploying something unexpected.

## [1.4.0] - 2026-09-02

### Security

- **Container hardening.** Every service runs with
  `security_opt: no-new-privileges:true` (no privilege escalation via
  setuid binaries even if a process escapes its initial capability
  set). Infrastructure containers (the reverse proxy, databases,
  caches, backups) drop every Linux capability and add back only what
  their entrypoints need (bind :80/:443, chown a data directory, drop to
  the service user). Application containers keep the default capability
  set: upstream images assume it, and a wrong guess there is a boot loop
  in production, not a hardening win. CI boots the stack under these
  settings on every push.

## [1.3.0] - 2026-09-02

### 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`, defaults 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; raise a limit if a service is OOM-killed
  under your real load (`docker inspect` shows `OOMKilled=true`).

## [1.2.0] - 2026-09-02

### Fixed

- **A file changing while the data archive is written no longer marks
  the backup as failed.** GNU `tar` exits 1 when a live application
  touched a file mid-read; the archive is complete and usable. The loop
  now treats exit 1 as success (noting it in the log line) and only a
  real `tar` failure (exit 2) produces `FAILED`. CI's archive check now
  reads the file name from the `backup OK` log line instead of racing an
  archive that may still be being written.

### Added

- **`tests/e2e-backup-restore.sh`**: seven end-to-end scenarios against
  the live stack, run by CI on every push and by you locally: the
  required-variable guard fires, a backup is produced, it is a readable
  archive with real dump content (and a readable data `tar.gz` where the
  stack has one), a database outage is reported as `FAILED`, **restore
 replaces database state** (a marker row inserted after the
  baseline backup is gone after restoring it), and pruning removes only
  old files.

## [1.1.0] - 2026-09-02

### Fixed

- **A failed database dump no longer produces a silent, corrupt backup.**
  The old loop piped the dump into `gzip` and only checked `gzip`'s exit
  status, so a dump that failed halfway (database down, wrong password,
  disk full) still left a small `.gz` that looked like a backup. The loop
  now runs with `pipefail`, logs `Database backup OK: <file> (<bytes>
  bytes)` or `Database backup FAILED` per cycle, keeps a failed dump as
  `<file>.failed` for diagnosis, and prunes only its own files. Retention
  set to `0` disables pruning instead of deleting everything.

### Added

- CI now waits for the first backup cycle and proves the produced
  archive is readable and contains a real dump header (plus a readable
  `tar.gz` for the data backup where the stack has one).

## [1.0.0] - 2026-08-31

First semver release. Brings this template to the fleet standard established
in [keycloak-traefik-letsencrypt-docker-compose](https://github.com/heyvaldemar/keycloak-traefik-letsencrypt-docker-compose)
v1.2.0.

### Security

- **Mattermost bumped 10.10.1 → 11.10.1** (back up before pulling;
  consult Mattermost's upgrade notes for your path). **Traefik bumped
  3.2 → 3.7** (3.2's Docker client cannot talk to Docker Engine 29).
- **All three images pinned by `tag@sha256:digest`** (`postgres:16`
  digest-pinned; major unchanged).
- **Credentials untracked from git.** The tracked `.env` carried a
  generated-looking database password published on GitHub. Rotate it if
  reused. `.env` is now gitignored; compose fails fast on unset values.

### Changed

- **Image pins live in the compose file as interpolation defaults**
  (`x-images` block); `.env` carries only secrets, hostnames, and
  deliberate overrides. Backup-loop variables escaped (`$$VAR`).
- README rebuilt to the fleet evaluator-first structure.

### Added

- **Deployment Verification workflow**: shellcheck + actionlint; Trivy
  scans of all three pinned images; weekly `check-pin-freshness` (digest
  drift + Mattermost and Traefik release lag); deploy-and-test that boots
  the full stack with ephemeral credentials and requires
  `/api/v4/system/ping` to answer OK through Traefik.

### Fixed

- Shellcheck findings in both restore scripts.

[Unreleased]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.12...HEAD
[1.10.12]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.11...v1.10.12
[1.10.11]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.10...v1.10.11
[1.10.10]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.9...v1.10.10
[1.10.9]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.8...v1.10.9
[1.10.8]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.7...v1.10.8
[1.10.7]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.6...v1.10.7
[1.10.6]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.5...v1.10.6
[1.10.5]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.4...v1.10.5
[1.10.4]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.3...v1.10.4
[1.10.3]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.2...v1.10.3
[1.10.2]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.1...v1.10.2
[1.10.1]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.10.0...v1.10.1
[1.10.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.5...v1.10.0
[1.9.5]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.4...v1.9.5
[1.9.4]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.3...v1.9.4
[1.9.3]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.2...v1.9.3
[1.9.2]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.1...v1.9.2
[1.9.1]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.9.0...v1.9.1
[1.9.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.8...v1.9.0
[1.8.6]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.5...v1.8.6
[1.8.5]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.4...v1.8.5
[1.8.4]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.3...v1.8.4
[1.8.3]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.2...v1.8.3
[1.8.2]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.8.1...v1.8.2
[1.8.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.7.0...v1.8.0
[1.7.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.5.0...v1.6.0
[1.5.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/heyvaldemar/mattermost-traefik-letsencrypt-docker-compose/releases/tag/v1.0.0