diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2c3a1314800e59d4f1eaeac2a957bf97c27fea48..e588cc7c61b4e37af8581e53bb89dad327c0162b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,6 +6,7 @@ variables:
 stages:
   - build_docker
   - testing
+  - release
 
 build_docker_from_scratch:
   only:
@@ -31,10 +32,12 @@ build_docker_from_cache:
     - docker build -t $CONTAINER_TEST_IMAGE .
     - docker push $CONTAINER_TEST_IMAGE
 
+before_script:
+  - pip3 install --user .
+
 mytest:
   stage: testing
   script:
-    - pip3 install .
     - pytest-3 -q --cov=resolve test
   coverage: '/^TOTAL.+?(\d+\%)$/'
 
@@ -43,10 +46,17 @@ test_mpi:
   variables:
     OMPI_MCA_btl_vader_single_copy_mechanism: none
   script:
-    - pip3 install .
     - mpiexec -n 2 --bind-to none pytest-3 -q test/test_mpi
 
-# staticchecks:
-#   stage: testing
-#   script:
-#     - flake8 .
+pages:
+  stage: release
+  script:
+    - rm -rf docs/build docs/source/mod
+    - sh docs/generate.sh
+    - mv docs/build public
+  artifacts:
+    paths:
+      - public
+  only:
+    - master
+
diff --git a/Dockerfile b/Dockerfile
index 49c98916ba192c15fa86f3dbc439c536d04c2b2c..844260a9e0fc63d9be890a9a4190eb29bd85a206 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,7 +13,8 @@ RUN pip3 install astropy
 RUN apt-get install -qq python3-mpi4py
 # Testing dependencies
 RUN apt-get install -qq python3-pytest-cov
-RUN pip3 install flake8
+# Documentation dependencies
+RUN pip3 install pydata-sphinx-theme
 
 # Create user (openmpi does not like to be run as root)
 RUN useradd -ms /bin/bash testinguser
diff --git a/README.md b/README.md
index 2f43b3ce6ebf3d47e3906b2f21a13f41ef1ea144..6bdaaefaddb85e1bfd4fb900bfe5ba2b29ab2ad1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
 # resolve
 
+Documentation:
+[http://ift.pages.mpcdf.de/resolve](http://ift.pages.mpcdf.de/resolve)
+
 Resolve aims to be a general radio aperature synthesis algorithm.
 It is based on Bayesian principles and formulated in the language of information field theory.
 Its features include single-frequency imaging with either only a diffuse or a diffuse+point-like sky model as prior, single-channel antenna-based calibration with a regularization in temporal domain and w-stacking.
diff --git a/docs/generate.sh b/docs/generate.sh
new file mode 100755
index 0000000000000000000000000000000000000000..12eb53a4ad803631c4c49d30ca9cf8b4b46d06e4
--- /dev/null
+++ b/docs/generate.sh
@@ -0,0 +1,2 @@
+sphinx-apidoc -e -o docs/source/mod resolve
+sphinx-build -b html docs/source/ docs/build/
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..da89816f6a60df02516cfe1be133b83d9020f0e0
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,28 @@
+extensions = [
+    'sphinx.ext.napoleon',  # Support for NumPy and Google style docstrings
+    'sphinx.ext.imgmath',  # Render math as images
+    'sphinx.ext.viewcode'  # Add links to highlighted source code
+]
+master_doc = 'index'
+
+napoleon_google_docstring = False
+napoleon_numpy_docstring = True
+napoleon_use_ivar = True
+napoleon_use_admonition_for_notes = True
+napoleon_use_admonition_for_examples = True
+napoleon_use_admonition_for_references = True
+napoleon_include_special_with_doc = True
+
+project = u'resolve'
+copyright = u'2019-2021, Max-Planck-Society'
+author = u'Philipp Arras'
+
+# FIXME release = resolve.version.__version__
+# FIXME version = release[:-2]
+
+language = None
+exclude_patterns = []
+add_module_names = False
+
+html_theme = "pydata_sphinx_theme"
+html_theme_options = {"gitlab_url": "https://gitlab.mpcdf.mpg.de/ift/resolve"}
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..dcfbb0f9f43a570e55d8872841fc69a4187aacba
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,17 @@
+Resolve
+=======
+
+Resolve is a Bayesian radio imaging algorithm.
+
+References
+----------
+
+FIXME
+
+Contents
+........
+
+.. toctree::
+   :maxdepth: 2
+
+   Package Documentation <mod/resolve>
diff --git a/resolve/__init__.py b/resolve/__init__.py
index fadc803f26d1ea7207a44529460770d600f58865..ba750ec666fc4375cd77e990c6f020a357a73103 100644
--- a/resolve/__init__.py
+++ b/resolve/__init__.py
@@ -1,5 +1,7 @@
+from .antenna_positions import AntennaPositions
 from .calibration import calibration_distribution
 from .constants import *
+from .direction import Direction
 from .fits import field2fits
 from .global_config import *
 from .likelihood import *
@@ -16,7 +18,7 @@ from .multi_frequency.operators import (
 from .observation import Observation, tmin_tmax, unique_antennas, unique_times
 from .plotter import MfPlotter, Plotter
 from .points import PointInserter
-from .polarization import polarization_matrix_exponential
+from .polarization import Polarization, polarization_matrix_exponential
 from .primary_beam import vla_beam
 from .response import MfResponse, ResponseDistributor, StokesIResponse, SingleResponse
 from .simple_operators import *
diff --git a/resolve/minimization.py b/resolve/minimization.py
index 75a60a897358c4cad75d30fa410b1778f169953a..a0e369598c844c28da8faae55f463ffc9b70d094 100644
--- a/resolve/minimization.py
+++ b/resolve/minimization.py
@@ -39,7 +39,7 @@ class Minimization:
                 "comm": comm,
                 "nanisinf": True,
             }
-            self._e = ift.MetricGaussianKL.make(**dct)
+            self._e = ift.MetricGaussianKL(**dct)
             self._n, self._m = dct["n_samples"], dct["mirror_samples"]
 
     def minimize(self, minimizer):
diff --git a/test/test_mpi/test_add.py b/test/test_mpi/test_add.py
index 2da1358dcd2512ff92f66547785e1d123002054a..edbac1621fa6b543832284877ddd41b84b9cd3e2 100644
--- a/test/test_mpi/test_add.py
+++ b/test/test_mpi/test_add.py
@@ -128,6 +128,6 @@ def test_mpi_adder():
         for ham in hams_for_sampling:
             with ift.random.Context(42):
                 mini_results.append(
-                    mini(ift.MetricGaussianKL.make(pos, ham, 3, True))[0].position
+                    mini(ift.MetricGaussianKL(pos, ham, 3, True))[0].position
                 )
         allclose(mini_results)