diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index 1e3e3389e511db88fe3da2e5731581de9d36f48c..eb441afa3d8b646f116e9ae8475ac45914c5b25c 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -2,17 +2,40 @@ FROM debian:latest
 
 RUN echo "deb http://deb.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/buster-backports.list
 RUN apt-get update
-RUN apt-get install -y cmake
+RUN apt-get install -y cmake/buster-backports
 RUN apt-get install -y lcov
 RUN apt-get install -y doxygen
 RUN apt-get install -y clang-format-11 clang-tidy-11
 RUN apt-get install -y python3-pip
+RUN apt-get install -y git
 
 COPY doc/requirements.txt requirements.txt
 RUN pip3 install -r requirements.txt
 
-RUN echo "deb http://ftp.de.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/buster-backports.list
-RUN apt-get update
-RUN apt-get install -y cmake/buster-backports
-RUN apt-get install -y git
+RUN apt-get install -y build-essential autoconf libtool pkg-config
+RUN git clone --recurse-submodules -b v1.38.0 https://github.com/grpc/grpc
+
+WORKDIR /grpc
+RUN mkdir -p cmake/build
+WORKDIR cmake/build
+RUN cmake -DgRPC_INSTALL=ON \
+      -DgRPC_BUILD_CSHARP_EXT=OFF \
+      -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
+      -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=OFF \
+      -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
+      -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \
+      -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF \
+      -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF \
+      -DgRPC_BUILD_TESTS=OFF \
+      ../..
+RUN make -j
+RUN make install
+WORKDIR /grpc
+RUN mkdir -p third_party/abseil-cpp/cmake/build
+WORKDIR third_party/abseil-cpp/cmake/build
+RUN cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
+      ../..
+RUN make -j
+RUN make install
 
+# TODO build stuff
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index eca036b6000cb3eb5544adfbb58c35c2811abb50..b0278e2c63a02b6291c05cf45576a3169837331d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -60,10 +60,9 @@ test:
   tags: [ docker ]
   stage: test
   script:
-    - cmake --version
     - mkdir build
     - cd build
-    - cmake -DCMAKE_BUILD_TYPE=Debug ..
+    - cmake -DGRPC_FETCHCONTENT=OFF -DCMAKE_BUILD_TYPE=Debug ..
     - cmake --build .
     - cmake --build . --target unit_test_coverage
 
@@ -87,5 +86,4 @@ pages_prepare: &pages_prepare
 pages:
   <<: *pages_prepare
   only:
-    refs:
-      - main
+    - schedule
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 58816b641b28fd93cb016599bd39d311af628521..287c2580dd1edfcb380829a2b2d4ce49a3f1f717 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -112,33 +112,34 @@ target_include_directories(caosdb PUBLIC
    )
 
 
-## Install libcaosdb in CMAKE_INSTALL_PREFIX (defaults to /usr/local on linux).
-## To change the install location, run
-##   cmake -DCMAKE_INSTALL_PREFIX=<desired-install-path> ..
-
-## install(...) specifies installation rules for the project. It can specify
-## location of installed files on the system, user permissions, build
-## configurations, etc. Here, we are only copying files.
-## install(TARGETS ...) specifies rules for installing targets.
-## Here, we are taking a target or list of targets (libcosdb) and telling
-## CMake the following:
-##   - put shared libraries associated with libcaosdb in ${libcaosdb_LIB_DEST}
-##   - put static libraries associated with libcaosdb in ${libcaosdb_LIB_DEST}
-##   - put include files associated with libcaosdb in ${libcaosdb_INCLUDE_DEST}
-## We also need to specify the export that is associated with libcaosdb; an
-## export is just a list of targets to be installed. So we are associating
-## libcaosdb with libcaosdbTargets.
-#install(
-    ## targets to install
-    #TARGETS caosdb
-    ## name of the CMake "export group" containing the targets we want to install
+set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/libcaosdb/")
+# Install libcaosdb in CMAKE_INSTALL_PREFIX (defaults to /usr/local on linux).
+# To change the install location, run
+#   cmake -DCMAKE_INSTALL_PREFIX=<desired-install-path> ..
+
+# install(...) specifies installation rules for the project. It can specify
+# location of installed files on the system, user permissions, build
+# configurations, etc. Here, we are only copying files.
+# install(TARGETS ...) specifies rules for installing targets.
+# Here, we are taking a target or list of targets (libcosdb) and telling
+# CMake the following:
+#   - put shared libraries associated with libcaosdb in ${libcaosdb_LIB_DEST}
+#   - put static libraries associated with libcaosdb in ${libcaosdb_LIB_DEST}
+#   - put include files associated with libcaosdb in ${libcaosdb_INCLUDE_DEST}
+# We also need to specify the export that is associated with libcaosdb; an
+# export is just a list of targets to be installed. So we are associating
+# libcaosdb with libcaosdbTargets.
+install(
+    # targets to install
+    TARGETS caosdb
+    # name of the CMake "export group" containing the targets we want to install
     #EXPORT libcaosdbTargets
-    ## Dynamic, static library and include destination locations after running
-    ## "make install"
-    #LIBRARY DESTINATION ${libcaosdb_LIB_DEST}
-    #ARCHIVE DESTINATION ${libcaosdb_LIB_DEST}
+    # Dynamic, static library and include destination locations after running
+    # "make install"
+    LIBRARY DESTINATION ${libcaosdb_LIB_DEST}
+    ARCHIVE DESTINATION ${libcaosdb_LIB_DEST}
     #INCLUDES DESTINATION ${libcaosdb_INCLUDE_DEST}
-    #)
+    )
 
 
 # We now need to install the export libcaosdbTargets that we defined above.
