diff --git a/.coveragerc b/.coveragerc
index f1263edf116d2e9f372c2e11a8ef333d779559a8..29d7fcbe390c8d646f0cd13a7b8069b6812f5a9b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -2,12 +2,9 @@
 source = km3io
 
 [report]
-include =
-    km3io/*
 omit =
-    tests/*
-    km3io/definitions/*
-    km3io/version.py
+    src/km3io/definitions/*
+    src/km3io/version.py
 exclude_lines =
     pragma: no cover
     raise AssertionError
diff --git a/.gitignore b/.gitignore
index 9bbda9f6a639c869a5867520cf6096a478800257..b20df1b8b925c0ffcbaa6adbee83bb5fea594ba8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,51 +6,21 @@ __pycache__/
 # C extensions
 *.so
 
-# Version info
-km3io/version.py
-
 # Distribution / packaging
 .Python
-env/
-bin/
+venv/
 build/
 develop-eggs/
 dist/
 eggs/
-lib/
-lib64/
-parts/
 sdist/
-var/
 *.egg-info/
-.installed.cfg
 *.egg
-.eggs
-pip-wheel-metadata
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
 
 # Unit test / coverage reports
-htmlcov/
-.tox/
 .coverage
 .cache
-nosetests.xml
-coverage.xml
-.coverage.*
-
-# Translations
-*.mo
-
-# Mr Developer
-.mr.developer.cfg
-.project
-.pydevproject
-
-# Rope
-.ropeproject
+reports
 
 # Sphinx documentation
 doc/_build/
@@ -58,28 +28,12 @@ doc/auto_examples/
 doc/modules/
 doc/api
 
-# VI swap files
-*.swp
-*.swo
-
-# PyCharm files
-.idea
-
 # jupyter files
 .ipynb_checkpoints/
 
-# 
-junit*.xml
-reports
-
-# misc
-*~
-*.dqd
-*.dat
-
 .pytest_cache/
-.DS_Store
+junit.xml
+.mypy_cache
 
-# venv, pyenv tmp
-.python-version
-venv
+# Version info
+src/km3io/version.py
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a3ca63fe293270bc92140d2a840378db9f8ef0ff..a61ffcbaa655253e57381f2259be823917459c9c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,7 +8,6 @@ variables:
 cache:
   paths:
     - .cache/pip
-    - venv/
   key: "$CI_COMMIT_REF_SLUG"
 
 stages:
diff --git a/MANIFEST.in b/MANIFEST.in
index 8da04a047d30a631a00f96cb01f44ab07ca86077..1bf23fd45e1b143824363f564d433edc17368b33 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,6 @@
-include README.rst
+include README.rst CHANGELOG.rst CONTRIBUTING.rst
 include LICENSE
-include requirements.txt
+include setup.py setup.cfg pyproject.toml
+include src/km3io/version.py
+
+recursive-include tests
diff --git a/Makefile b/Makefile
index 81f9d0421729f77bf4bf8bf6da0642f78c66b91b..3e13608fc15ea218fb03707486f1e65267ae6b42 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ test:
 	py.test --junitxml=./reports/junit.xml -o junit_suite_name=$(PKGNAME) tests
 
 test-cov:
-	py.test --cov ./km3io --cov-report term-missing --cov-report xml:reports/coverage.xml --cov-report html:reports/coverage tests
+	py.test --cov src/km3io --cov-report term-missing --cov-report xml:reports/coverage.xml --cov-report html:reports/coverage tests
 
 test-loop: 
 	py.test tests
@@ -40,7 +40,7 @@ dependencies:
 
 .PHONY: black
 black:
-	black --exclude '/_definitions/|version.py' km3io
+	black --exclude '/_definitions/|version.py' src/km3io
 	black examples
 	black tests
 	black doc/conf.py
@@ -48,7 +48,7 @@ black:
 
 .PHONY: black-check
 black-check:
-	black --check --exclude '/_definitions/|version.py' km3io
+	black --check --exclude '/_definitions/|version.py' src/km3io
 	black --check examples
 	black --check tests
 	black --check doc/conf.py
diff --git a/doc/conf.py b/doc/conf.py
index cd79de6ff493ec4d02ec3160cea07ad27b777733..a6af1b8b55cc52bf341a4a2d2ce1b0b9a3acf518 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -51,7 +51,7 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
 # AutoAPI
 autoapi_type = "python"
-autoapi_dirs = ["../km3io"]
+autoapi_dirs = ["../src/km3io"]
 autoapi_options = ["members", "undoc-members", "show-module-summary"]
 autoapi_include_summaries = True
 
diff --git a/pyproject.toml b/pyproject.toml
index 8c1be0fb0ea9a7c44107498e8799d3ac99b745e7..1c4b5fb19b7266b15eb8607e065a53a310116284 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,4 +2,4 @@
 requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]
 
 [tool.setuptools_scm]
-write_to = "km3io/version.py"
+write_to = "src/km3io/version.py"
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e31081a3aeb7b2c31a66e939bb22939eb80d707e
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,98 @@
+[metadata]
+name = km3io
+description = "KM3NeT I/O library without ROOT"
+long_description = file: README.rst
+long_description_content_type = text/x-rst
+url = https://git.km3net.de/km3py/km3io
+author = "Tamas Gal and Zineb Aly"
+author_email = tgal@km3net.de
+maintainer = Tamas Gal
+maintainer_email = tgal@km3net.de
+license = MIT
+license_file = LICENSE
+classifiers =
+    Development Status :: 5 - Production/Stable
+    Intended Audience :: Developers
+    Intended Audience :: Science/Research
+    License :: OSI Approved :: BSD License
+    Operating System :: OS Independent
+    Programming Language :: Python
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3 :: Only
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
+    Topic :: Scientific/Engineering
+keywords =
+    neutrino
+    astroparticle
+    physics
+    HEP
+    root
+
+[options]
+packages = find:
+install_requires =
+    docopt
+    numba>=0.50
+    awkward>=1.0.0rc2
+    awkward0
+    uproot3>=3.11.1
+    uproot>=4.2.2
+    setuptools_scm
+python_requires = >=3.6
+include_package_data = True
+package_dir =
+    =src
+
+[options.packages.find]
+where = src
+
+[options.extras_require]
+all =
+dev =
+    black==21.6b0
+    km3net-testdata>=0.2.26
+    ipykernel
+    matplotlib
+    memory_profiler
+    numpydoc
+    pillow
+    pytest
+    pytest-cov
+    pytest-flake8
+    pytest-pylint
+    pytest-watch
+    scipy
+    sphinx
+    sphinx-autoapi
+    sphinx-gallery>=0.1.12
+    sphinx_rtd_theme
+    sphinxcontrib-versioning
+    wheel
+
+[options.entry_points]
+console_scripts =
+    KPrintTree = km3io.utils.kprinttree:main
+
+[options.package_data]
+* = *.mplstyle, *.py.typed
+
+[bdist_wheel]
+universal = 1
+
+[tool:pytest]
+junit_family = xunit2
+addopts = -vv -rs -Wd
+testpaths =
+    tests
+
+[check-manifest]
+ignore =
+    src/km3pipe/version.py
+
+[tool:isort]
+profile = black
+multi_line_output = 3
diff --git a/setup.py b/setup.py
index 8775f6c668d9fa5117e256997ec4fbe3a65b6d5c..36f7fe77d71ee14ca8f7dc19f56f1aaa8cebf953 100644
--- a/setup.py
+++ b/setup.py
@@ -1,47 +1,4 @@
-#!/usr/bin/env python
-# Filename: setup.py
-"""
-The km3io setup script.
-
-"""
-import os
+#!/usr/bin/env python3
 from setuptools import setup
-import sys
-
-
-def read_requirements(kind):
-    """Return a list of stripped lines from a file"""
-    with open(os.path.join("requirements", kind + ".txt")) as fobj:
-        return [l.strip() for l in fobj.readlines()]
-
-
-try:
-    with open("README.rst") as fh:
-        long_description = fh.read()
-except UnicodeDecodeError:
-    long_description = "km3io, a library to read KM3NeT files without ROOT"
-
-setup(
-    name="km3io",
-    url="http://git.km3net.de/km3py/km3io",
-    description="KM3NeT I/O without ROOT",
-    long_description=long_description,
-    author="Zineb Aly, Tamas Gal, Johannes Schumann",
-    author_email="zaly@km3net.de, tgal@km3net.de, johannes.schumann@fau.de",
-    packages=["km3io"],
-    include_package_data=True,
-    platforms="any",
-    setup_requires=["setuptools_scm"],
-    use_scm_version=True,
-    install_requires=read_requirements("install"),
-    extras_require={kind: read_requirements(kind) for kind in ["dev"]},
-    python_requires=">=3.6",
-    entry_points={"console_scripts": ["KPrintTree=km3io.utils.kprinttree:main"]},
-    classifiers=[
-        "Intended Audience :: Developers",
-        "Intended Audience :: Science/Research",
-        "Programming Language :: Python",
-    ],
-)
 
-__author__ = "Zineb Aly, Tamas Gal and Johannes Schumann"
+setup()
diff --git a/km3io/__init__.py b/src/km3io/__init__.py
similarity index 100%
rename from km3io/__init__.py
rename to src/km3io/__init__.py
diff --git a/km3io/_definitions/__init__.py b/src/km3io/_definitions/__init__.py
similarity index 100%
rename from km3io/_definitions/__init__.py
rename to src/km3io/_definitions/__init__.py
diff --git a/km3io/_definitions/applications.py b/src/km3io/_definitions/applications.py
similarity index 100%
rename from km3io/_definitions/applications.py
rename to src/km3io/_definitions/applications.py
diff --git a/km3io/_definitions/daqdatatypes.py b/src/km3io/_definitions/daqdatatypes.py
similarity index 100%
rename from km3io/_definitions/daqdatatypes.py
rename to src/km3io/_definitions/daqdatatypes.py
diff --git a/km3io/_definitions/fitparameters.py b/src/km3io/_definitions/fitparameters.py
similarity index 100%
rename from km3io/_definitions/fitparameters.py
rename to src/km3io/_definitions/fitparameters.py
diff --git a/km3io/_definitions/mc_header.py b/src/km3io/_definitions/mc_header.py
similarity index 100%
rename from km3io/_definitions/mc_header.py
rename to src/km3io/_definitions/mc_header.py
diff --git a/km3io/_definitions/pmt_status.py b/src/km3io/_definitions/pmt_status.py
similarity index 100%
rename from km3io/_definitions/pmt_status.py
rename to src/km3io/_definitions/pmt_status.py
diff --git a/km3io/_definitions/reconstruction.py b/src/km3io/_definitions/reconstruction.py
similarity index 100%
rename from km3io/_definitions/reconstruction.py
rename to src/km3io/_definitions/reconstruction.py
diff --git a/km3io/_definitions/root.py b/src/km3io/_definitions/root.py
similarity index 100%
rename from km3io/_definitions/root.py
rename to src/km3io/_definitions/root.py
diff --git a/km3io/_definitions/trigger.py b/src/km3io/_definitions/trigger.py
similarity index 100%
rename from km3io/_definitions/trigger.py
rename to src/km3io/_definitions/trigger.py
diff --git a/km3io/_definitions/trkmembers.py b/src/km3io/_definitions/trkmembers.py
similarity index 100%
rename from km3io/_definitions/trkmembers.py
rename to src/km3io/_definitions/trkmembers.py
diff --git a/km3io/_definitions/w2list_genhen.py b/src/km3io/_definitions/w2list_genhen.py
similarity index 100%
rename from km3io/_definitions/w2list_genhen.py
rename to src/km3io/_definitions/w2list_genhen.py
diff --git a/km3io/_definitions/w2list_gseagen.py b/src/km3io/_definitions/w2list_gseagen.py
similarity index 100%
rename from km3io/_definitions/w2list_gseagen.py
rename to src/km3io/_definitions/w2list_gseagen.py
diff --git a/km3io/acoustics.py b/src/km3io/acoustics.py
similarity index 100%
rename from km3io/acoustics.py
rename to src/km3io/acoustics.py
diff --git a/km3io/definitions.py b/src/km3io/definitions.py
similarity index 100%
rename from km3io/definitions.py
rename to src/km3io/definitions.py
diff --git a/km3io/gseagen.py b/src/km3io/gseagen.py
similarity index 100%
rename from km3io/gseagen.py
rename to src/km3io/gseagen.py
diff --git a/km3io/offline.py b/src/km3io/offline.py
similarity index 100%
rename from km3io/offline.py
rename to src/km3io/offline.py
diff --git a/km3io/online.py b/src/km3io/online.py
similarity index 100%
rename from km3io/online.py
rename to src/km3io/online.py
diff --git a/km3io/rootio.py b/src/km3io/rootio.py
similarity index 100%
rename from km3io/rootio.py
rename to src/km3io/rootio.py
diff --git a/km3io/tools.py b/src/km3io/tools.py
similarity index 100%
rename from km3io/tools.py
rename to src/km3io/tools.py
diff --git a/km3io/utils/kprinttree.py b/src/km3io/utils/kprinttree.py
similarity index 100%
rename from km3io/utils/kprinttree.py
rename to src/km3io/utils/kprinttree.py