diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 077f8ec1ef7..4473a007e8f 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,7 +1,7 @@
Thank you for contributing to pyo3!
Please consider adding the following to your pull request:
- - an entry in CHANGELOG.md
+ - an entry for this PR in newsfragments - see [https://pyo3.rs/main/contributing.html#documenting-changes]
- docs to all new functions and / or detail in the guide
- tests for all new or changed functions
diff --git a/.netlify/build.sh b/.netlify/build.sh
index e5ed8f55257..b25c3a2e3b8 100755
--- a/.netlify/build.sh
+++ b/.netlify/build.sh
@@ -23,6 +23,10 @@ else
echo "" > netlify_build/index.html
fi
+## Generate towncrier release notes
+
+pip install towncrier
+towncrier build --yes --version Unreleased --date TBC
## Build guide
diff --git a/.towncrier.template.md b/.towncrier.template.md
new file mode 100644
index 00000000000..056a06d9a10
--- /dev/null
+++ b/.towncrier.template.md
@@ -0,0 +1,14 @@
+{% for section_text, section in sections.items() %}{%- if section %}{{section_text}}{% endif -%}
+{% if section %}
+{% for category in ['packaging', 'added', 'changed', 'removed', 'fixed' ] if category in section %}
+### {{ definitions[category]['name'] }}
+
+{% if definitions[category]['showcontent'] %}
+{% for text, pull_requests in section[category].items() %}
+- {{ text }} {{ pull_requests|join(', ') }}
+{% endfor %}
+{% else %}
+- {{ section[category]['']|join(', ') }}
+{% endif %}
+
+{% endfor %}{% else %}No significant changes.{% endif %}{% endfor %}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c611c8d61cb..92b49803009 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,15 +6,7 @@ PyO3 versions, please see the [migration guide](https://pyo3.rs/latest/migration
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased]
-
-### Added
-
-- Added `type_input()` and `type_output()` to get the Python type of any Python-compatible object. [#2490](https://github.com/PyO3/pyo3/pull/2490)
-
-### Removed
-
-- Remove the deprecated `pyproto` feature, `#[pyproto]` macro, and all accompanying APIs. [#2587](https://github.com/PyO3/pyo3/pull/2587)
+
## [0.17.1] - 2022-08-28
diff --git a/Contributing.md b/Contributing.md
index e260e620efe..a1fd31a34d9 100644
--- a/Contributing.md
+++ b/Contributing.md
@@ -98,6 +98,17 @@ You can run these tests yourself with
```cargo xtask ci```
See [its documentation](https://github.com/PyO3/pyo3/tree/main/xtask#readme) for more commands you can run.
+### Documenting changes
+
+We use [towncrier](https://towncrier.readthedocs.io/en/stable/index.html) to generate a CHANGELOG for each release.
+
+To include your changes in the release notes, you should create one (or more) news items in the `newsfragments` directory. Valid news items should be saved as `..md` where `` is the pull request number and `` is one of the following:
+- `packaging` - for dependency changes and Python / Rust version compatibility changes
+- `added` - for new features
+- `changed` - for features which already existed but have been altered or deprecated
+- `removed` - for features which have been removed
+- `fixed` - for "changed" features which were classed as a bugfix
+
## Python and Rust version support policy
PyO3 aims to keep sufficient compatibility to make packaging Python extensions built with PyO3 feasible on most common package managers.
diff --git a/newsfragments/.gitignore b/newsfragments/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/newsfragments/2490.added.md b/newsfragments/2490.added.md
new file mode 100644
index 00000000000..f1d85bc3240
--- /dev/null
+++ b/newsfragments/2490.added.md
@@ -0,0 +1 @@
+Added `type_input()` and `type_output()` to get the Python type of any Python-compatible object.
diff --git a/newsfragments/2587.removed.md b/newsfragments/2587.removed.md
new file mode 100644
index 00000000000..65650998cee
--- /dev/null
+++ b/newsfragments/2587.removed.md
@@ -0,0 +1 @@
+Remove the deprecated `pyproto` feature, `#[pyproto]` macro, and all accompanying APIs.
diff --git a/pyproject.toml b/pyproject.toml
index e9e2104d6ea..07e5a0a230c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,3 +17,27 @@ exclude = '''
)/
)
'''
+
+[tool.towncrier]
+filename = "CHANGELOG.md"
+version = "0.17.1"
+start_string = "\n"
+template = ".towncrier.template.md"
+title_format = "## [{version}] - {project_date}"
+issue_format = "[#{issue}](https://github.com/PyO3/pyo3/pull/{issue})" # Note PyO3 shows pulls, not issues, in the CHANGELOG
+underlines = ["", "", ""]
+
+[tool.towncrier.fragment.packaging]
+name = "Packaging"
+
+[tool.towncrier.fragment.added]
+name = "Added"
+
+[tool.towncrier.fragment.changed]
+name = "Changed"
+
+[tool.towncrier.fragment.removed]
+name = "Removed"
+
+[tool.towncrier.fragment.fixed]
+name = "Fixed"