diff --git a/.gitignore b/.gitignore
index 3d076f8be233091e5de4724411b7de85f348e701..656fb787440124c276493eee15f0b53fdd71f7c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ __pycache__
 build
 dist
 env
+
+exposureinitializer/__version__.py
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 38ace8a201c7b7b500bf99b628bef457442f123e..45f0ad886ed5cbf49282ab147bf1396f40253f14 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,20 +8,55 @@ cache:
     - .cache/pip
     - venv/
 
-before_script:
-  - python3 -V
-  - pip3 install virtualenv
-  - virtualenv venv
-  - source venv/bin/activate
-  - pip3 install --upgrade black==22.6.0
-  - pip3 install .
-  - pip3 install .[tests]
+.before_script: &test-before-script
+  before_script:
+    - python3 -V
+    - pip3 install virtualenv
+    - virtualenv venv
+    - source venv/bin/activate
+    - pip3 config set global.extra-index-url https://git.gfz-potsdam.de/api/v4/projects/2940/packages/pypi/simple
+    - pip3 install --upgrade black==22.6.0
+    - pip3 install .
+    - pip3 install .[tests]
+
+stages:
+  - tests
+  - release
 
 linters:
+  <<: *test-before-script
+
+  stage: tests
   script:
     - pip3 install .[linters]
     - make check
 
 tests:
+  <<: *test-before-script
+
+  stage: tests
   script:
     - pytest tests
+
+build:
+  stage: release
+  script:
+    - pip install --upgrade pip
+    - pip install setuptools setuptools_scm[toml] --upgrade
+    - pip install build twine
+    - python setup.py bdist_wheel
+    - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url https://git.gfz-potsdam.de/api/v4/projects/2940/packages/pypi dist/*
+  only:
+    - tags
+
+release_job:
+  stage: release
+  image: registry.gitlab.com/gitlab-org/release-cli:latest
+  only:
+    - tags
+  script:
+    - echo "running release_job for $CI_COMMIT_TAG"
+  release:
+    tag_name: '$CI_COMMIT_TAG'
+    description: '$CI_COMMIT_TAG_MESSAGE'
+    ref: '$CI_COMMIT_SHA'
diff --git a/README.md b/README.md
index dc8fc2478ab7988e942810ebadaff5f914f7409c..9c6c5f423a53a9932ab1aeeef7b0b0463b8aa5f5 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,16 @@ from OpenQuake exposure files.
 
 | Version | Description                                                                                           |
 |---------|-------------------------------------------------------------------------------------------------------|
-| 1.0     | Code used for the 2023.01 exposure data release. First published version of the exposure-initializer. |
+| 1.0.0   | Code used for the 2023.01 exposure data release. First published version of the exposure-initializer. |
+
+## How to install
+First, the package registry needs to be added to the pip index urls, so the package (and its dependencies) can be found.
+Afterwards, `exposure-initializer` can be installed with pip.
+
+```commandline
+pip config set global.extra-index-url https://git.gfz-potsdam.de/api/v4/projects/2940/packages/pypi/simple
+pip install exposureinitializer
+```
 
 ## Copyright and copyleft 
 
diff --git a/exposureinitializer/__init__.py b/exposureinitializer/__init__.py
index 4875bf7e1587905b5d55042a1a44ec202aa33ced..6491712ac50159f4ebe778fe6dc480b98ba97019 100644
--- a/exposureinitializer/__init__.py
+++ b/exposureinitializer/__init__.py
@@ -15,3 +15,12 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with this program. If not, see http://www.gnu.org/licenses/.
+from .exposureinitializer import ExposureInitializer
+from importlib.metadata import version, PackageNotFoundError
+
+try:
+    __version__ = version("exposureinitializer")
+except PackageNotFoundError:
+    __version__ = "unknown version"
+
+__all__ = ["ExposureInitializer"]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..972ee997f97833f74fa00b7f7caa4cc93db51dac
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,6 @@
+# pyproject.toml
+[build-system]
+requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"]
+
+[tool.setuptools_scm]
+write_to = "exposureinitializer/__version__.py"
diff --git a/setup.py b/setup.py
index a3aa9222f7958249f77825ca5ef06faeae4a854f..6a5fd143b29e1433af946b8bbdac2dac5da7531a 100644
--- a/setup.py
+++ b/setup.py
@@ -23,15 +23,14 @@ linters_require = ["black>=20.8b1", "pylint", "flake8"]
 
 setup(
     name="exposureinitializer",
-    version="1.0",
     description="This program generates the baseline exposure on the tiles for one country",
     keywords="exposuremodel, building, baselinemodel",
     author="Helmholtz-Zentrum Potsdam Deutsches GeoForschungsZentrum GFZ",
     license="AGPLv3+",
     install_requires=[
         "numpy",
-        "exposurelib@https://git.gfz-potsdam.de/globaldynamicexposure/libraries/exposure-lib/-/archive/master/exposure-lib-master.zip",  # noqa: E501
-        "taxonomylib@https://git.gfz-potsdam.de/globaldynamicexposure/libraries/taxonomy-lib/-/archive/main/taxonomy-lib-main.zip",  # noqa: E501
+        "exposurelib==1.0.0",
+        "taxonomylib==1.0.0",
     ],
     extras_require={
         "tests": tests_require,