diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c3c6f926485897b20478549307d8833f0f296754..345a4fcace952aa508547961d2bc56c9e490910d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -191,6 +191,52 @@ build-package-2204:
     - if: '$CI_COMMIT_TAG'
     - if: '$UPLOAD_PACKAGE'
 
+.build-wheel:
+  stage: build
+  needs: []
+  image: docker:20.10
+  services:
+    - docker:20.10-dind
+  tags:
+    - das6
+  before_script:
+    - apk add bash
+  script:
+    - cd docker
+    - USER=root ./make_wheels.sh $PYTHON_VERSION
+  artifacts:
+    paths:
+    - output-*/*
+  rules:
+    - <<: *if_astron_repo
+  when: manual
+  allow_failure: true
+
+build-wheel-36:
+  extends: .build-wheel
+  variables:
+    PYTHON_VERSION: 36
+
+build-wheel-37:
+  extends: .build-wheel
+  variables:
+    PYTHON_VERSION: 37
+
+build-wheel-38:
+  extends: .build-wheel
+  variables:
+    PYTHON_VERSION: 38
+
+build-wheel-39:
+  extends: .build-wheel
+  variables:
+    PYTHON_VERSION: 39
+
+build-wheel-310:
+  extends: .build-wheel
+  variables:
+    PYTHON_VERSION: 310
+
 linting-2204:
   stage: linting
   extends: [".failable",".needs-base-2204"]
@@ -359,6 +405,35 @@ deploy-package-2204:
       when: never
     - if: '$UPLOAD_PACKAGE'
 
+.deploy-wheel:
+  stage: publish
+  image: python:latest
+  script:
+    - pip install twine
+    - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi output-*/*.whl
+  when: manual
+  allow_failure: true
+
+deploy-wheel-36:
+  extends: .deploy-wheel
+  needs: ["build-wheel-36"]
+
+deploy-wheel-37:
+  extends: .deploy-wheel
+  needs: ["build-wheel-37"]
+
+deploy-wheel-38:
+  extends: .deploy-wheel
+  needs: ["build-wheel-38"]
+
+deploy-wheel-39:
+  extends: .deploy-wheel
+  needs: ["build-wheel-39"]
+
+deploy-wheel-310:
+  extends: .deploy-wheel
+  needs: ["build-wheel-310"]
+
 pages-2204:
   stage: pages
   needs: ["versioning","build-debug-2204","unit-test-2204","integration-test-2204"]
@@ -385,6 +460,5 @@ pages-2204:
       coverage_report:
         coverage_format: cobertura
         path: public/build/reports/code-coverage.xml
-
   rules:
     - <<: *if_not_astron_repo
diff --git a/docker/make_wheels.sh b/docker/make_wheels.sh
index aa4b24bc6cbd57b3995517bacefd04d03cf16fe5..9ef5e9249cac43e59368d9c7696d72ae8a5f6497 100755
--- a/docker/make_wheels.sh
+++ b/docker/make_wheels.sh
@@ -1,25 +1,34 @@
-#!/usr/bin/bash
+#!/bin/bash
 
 # Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-# Script to make python wheels for several versions
-# The working directory matters, script should be run with ./make_wheels.sh
-# The parent directory should contain the source code with the version to build.
+# Script that makes python wheels for several versions.
+# The working directory matters since the parent directory should contain the
+# source code. The script should be run with
+# ./make_wheels.sh <python versions>
+# For example, when <python versions> equals 38 310, the script builds wheels
+# for python 3.8 and 3.10.
+# If <python versions> is empty, it becomes: 310 39 38 37 36 .
 
 set -euo pipefail
-for py_version in 310 39 38 37 36; do
+for py_version in ${@:-310 39 38 37 36}; do
     pushd ..
-    sed -i "s=\(master_wheel\)[0-9]*=\1${py_version}=" docker/py310_wheel.docker
-    grep wheel3 docker/py310_wheel.docker
+
+    # Use the Python3.10 dockerfile as a template, even for Python3.10.
+    dockerfile=$(mktemp make_wheels.docker.XXXXXX)
+    cat docker/py310_wheel.docker > $dockerfile
+    sed -i "s=\(casacore:master_wheel\)310=\1${py_version}=" $dockerfile
 
     ## Build docker image from docker-file. The current wheel is created there
-    #docker build -t dp3-py${py_version}-$USER -f docker/py310_wheel.docker .
+    docker build -t dp3-py${py_version}-$USER -f $dockerfile .
     ## Create a docker container from that image, and extract the wheel
-    #containerid=$(docker create dp3-py${py_version}-$USER)
-    #echo "Docker container ID is: $containerid"
-    #docker cp $containerid:/output/ output-${py_version}  # Copies whole dir
-    #docker rm ${containerid}
-    #docker image rm dp3-py${py_version}-$USER
+    containerid=$(docker create dp3-py${py_version}-$USER)
+    echo "Docker container ID is: $containerid"
+    docker cp $containerid:/output/ output-${py_version}  # Copies whole dir
+    docker rm ${containerid}
+    docker image rm dp3-py${py_version}-$USER
+
+    rm $dockerfile
     popd
 done
diff --git a/docker/py310_wheel.docker b/docker/py310_wheel.docker
index b48fcbb7a6db12a05e2d515b2fe97052e972559d..c00aa273c7394b61dc84454b97cab10827b586f3 100644
--- a/docker/py310_wheel.docker
+++ b/docker/py310_wheel.docker
@@ -50,6 +50,6 @@ RUN sed -i '/find_package(PythonLibs 3 REQUIRED)/d' CMakeLists.txt
 # and to remove the previous wheel when changing parameters that causes a new
 # wheel filename e.g. changing the version number.
 RUN /opt/python/${TARGET}/bin/python ./setup.py build_ext -j${THREADS}
-RUN mv /dp3/build/temp.linux-x86_64-cpython-39/dp3/DP3 /dp3/build/temp.linux-x86_64-cpython-39/dp3/__DP3_from_pip__
+RUN mv /dp3/build/temp.linux-x86_64-*/dp3/DP3 /dp3/build/__DP3_from_pip__
 RUN /opt/python/${TARGET}/bin/python ./setup.py bdist_wheel -d .
 RUN auditwheel repair --plat manylinux2014_x86_64 -w /output *.whl
diff --git a/setup.py b/setup.py
index 9b5ad22cbf02b5d20fbeac7270eb0224a9004202..4a1b37ed8bfa0e58b7ecdd714106f66f4543eca3 100755
--- a/setup.py
+++ b/setup.py
@@ -130,7 +130,7 @@ setup(
         # LD_PRELOAD=/path/to/lib/libpython3.9.so __DP3_from_pip__
         (
             "bin",
-            ["/dp3/build/temp.linux-x86_64-cpython-39/dp3/__DP3_from_pip__"],
+            ["build/__DP3_from_pip__"],
         ),
     ],
     url="https://dp3.readthedocs.io/",