diff --git a/Dockerfile.rpmbuild b/Dockerfile.rpmbuild
new file mode 100644
index 0000000..01201c1
--- /dev/null
+++ b/Dockerfile.rpmbuild
@@ -0,0 +1,108 @@
+# syntax=docker/dockerfile:1
+FROM fedora:42
+
+# rpmbuild in a container does not auto-install BuildRequires. Since we're
+# building directly in Docker (not mock), we pre-install the common deps that
+# Fedora's pyproject macros will require for Bouquin.
+#
+# NOTE: bouquin also needs python3dist(sqlcipher4) at build time (because
+# %pyproject_buildrequires includes runtime deps). That one is NOT in Fedora;
+# we install it from /deps.
+RUN set -eux; \
+ dnf -y update; \
+ dnf -y install \
+ rpm-build rpmdevtools \
+ redhat-rpm-config \
+ gcc \
+ make \
+ findutils \
+ tar \
+ gzip \
+ rsync \
+ python3 \
+ python3-devel \
+ python3-pip \
+ python3-setuptools \
+ python3-wheel \
+ pyproject-rpm-macros \
+ python3-rpm-macros \
+ python3-poetry-core \
+ desktop-file-utils \
+ python3-requests \
+ python3-markdown \
+ python3-pyside6 \
+ xcb-util-cursor ; \
+ dnf -y clean all
+
+RUN set -eux; cat > /usr/local/bin/build-rpm <<'EOF'
+#!/usr/bin/env bash
+set -euo pipefail
+
+SRC="${SRC:-/src}"
+WORKROOT="${WORKROOT:-/work}"
+OUT="${OUT:-/out}"
+DEPS_DIR="${DEPS_DIR:-/deps}"
+
+# Install bouquin-sqlcipher4 from local rpm
+# Filter out .src.rpm and debug* subpackages if present.
+if [ -d "${DEPS_DIR}" ] && compgen -G "${DEPS_DIR}/*.rpm" > /dev/null; then
+ mapfile -t rpms < <(ls -1 "${DEPS_DIR}"/*.rpm | grep -vE '(\.src\.rpm$|-(debuginfo|debugsource)-)')
+ if [ "${#rpms[@]}" -gt 0 ]; then
+ echo "Installing dependency RPMs from ${DEPS_DIR}:"
+ printf ' - %s\n' "${rpms[@]}"
+ dnf -y install "${rpms[@]}"
+ dnf -y clean all
+ else
+ echo "NOTE: Only src/debug RPMs found in ${DEPS_DIR}; nothing installed." >&2
+ fi
+else
+ echo "NOTE: No RPMs found in ${DEPS_DIR}. If the build fails with missing python3dist(sqlcipher4)," >&2
+ echo " mount your bouquin-sqlcipher4 RPM directory as -v
:/deps" >&2
+fi
+
+mkdir -p "${WORKROOT}" "${OUT}"
+WORK="${WORKROOT}/src"
+rm -rf "${WORK}"
+mkdir -p "${WORK}"
+
+rsync -a --delete \
+ --exclude '.git' \
+ --exclude '.venv' \
+ --exclude 'dist' \
+ --exclude 'build' \
+ --exclude '__pycache__' \
+ --exclude '.pytest_cache' \
+ --exclude '.mypy_cache' \
+ "${SRC}/" "${WORK}/"
+
+cd "${WORK}"
+
+# Determine version from pyproject.toml unless provided
+if [ -n "${VERSION:-}" ]; then
+ ver="${VERSION}"
+else
+ ver="$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "([^"]+)".*/\1/')"
+fi
+
+TOPDIR="${WORKROOT}/rpmbuild"
+mkdir -p "${TOPDIR}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
+
+tarball="${TOPDIR}/SOURCES/bouquin-${ver}.tar.gz"
+tar -czf "${tarball}" --transform "s#^#bouquin/#" .
+
+cp -v "rpm/bouquin.spec" "${TOPDIR}/SPECS/bouquin.spec"
+
+rpmbuild -ba "${TOPDIR}/SPECS/bouquin.spec" \
+ --define "_topdir ${TOPDIR}" \
+ --define "upstream_version ${ver}"
+
+shopt -s nullglob
+cp -v "${TOPDIR}"/RPMS/*/*.rpm "${OUT}/" || true
+cp -v "${TOPDIR}"/SRPMS/*.src.rpm "${OUT}/" || true
+echo "Artifacts copied to ${OUT}"
+EOF
+
+RUN chmod +x /usr/local/bin/build-rpm
+
+WORKDIR /work
+ENTRYPOINT ["/usr/local/bin/build-rpm"]
diff --git a/README.md b/README.md
index 4a52ef4..7d4e55f 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,25 @@ sudo apt update
sudo apt install bouquin
```
+### Fedora 42
+
+```bash
+sudo rpm --import https://mig5.net/static/mig5.asc
+
+sudo tee /etc/yum.repos.d/mig5.repo > /dev/null << 'EOF'
+[mig5]
+name=mig5 Repository
+baseurl=https://rpm.mig5.net/rpm/$basearch
+enabled=1
+gpgcheck=1
+repo_gpgcheck=1
+gpgkey=https://mig5.net/static/mig5.asc
+EOF
+
+sudo dnf upgrade --refresh
+sudo dnf install bouquin
+```
+
### From PyPi/pip
* `pip install bouquin`
diff --git a/poetry.lock b/poetry.lock
index d2932af..c320489 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -640,4 +640,4 @@ zstd = ["backports-zstd (>=1.0.0)"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<3.14"
-content-hash = "0241cd7378c45e79da728a23b89defa18f776ada9af1e60f2a19b0d90f3a2c19"
+content-hash = "0ce61476b8c35f864d395ab3a4ee4645a1ad2e16aa800cba5e77e5bfee23d6a6"
diff --git a/pyproject.toml b/pyproject.toml
index f4592bc..9adca9a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,8 +13,8 @@ include = ["bouquin/locales/*.json", "bouquin/keys/mig5.asc", "bouquin/fonts/Not
python = ">=3.10,<3.14"
pyside6 = ">=6.8.1,<7.0.0"
bouquin-sqlcipher4 = "^4.12.0"
-requests = "^2.32.5"
-markdown = "^3.10"
+requests = "^2.32.3"
+markdown = "^3.7"
[tool.poetry.scripts]
bouquin = "bouquin.__main__:main"
diff --git a/release.sh b/release.sh
index 2fab9ac..d358e80 100755
--- a/release.sh
+++ b/release.sh
@@ -69,4 +69,28 @@ for dist in ${DISTS[@]}; do
reprepro -b /home/user/git/repo includedeb "${release}" "${debfile}"
done
+# RPM
+sudo apt-get -y install createrepo-c rpm
+docker build -f Dockerfile.rpmbuild -t bouquin-rpm:f42 --progress=plain .
+docker run --rm -v "$PWD":/src -v "$PWD/dist/rpm":/out -v "$HOME/git/bouquin-sqlcipher4/dist/rpm":/deps:ro bouquin-rpm:f42
+REPO_ROOT="${HOME}/git/repo_rpm"
+RPM_REPO="${REPO_ROOT}/rpm/x86_64"
+BUILD_OUTPUT="${HOME}/git/bouquin/dist"
+REMOTE="letessier.mig5.net:/opt/repo_rpm"
+KEYID="00AE817C24A10C2540461A9C1D7CDE0234DB458D"
+
+echo "==> Updating RPM repo..."
+mkdir -p "$RPM_REPO"
+cp "${BUILD_OUTPUT}/rpm/"*.rpm "$RPM_REPO/"
+
+createrepo_c "$RPM_REPO"
+
+echo "==> Signing repomd.xml..."
+qubes-gpg-client --local-user "$KEYID" --detach-sign --armor "$RPM_REPO/repodata/repomd.xml" > "$RPM_REPO/repodata/repomd.xml.asc"
+
+echo "==> Syncing repo to server..."
+rsync -aHPvz --exclude=.git --delete "$REPO_ROOT/" "$REMOTE/"
+
+echo "Done!"
+
ssh wolverine.mig5.net "echo ${VERSION} | tee /opt/www/mig5.net/bouquin/version.txt"
diff --git a/rpm/bouquin.spec b/rpm/bouquin.spec
new file mode 100644
index 0000000..341d80d
--- /dev/null
+++ b/rpm/bouquin.spec
@@ -0,0 +1,86 @@
+# bouquin Fedora 42 RPM spec using Fedora's pyproject RPM macros (Poetry backend).
+#
+# NOTE: Bouquin depends on "bouquin-sqlcipher4" project, but the RPM actually
+# provides the Python distribution/module as "sqlcipher4". To keep Fedora's
+# auto-generated python3dist() Requires correct, we rewrite the dependency key in
+# pyproject.toml at build time.
+%global upstream_version 0.8.0
+
+Name: bouquin
+Version: %{upstream_version}
+Release: 1%{?dist}.bouquin1
+Summary: A simple, opinionated notebook application (Python/Qt/SQLCipher)
+
+License: GPL-3.0-or-later
+URL: https://git.mig5.net/mig5/bouquin
+Source0: %{name}-%{version}.tar.gz
+
+BuildArch: noarch
+
+BuildRequires: pyproject-rpm-macros
+BuildRequires: python3-devel
+BuildRequires: python3-poetry-core
+BuildRequires: desktop-file-utils
+
+# Non-Python runtime dep (Fedora equivalent of Debian's libxcb-cursor0)
+Requires: xcb-util-cursor
+
+# Make sure private repo dependency is pulled in by package name as well.
+Requires: python3-sqlcipher4 >= 4.12.0
+
+%description
+Bouquin is a simple, opinionated notebook application written in Python and Qt,
+storing data using SQLCipher.
+
+%prep
+%autosetup -n bouquin
+
+# Patch dependency name so Fedora's python dependency generator targets the
+# provider from bouquin-sqlcipher4 RPM (python3dist(sqlcipher4)).
+%{python3} - <<'PY'
+from pathlib import Path
+import re
+
+p = Path("pyproject.toml")
+txt = p.read_text(encoding="utf-8")
+
+pattern = re.compile(r'(?ms)(^\[tool\.poetry\.dependencies\]\n.*?)(^\[|\Z)')
+m = pattern.search(txt)
+if not m:
+ raise SystemExit("Could not locate [tool.poetry.dependencies] in pyproject.toml")
+
+deps_block = m.group(1)
+deps_block2 = re.sub(r'(?m)^bouquin-sqlcipher4\s*=\s*(".*?")\s*$', r'sqlcipher4 = \1', deps_block)
+if deps_block == deps_block2:
+ raise SystemExit("Did not find bouquin-sqlcipher4 dependency to rewrite")
+
+p.write_text(txt[:m.start(1)] + deps_block2 + txt[m.end(1):], encoding="utf-8")
+PY
+
+desktop-file-validate debian/bouquin.desktop
+
+%generate_buildrequires
+%pyproject_buildrequires
+
+%build
+%pyproject_wheel
+
+%install
+%pyproject_install
+%pyproject_save_files bouquin
+
+# Desktop integration (mirrors debian/bouquin.install)
+install -Dpm 0644 debian/bouquin.desktop %{buildroot}%{_datadir}/applications/bouquin.desktop
+install -Dpm 0644 bouquin/icons/bouquin.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/bouquin.svg
+
+%files -f %{pyproject_files}
+%license LICENSE
+%doc README.md CHANGELOG.md
+%{_bindir}/bouquin
+
+%{_datadir}/applications/bouquin.desktop
+%{_datadir}/icons/hicolor/scalable/apps/bouquin.svg
+
+%changelog
+* Wed Dec 24 2025 Miguel Jacq - %{version}-%{release}
+- Initial RPM packaging for Fedora 42