diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 358ed142577879e6161f09a66b64c6efe341715f..52547c6a82f8b9181dadca224b3ef1f5a8c9e678 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,6 +11,9 @@ stages:
   tags: [linux, docker]
   image: git.imp.fu-berlin.de:5000/bioroboticslab/auto/ci/centos:latest
 
+.macos:
+  tags: [macos, shell]
+
 .windows:
   tags: [windows, docker]
   image: git.imp.fu-berlin.de:5000/bioroboticslab/auto/ci/windows:latest-devel
@@ -59,6 +62,10 @@ stages:
   extends: .centos
   <<: *build_cpp
 
+"build cpp: [macos]":
+  extends: .macos
+  <<: *build_cpp
+
 "build cpp: [windows]":
   extends: .windows
   <<: *build_cpp
@@ -81,6 +88,24 @@ stages:
   variables:
     <<: [*python39]
 
+"build python: [macos, 3.7]":
+  extends: .macos
+  <<: *build_python
+  variables:
+    <<: [*python37]
+
+"build python: [macos, 3.8]":
+  extends: .macos
+  <<: *build_python
+  variables:
+    <<: [*python38]
+
+"build python: [macos, 3.9]":
+  extends: .macos
+  <<: *build_python
+  variables:
+    <<: [*python39]
+
 "build python: [windows, 3.7]":
   extends: .windows
   <<: *build_python
@@ -128,6 +153,12 @@ stages:
     - "build cpp: [centos]"
   <<: *package_cpp
 
+"package cpp: [macos]":
+  extends: .macos
+  dependencies:
+    - "build cpp: [macos]"
+  <<: *package_cpp
+
 "package cpp: [windows]":
   extends: .windows
   dependencies:
@@ -152,6 +183,24 @@ stages:
     - "build python: [centos, 3.9]"
   <<: *package_python
 
+"package python: [macos, 3.7]":
+  extends: .macos
+  dependencies:
+    - "build python: [macos, 3.7]"
+  <<: *package_python
+
+"package python: [macos, 3.8]":
+  extends: .macos
+  dependencies:
+    - "build python: [macos, 3.8]"
+  <<: *package_python
+
+"package python: [macos, 3.9]":
+  extends: .macos
+  dependencies:
+    - "build python: [macos, 3.9]"
+  <<: *package_python
+
 "package python: [windows, 3.7]":
   extends: .windows
   dependencies:
@@ -181,6 +230,9 @@ deploy python to staging:
     - "package python: [centos, 3.7]"
     - "package python: [centos, 3.8]"
     - "package python: [centos, 3.9]"
+    - "package python: [macos, 3.7]"
+    - "package python: [macos, 3.8]"
+    - "package python: [macos, 3.9]"
     - "package python: [windows, 3.7]"
     - "package python: [windows, 3.8]"
     - "package python: [windows, 3.9]"
@@ -196,6 +248,9 @@ deploy python to production:
     - "package python: [centos, 3.7]"
     - "package python: [centos, 3.8]"
     - "package python: [centos, 3.9]"
+    - "package python: [macos, 3.7]"
+    - "package python: [macos, 3.8]"
+    - "package python: [macos, 3.9]"
     - "package python: [windows, 3.7]"
     - "package python: [windows, 3.8]"
     - "package python: [windows, 3.9]"
diff --git a/ci/prepare.py b/ci/prepare.py
index 814c552251543925dc0bdbd6d148cb25a473ab9b..6f7336e4762a9940c7fef85a3f300887f07346d6 100755
--- a/ci/prepare.py
+++ b/ci/prepare.py
@@ -48,6 +48,8 @@ if __name__ == "__main__":
         os_name = "windows"
     elif system() == "Linux":
         os_name = "centos"
+    elif system() == "Darwin":
+        os_name = "macos"
     else:
         assert False
 
diff --git a/python/ci/build.py b/python/ci/build.py
index 3a8235747e8d006a1af505e9b0d89fe864579114..342f3ad1cb62f8965bb3c28bdc4b2f566b18bdaf 100755
--- a/python/ci/build.py
+++ b/python/ci/build.py
@@ -11,7 +11,7 @@ from shutil import which
 def python_executable():
     if system() == "Windows":
         return f"/Python{''.join(env['PYTHON_VERSION'].split('.'))}/python.exe"
-    elif system() == "Linux":
+    elif system() == "Linux" or system() == "Darwin":
         return which(f"python{env['PYTHON_VERSION']}")
     assert False
 
@@ -28,10 +28,13 @@ if __name__ == "__main__":
             "gmpxx.so.4",
             "mpfr.so.4",
         ]
+    elif system() == "Darwin":
+        shared_libraries = []
     else:
         assert False
 
-    env["INSTALL_SHARED_LIBRARIES"] = ";".join(shared_libraries)
+    if len(shared_libraries) > 0:
+        env["INSTALL_SHARED_LIBRARIES"] = ";".join(shared_libraries)
 
     env["CMAKE_PREFIX_PATH"] = str(Path("vendor").resolve())
     env["CMAKE_GENERATOR"] = "Ninja"
diff --git a/python/ci/package.py b/python/ci/package.py
index 5d327a16d89c7d1d3121a9f70c1fc9cd543f506a..fc3937ec9b6b7279e0f26c025ab5de5adde2d4df 100755
--- a/python/ci/package.py
+++ b/python/ci/package.py
@@ -11,7 +11,7 @@ from shutil import which
 def python_executable():
     if system() == "Windows":
         return f"/Python{''.join(env['PYTHON_VERSION'].split('.'))}/python.exe"
-    elif system() == "Linux":
+    elif system() == "Linux" or system() == "Darwin":
         return which(f"python{env['PYTHON_VERSION']}")
     assert False
 
diff --git a/python/ci/test.py b/python/ci/test.py
index 6b70a71d4e3c7128c8cbeda5571cc57be1f7fb27..f304e4e6dc6848e1d30c6a9c58079fb7c51bcaa4 100755
--- a/python/ci/test.py
+++ b/python/ci/test.py
@@ -11,7 +11,7 @@ from shutil import which
 def python_executable():
     if system() == "Windows":
         return f"/Python{''.join(env['PYTHON_VERSION'].split('.'))}/python.exe"
-    elif system() == "Linux":
+    elif system() == "Linux" or system() == "Darwin":
         return which(f"python{env['PYTHON_VERSION']}")
     assert False
 
@@ -19,7 +19,7 @@ def python_executable():
 def python_venv_executable():
     if system() == "Windows":
         return str(Path("python/.venv/Scripts/python.exe").resolve())
-    elif system() == "Linux":
+    elif system() == "Linux" or system() == "Darwin":
         return str(Path("python/.venv/bin/python").resolve())
     assert False