diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..2e5d9fb712be9a071f1e1c14f5443a2fe0500954
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,51 @@
+[project]
+name = "osmo"
+description = "Odevzdávací systém Matematické olympiády"
+version = "1.0"
+dependencies = [
+	# Udržujte prosím seřazené
+	'Flask',
+	'Flask-WTF',
+	'WTForms',
+	'bcrypt',
+	'bleach',
+	'blinker',
+	'click',
+	'dateutils',
+	'dnspython',
+	'flask_bootstrap',
+	'flask_sqlalchemy',
+	'markdown',
+	'pikepdf',
+	'pillow',
+	'psycopg2',
+	'pypdf',
+	'python-magic',
+	'python-poppler',
+	'pyzbar',
+	'qrcode[pil]',
+	'requests',
+	'sqlalchemy[mypy] < 2.0',
+	'token-bucket',
+	'unidecode',
+	'uwsgidecorators',
+	# Používáme pro vývoj, ale aby je pylsp našel, musí být ve stejném virtualenvu
+	# jako ostatní knihovny.
+	'sqlalchemy-stubs',
+	'types-Markdown',
+	'types-bleach',
+	'types-flask_sqlalchemy',
+	'types-pillow',
+	'types-python-dateutil',
+	'types-requests',
+	'types-setuptools',
+]
+requires-python = ">= 3.11"
+maintainers = [ { name = "Martin Mareš", email = "mj@ucw.cz" } ]
+readme = "README.md"
+license = "AGPL-3.0-or-later"
+license-files = ["LICENSE"]
+
+[build-system]
+requires = ["setuptools >= 66.1.1"]
+build-backend = "setuptools.build_meta"
diff --git a/setup.py b/setup.py
index 91dc036f1793f3a1d42353622a7645629f99c660..3f1ab4d229e50112b16d1777ca5067037fb256c8 100644
--- a/setup.py
+++ b/setup.py
@@ -3,9 +3,6 @@
 import setuptools
 
 setuptools.setup(
-    name='osmo',
-    version='1.0',
-    description='Odevzdávací systém Matematické olympiády',
     packages=['mo', 'mo/ext', 'mo/jobs', 'mo/web'],
     scripts=[
         'bin/add-role',
@@ -32,42 +29,4 @@ setuptools.setup(
     ],
     include_package_data=True,
     zip_safe=False,
-    install_requires=[
-        # Udržujte prosím seřazené
-        'Flask',
-        'Flask-WTF',
-        'WTForms',
-        'bcrypt',
-        'bleach',
-        'blinker',
-        'click',
-        'dateutils',
-        'dnspython',
-        'flask_bootstrap',
-        'flask_sqlalchemy',
-        'markdown',
-        'pikepdf',
-        'pillow',
-        'psycopg2',
-        'pypdf',
-        'python-magic',
-        'python-poppler',
-        'pyzbar',
-        'qrcode[pil]',
-        'requests',
-        'sqlalchemy[mypy] < 2.0',
-        'token-bucket',
-        'unidecode',
-        'uwsgidecorators',
-        # Používáme pro vývoj, ale aby je pylsp našel, musí být ve stejném virtualenvu
-        # jako ostatní knihovny.
-        'sqlalchemy-stubs',
-        'types-Markdown',
-        'types-bleach',
-        'types-flask_sqlalchemy',
-        'types-pillow',
-        'types-python-dateutil',
-        'types-requests',
-        'types-setuptools',
-    ],
 )