@@ -166,11 +167,12 @@ target_include_directories(caosdb PUBLIC
 
 ## install(FILES ...) simply puts files in a certain place with certain
 ## properties. We're just copying them to the desired place here.
-#install(FILES ${libcaosdb_INC} DESTINATION ${libcaosdb_INCLUDE_DEST})
+install(FILES ${libcaosdb_INC} DESTINATION ${libcaosdb_INCLUDE_DEST})
 
 
 
 ## CODE GENERATION
+option(GRPC_FETCHCONTENT "Fetch and build GRPC from the sources" ON)
 include(FetchGRPC)
 
 #
diff --git a/cmake/FetchGRPC.cmake b/cmake/FetchGRPC.cmake
index ca76b5c9fdf3dcb0c727b74354cada9aa1ca1b1c..e9af5ffa8fb167d78a9af43e81ed56cbe40ec93e 100644
--- a/cmake/FetchGRPC.cmake
+++ b/cmake/FetchGRPC.cmake
@@ -25,18 +25,6 @@ endif()
 
 find_package(Threads REQUIRED)
 
-# Another way is to use CMake's FetchContent module to clone gRPC at
-# configure time. This makes gRPC's source code available to your project,
-# similar to a git submodule.
-message(STATUS "Using gRPC via add_subdirectory (FetchContent).")
-include(FetchContent)
-FetchContent_Declare(
-  grpc
-  GIT_REPOSITORY  https://github.com/grpc/grpc.git
-  GIT_TAG         v1.38.0
-  GIT_SHALLOW     1
-)
-
 # disable a lot of stuff
 set(gRPC_BUILD_TESTS OFF)
 set(gRPC_BUILD_CSHARP_EXT OFF)
@@ -47,22 +35,76 @@ set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF)
 set(gRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN OFF)
 set(gRPC_BUILD_GRPC_NODE_PLUGIN OFF)
 set(protobuf_BUILD_TESTS OFF)
+set(vGRPC_TAG_VERSION_OF_YOUR_CHOICE "v1.38.0")
 
-FetchContent_MakeAvailable(grpc)
+if(GRPC_AS_SUBMODULE)
+  # One way to build a projects that uses gRPC is to just include the
+  # entire gRPC project tree via "add_subdirectory".
+  # This approach is very simple to use, but the are some potential
+  # disadvantages:
+  # * it includes gRPC's CMakeLists.txt directly into your build script
+  #   without and that can make gRPC's internal setting interfere with your
+  #   own build.
+  # * depending on what's installed on your system, the contents of submodules
+  #   in gRPC's third_party/* might need to be available (and there might be
+  #   additional prerequisites required to build them). Consider using
+  #   the gRPC_*_PROVIDER options to fine-tune the expected behavior.
+  #
+  # A more robust approach to add dependency on gRPC is using
+  # cmake's ExternalProject_Add (see cmake_externalproject/CMakeLists.txt).
 
