diff --git a/README.md b/README.md
index def4827ac1ca6e86a996220141a107cbf8230d99..78ea1dcd3d1bacc135bf8679045a85ba374d7df4 100644
--- a/README.md
+++ b/README.md
@@ -47,24 +47,24 @@ Each model has advantages and disadvantages.
 - Seems to have a slight performance hit (~10% increase in time in some very basic tests)
 
 # Installation
-    
+
     if using mamba/conda:
 
     mamba install -c conda-forge jpype1
 
 
- 
+
     git clone https://gitlab.orekit.org/Petrush/orekit_jpype.git
     pip install . -vv
 
     The pip will also install the orekitdata package from the orekitdata git respotory.
 
- 
+
  # Development
- 
-    To generate stub files, use the stubgenj package 
+
+    To generate stub files, use the stubgenj package
     https://gitlab.cern.ch/scripting-tools/stubgenj
-    
+
     and command line:
     python -m stubgenj --convert-strings --classpath "orekit_jpype/jars/*.jar" org.orekit  org.hipparchus java
 
@@ -74,11 +74,11 @@ Each model has advantages and disadvantages.
 
     To upload to pypi:
 
-    python3 -m twine upload --repository testpypi dist/*     
+    python3 -m twine upload --repository testpypi dist/*
 
     To install (something like):
 
-    pip install -i https://test.pypi.org/simple/ orekit-jpype==12.0.2.dev2     
+    pip install -i https://test.pypi.org/simple/ orekit-jpype==12.0.2.dev2
 
 # usage
 See the example notebooks and the package test folder for examples.
@@ -95,3 +95,46 @@ In the JCC version, the interfaces are implemented as special classes named Pyth
 ### Subclassing of Orekit abstract classes
 In the JCC version of Orekit it is possible to subclass classes from the set of PythonClassName classes. This is not possible in the Jpype version of Orekit, which is limited to implementation of interfaces only.
 
+
+# Packaging a project with `pyinstaller`
+
+This repository contains hooks for `pyinstaller` so that your project relying on Orekit can be packaged into an executable. No additional arguments are needed to `pyinstaller` thanks to the hooks, as long as `orekit_jpype` is installed in your Python packages.
+
+## Packaging the orekit data folder or library
+
+### Using the orekitdata Python library
+
+If you are using the [orekit data repository as a Python library](https://gitlab.orekit.org/orekit/orekit-data#notes-for-orekit-python-users), it already contains hooks for `pyinstaller` so you don't have to pass any argument, the orekit data folder will be automatically collected:
+
+```bash
+pyinstaller <your main Python script>
+```
+
+This will create a folder `dist/` containing your executable.
+
+### Locally managed orekit data folder
+
+If the `orekit-data` folder is located in the same folder as your main Python script, you can for instance use the following syntax to load the orekit data in your Python code:
+
+```python
+from orekit_jpype.pyhelpers import setup_orekit_data
+dirpath = os.path.dirname(os.path.abspath(__file__))
+setup_orekit_data(filenames=os.path.join(dirpath, "orekit-data"), from_pip_library=False)
+```
+
+Then you can use the following option to package your `orekit-data` folder in your executable.
+
+```bash
+pyinstaller --add-data orekit-data/*:./orekit-data/ <your main Python script>
+```
+
+This will create a folder `dist/` containing your executable.
+
+## For developers: testing the hook
+
+```bash
+python -m PyInstaller.utils.run_tests --include_only orekit_jpype._pyinstaller
+```
+
+This will package a minimal executable containing orekit_jpype, orekitdata and run the test case `test/OrekitDataLoadOnlyLibTest.py` to test that everything works as expected.
+TODO: include this test procedure in a CI pipeline.
diff --git a/environment.yml b/environment.yml
index 43aed5fc8ad2472cd35c366ee2c52d8090f6c08f..c06ce6d17e922c5340b67efe71ccf89ec0741eb2 100644
--- a/environment.yml
+++ b/environment.yml
@@ -10,4 +10,4 @@ dependencies:
   - scipy
   - pytz
   - cartopy
-
+  - pyinstaller
diff --git a/orekit_jpype/_pyinstaller/entry_points.py b/orekit_jpype/_pyinstaller/entry_points.py
new file mode 100644
index 0000000000000000000000000000000000000000..aae4219f7ae98a31a2217f3660b65f55ab395111
--- /dev/null
+++ b/orekit_jpype/_pyinstaller/entry_points.py
@@ -0,0 +1,16 @@
+import os
+from pathlib import Path
+
+
+fspath = getattr(os, 'fspath', str)
+
+
+_pyinstaller_path = Path(__file__).parent
+
+
+def get_hook_dirs():
+    return [fspath(str(_pyinstaller_path))]
+
+
+def get_PyInstaller_tests():
+    return [fspath(_pyinstaller_path)]
diff --git a/orekit_jpype/_pyinstaller/hook-orekit_jpype.py b/orekit_jpype/_pyinstaller/hook-orekit_jpype.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb26057d21e6319ce70b0171c226f0a20c1014b5
--- /dev/null
+++ b/orekit_jpype/_pyinstaller/hook-orekit_jpype.py
@@ -0,0 +1,10 @@
+import os
+from pathlib import Path
+
+import orekit_jpype
+
+fspath = getattr(os, 'fspath', str)
+
+jar_path_glob = Path(orekit_jpype.__file__).parent.joinpath("jars", "*.jar")
+
+datas = [[fspath(jar_path_glob), os.path.join("orekit_jpype", "jars")]]
diff --git a/orekit_jpype/_pyinstaller/orekit_data_load_example.py b/orekit_jpype/_pyinstaller/orekit_data_load_example.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5bda9c4c1bea50d807466212d1c67b6c2beaf04
--- /dev/null
+++ b/orekit_jpype/_pyinstaller/orekit_data_load_example.py
@@ -0,0 +1,8 @@
+import orekit_jpype as orekit
+orekit.initVM()
+
+from orekit_jpype.pyhelpers import setup_orekit_data
+setup_orekit_data(from_pip_library=True)
+
+from org.orekit.utils import Constants
+assert(Constants.WGS84_EARTH_EQUATORIAL_RADIUS == 6378137.0)
diff --git a/orekit_jpype/_pyinstaller/test_orekit_jpype_pyinstaller.py b/orekit_jpype/_pyinstaller/test_orekit_jpype_pyinstaller.py
new file mode 100644
index 0000000000000000000000000000000000000000..5baa6bbdbf973602cd9b38a019065ce76b82f49a
--- /dev/null
+++ b/orekit_jpype/_pyinstaller/test_orekit_jpype_pyinstaller.py
@@ -0,0 +1,26 @@
+import os
+from pathlib import Path
+from subprocess import run
+
+import PyInstaller.__main__
+
+
+fspath = getattr(os, 'fspath', str)
+
+
+test_file = Path(__file__).parent.joinpath('orekit_data_load_example.py')
+
+
+def test_start_and_stop(tmp_path):
+    name = 'orekit_jpype_test_app'
+    dist = tmp_path.joinpath('dist')
+    work = tmp_path.joinpath('build')
+
+    PyInstaller.__main__.run([
+        '--name', name,
+        '--distpath', fspath(dist),
+        '--workpath', fspath(work),
+        fspath(test_file)
+    ])
+
+    run([str(dist / name / name)], check=True)
diff --git a/pyproject.toml b/pyproject.toml
index fa32798aa7c46e2593d14386466c9dd44d5304e7..7348e31de04fa7dfa06e7b134c9a13bab6bbafa1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -40,5 +40,11 @@ tests = [
 ]
 
 [pytest]
-testpaths = "test"
+testpaths = [
+    "test",
+    "orekit_jpype/_pyinstaller"
+]
 
+[project.entry-points.pyinstaller40]
+"hook-dirs" = "orekit_jpype._pyinstaller.entry_points:get_hook_dirs"
+"tests" = "orekit_jpype._pyinstaller.entry_points:get_PyInstaller_tests"