diff --git a/.chglog/CHANGELOG.md.tpl b/.chglog/CHANGELOG.md.tpl
new file mode 100644
index 000000000..296e50974
--- /dev/null
+++ b/.chglog/CHANGELOG.md.tpl
@@ -0,0 +1,22 @@
+{{ range .Versions }}
+
+## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
+
+- [Release note](https://libretime.org/docs/releases/{{ .Tag.Name }}/)
+
+{{ range .CommitGroups -}}
+### {{ .Title }}
+
+{{ range reverse .Commits -}}
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+{{ end }}
+{{ end -}}
+
+{{- if .RevertCommits -}}
+### Reverts
+
+{{ range .RevertCommits -}}
+- {{ .Revert.Header }}
+{{ end }}
+{{ end -}}
+{{ end -}}
diff --git a/.chglog/config.yml b/.chglog/config.yml
new file mode 100644
index 000000000..262f1e232
--- /dev/null
+++ b/.chglog/config.yml
@@ -0,0 +1,25 @@
+style: github
+template: CHANGELOG.md.tpl
+info:
+ title: CHANGELOG
+ repository_url: https://github.com/libretime/libretime
+options:
+ commits:
+ filters:
+ Type: [feat, fix, docs, test, ci]
+ sort_by: Date
+ commit_groups:
+ title_maps:
+ feat: Features
+ fix: Bug Fixes
+ docs: Documentation
+ test: Tests
+ ci: CI
+ sort_by: Custom
+ title_order: [feat, fix, docs, test, ci]
+ header:
+ pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
+ pattern_maps:
+ - Type
+ - Scope
+ - Subject
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/Makefile b/Makefile
index c156f215a..4b93ed221 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,9 @@ shell-check:
VERSION:
tools/version.sh
+changelog:
+ tools/changelog.sh
+
.PHONY: tarball
tarball: VERSION
$(MAKE) -C legacy build
diff --git a/docs/developer-manual/development/releases.md b/docs/developer-manual/development/releases.md
index ce88f750c..b138e615e 100644
--- a/docs/developer-manual/development/releases.md
+++ b/docs/developer-manual/development/releases.md
@@ -152,6 +152,15 @@ git show --quiet
git tag -a -m "$VERSION" "$VERSION"
```
+Generate the changelog for the newly tagged version:
+
+```bash
+make changelog
+
+git add .
+git commit -m "chore: generate changelog for $VERSION"
+```
+
Push the tag upstream to finalize the release process:
```bash
diff --git a/tools/changelog.sh b/tools/changelog.sh
new file mode 100755
index 000000000..685b9e4cd
--- /dev/null
+++ b/tools/changelog.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -u
+
+error() {
+ echo >&2 "error: $*"
+ exit 1
+}
+
+command -v git > /dev/null || error "git command not found!"
+command -v git-chglog > /dev/null || error "git-chglog command not found!"
+
+changelog="CHANGELOG.md"
+tag="${tag:-$(git describe --abbrev=0 --tags || error "could not extract latest tag")}"
+
+if grep --quiet "" "$changelog"; then
+ error "changelog has already been generated for tag $tag!"
+fi
+
+cat <(git-chglog "$tag") "$changelog" > "$changelog.tmp"
+mv "$changelog.tmp" "$changelog"
+
+if command -v npx > /dev/null; then
+ npx prettier --write "$changelog"
+fi