-# Since FetchContent uses add_subdirectory under the hood, we can use
-# the grpc targets directly from this build.
-set(_PROTOBUF_LIBPROTOBUF libprotobuf)
-set(_REFLECTION grpc++_reflection)
-set(_PROTOBUF_PROTOC $<TARGET_FILE:protoc>)
-set(_GRPC_GRPCPP grpc++)
-if(CMAKE_CROSSCOMPILING)
-find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
-else()
-set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
-endif()
+  # Include the gRPC's cmake build (normally grpc source code would live
+  # in a git submodule called "third_party/grpc", but this example lives in
+  # the same repository as gRPC sources, so we just look a few directories up)
+  add_subdirectory(../../.. ${CMAKE_CURRENT_BINARY_DIR}/grpc EXCLUDE_FROM_ALL)
+  message(STATUS "Using gRPC via add_subdirectory.")
 
-set_target_properties(
+  # After using add_subdirectory, we can now use the grpc targets directly from
+  # this build.
+  set(_PROTOBUF_LIBPROTOBUF libprotobuf)
+  set(_REFLECTION grpc++_reflection)
+  if(CMAKE_CROSSCOMPILING)
+    find_program(_PROTOBUF_PROTOC protoc)
+  else()
+    set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
+  endif()
+  set(_GRPC_GRPCPP grpc++)
+  if(CMAKE_CROSSCOMPILING)
+    find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
+  else()
+    set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
+  endif()
+elseif(GRPC_FETCHCONTENT)
+  # Another way is to use CMake's FetchContent module to clone gRPC at
+  # configure time. This makes gRPC's source code available to your project,
+  # similar to a git submodule.
+  message(STATUS "Using gRPC via add_subdirectory (FetchContent).")
+  include(FetchContent)
+  FetchContent_Declare(
+    grpc
+    GIT_REPOSITORY https://github.com/grpc/grpc.git
+    # when using gRPC, you will actually set this to an existing tag, such as
+    # v1.25.0, v1.26.0 etc..
+    # For the purpose of testing, we override the tag used to the commit
+    # that's currently under test.
+    GIT_TAG        vGRPC_TAG_VERSION_OF_YOUR_CHOICE
+    GIT_SHALLOW     1)
+  FetchContent_MakeAvailable(grpc)
+
+  # Since FetchContent uses add_subdirectory under the hood, we can use
+  # the grpc targets directly from this build.
+  set(_PROTOBUF_LIBPROTOBUF libprotobuf)
+  set(_REFLECTION grpc++_reflection)
+  set(_PROTOBUF_PROTOC $<TARGET_FILE:protoc>)
+  set(_GRPC_GRPCPP grpc++)
+  if(CMAKE_CROSSCOMPILING)
+    find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
+  else()
+    set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
+  endif()
+
+  ## disable more unused stuff
+  set_target_properties(
     zlibstatic
     libprotobuf
     libprotoc
@@ -184,4 +226,36 @@ set_target_properties(
     PROPERTIES
     CXX_CLANG_TIDY ""
     CXX_INCLUDE_WHAT_YOU_USE ""
-    EXCLUDE_FROM_ALL 1)
+    EXCLUDE_FROM_ALL 1
+  )
+else()
+  # This branch assumes that gRPC and all its dependencies are already installed
+  # on this system, so they can be located by find_package().
+
+  # Find Protobuf installation
+  # Looks for protobuf-config.cmake file installed by Protobuf's cmake installation.
+  set(protobuf_MODULE_COMPATIBLE TRUE)
+  find_package(Protobuf CONFIG REQUIRED)
+  message(STATUS "Using protobuf ${Protobuf_VERSION}")
+
+  set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
+  set(_REFLECTION gRPC::grpc++_reflection)
+  if(CMAKE_CROSSCOMPILING)
+    find_program(_PROTOBUF_PROTOC protoc)
+  else()
+    set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
+  endif()
+
+  # Find gRPC installation
+  # Looks for gRPCConfig.cmake file installed by gRPC's cmake installation.
+  find_package(gRPC CONFIG REQUIRED)
+  message(STATUS "Using gRPC ${gRPC_VERSION}")
+
+  set(_GRPC_GRPCPP gRPC::grpc++)
+  if(CMAKE_CROSSCOMPILING)
+    find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
+  else()
+    set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
+  endif()
+endif()
+