diff --git a/.docker/Dockerfile b/.docker/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..7447d487dd6ee93ceb6e9ca4fa860da5c341d20e
--- /dev/null
+++ b/.docker/Dockerfile
@@ -0,0 +1,24 @@
+ARG JULIALIB_REGISTRY_IMAGE
+FROM $JULIALIB_REGISTRY_IMAGE
+
+COPY .docker/wait-for-it.sh /wait-for-it.sh
+
+# build an install cpplib
+ARG F_BRANCH
+# Also need the `F_BRANCH` in the running container
+ENV F_BRANCH=$F_BRANCH
+ARG CPP_DEFAULT_BRANCH
+COPY .docker/install_cpplib.sh /install_cpplib.sh
+WORKDIR /
+RUN echo "F_BRANCH=$F_BRANCH"
+RUN echo "CPP_DEFAULT_BRANCH=$CPP_DEFAULT_BRANCH"
+RUN ./install_cpplib.sh
+
+COPY . /caosdb-juliainttest
+WORKDIR /caosdb-juliainttest
+
+# Set-up and run the integration test
+CMD export LD_LIBRARY_PATH=/root/.local/lib:$LD_LIBRARY_PATH && \
+    echo "F_BRANCH = $F_BRANCH" && \
+    /wait-for-it.sh caosdb-server:10443 -t 500 -- \
+    julia -e 'using Pkg; Pkg.add(path=pwd()); Pkg.test("CaosDBIntegrationTests")'
\ No newline at end of file
diff --git a/.docker/cert.sh b/.docker/cert.sh
new file mode 100755
index 0000000000000000000000000000000000000000..efc660ba784924cca0b21515d3c846348b1fa628
--- /dev/null
+++ b/.docker/cert.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2019 Daniel Hornung, Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+
+
+# Creates a directory `cert` and certificates in this directory.
+#
+# The hostname for which the certificate is created can be changed by setting
+# the environment variable CAOSHOSTNAME.
+#
+# ## Overview of variables ##
+#
+# - CAOSHOSTNAME :: Hostname for the key (localhost)
+# - KEYPW :: Password for the key (default ist CaosDBSecret)
+# - KEYSTOREPW :: Password for the key store (same as KEYPW)
+function cert() {
+    mkdir -p cert
+    cd cert
+    KEYPW="${KEYPW:-CaosDBSecret}"
+    CAOSHOSTNAME="${CAOSHOSTNAME:-localhost}"
+    KEYSTOREPW="${KEYPW:-}"
+    # NOTE: KEYPW and KEYSTOREPW are the same, due to Java limitations.
+    KEYPW="${KEYPW}" openssl genrsa -aes256 -out caosdb.key.pem \
+         -passout env:KEYPW 2048
+    # Certificate is for localhost
+    KEYPW="${KEYPW}" openssl req -new -x509 -key caosdb.key.pem \
+         -out caosdb.cert.pem -passin env:KEYPW \
+         -addext "subjectAltName = DNS:${CAOSHOSTNAME}" \
+         -subj "/C=/ST=/L=/O=/OU=/CN=${CAOSHOSTNAME}"
+    KEYPW="${KEYPW}" KEYSTOREPW="$KEYSTOREPW" openssl pkcs12 -export \
+         -inkey caosdb.key.pem -in caosdb.cert.pem -out all-certs.pkcs12 \
+         -passin env:KEYPW -passout env:KEYPW
+
+    keytool -importkeystore -srckeystore all-certs.pkcs12 -srcstoretype PKCS12 \
+            -deststoretype pkcs12 -destkeystore caosdb.jks \
+            -srcstorepass "${KEYPW}" \
+            -destkeypass "${KEYPW}" -deststorepass "$KEYSTOREPW"
+    echo "Certificates successfuly created."
+}
+
+cert
diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9fd73819f8b78fc855344ed732c72c0fd959d4e
--- /dev/null
+++ b/.docker/docker-compose.yml
@@ -0,0 +1,46 @@
+version: '3.7'
+services:
+  sqldb:
+    image: mariadb:10.4
+    environment:
+      MYSQL_ROOT_PASSWORD: caosdb1234
+    networks:
+      - caosnet
+  caosdb-server:
+    image: "$CI_REGISTRY/caosdb/src/caosdb-deploy:$CAOSDB_TAG"
+    user: 999:999
+    depends_on:
+      - sqldb
+    networks:
+      - caosnet
+    volumes:
+      - type: bind
+        source: ./cert
+        target: /opt/caosdb/cert
+      - type: volume
+        source: extroot
+        target: /opt/caosdb/mnt/extroot
+      - type: volume
+        source: scripting
+        target: /opt/caosdb/git/caosdb-server/scripting
+      - type: volume
+        source: authtoken
+        target: /opt/caosdb/git/caosdb-server/authtoken
+    ports:
+      # - "from_outside:from_inside"
+      - "10443:10443"
+      - "10080:10080"
+      - "8080:8080"
+      - "8443:8443"
+    environment:
+      DEBUG: 1
+      CAOSDB_CONFIG_AUTHTOKEN_CONFIG: "conf/core/authtoken.example.yaml"
+      CAOSDB_CONFIG_GRPC_SERVER_PORT_HTTPS: 8443
+      CAOSDB_CONFIG_GRPC_SERVER_PORT_HTTP: 8080
+volumes:
+  scripting:
+  extroot:
+  authtoken:
+networks:
+  caosnet:
+    driver: bridge
diff --git a/.docker/install_cpplib.sh b/.docker/install_cpplib.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f111d28459bbeb8e658ebbaef51a9b6a86ba1c1f
--- /dev/null
+++ b/.docker/install_cpplib.sh
@@ -0,0 +1,19 @@
+
+#!/bin/bash
+
+git clone https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git
+cd caosdb-cpplib
+# check if there is a crooesponding cpp branch. Use the default branch
+# if there isn't.
+if (git show-ref --verify --quiet refs/heads/$F_BRANCH) ; then
+    git checkout $F_BRANCH
+else
+    git checkout $CPP_DEFAULT_BRANCH
+fi
+git submodule update --init --recursive
+mkdir build
+cd build
+conan install .. -s "compiler.libcxx=libstdc++11"
+cmake ..
+cmake --build .
+cmake --install .
diff --git a/.docker/run.sh b/.docker/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a1d3d35f7c54c5de6f7ff841a6ccc565868d1502
--- /dev/null
+++ b/.docker/run.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+F_BRANCH=$F_BRANCH docker-compose -f tester.yml run tester
+rv=$?
+echo $rv > result
diff --git a/.docker/tester.yml b/.docker/tester.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6f003b3f3d028b84fb7e16213822cecbcbdc4a2c
--- /dev/null
+++ b/.docker/tester.yml
@@ -0,0 +1,30 @@
+version: '3.7'
+services:
+  tester:
+    image: "$JULIAINTTEST_REGISTRY_IMAGE"
+    environment:
+      CAOSDB_SERVER_HOST: caosdb-server
+      CAOSDB_SERVER_CERT: /cert/caosdb.cert.pem
+      F_BRANCH: $F_BRANCH
+    networks:
+      - docker_caosnet
+    volumes:
+      - type: bind
+        source: ./cert
+        target: /cert
+      - type: volume
+        source: extroot
+        target: /extroot
+      - type: volume
+        source: scripting
+        target: /scripting
+      - type: volume
+        source: authtoken
+        target: /authtoken
+networks:
+  docker_caosnet:
+    external: true
+volumes:
+  scripting:
+  extroot:
+  authtoken:
diff --git a/.docker/wait-for-it.sh b/.docker/wait-for-it.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d69e99f1f13257b559dce2433de0515379663efa
--- /dev/null
+++ b/.docker/wait-for-it.sh
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+# License: 
+# From https://github.com/vishnubob/wait-for-it
+# The MIT License (MIT)
+# Use this script to test if a given TCP host/port are available
+
+WAITFORIT_cmdname=${0##*/}
+
+echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
+
+usage()
+{
+    cat << USAGE >&2
+Usage:
+    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
+    -h HOST | --host=HOST       Host or IP under test
+    -p PORT | --port=PORT       TCP port under test
+                                Alternatively, you specify the host and port as host:port
+    -s | --strict               Only execute subcommand if the test succeeds
+    -q | --quiet                Don't output any status messages
+    -t TIMEOUT | --timeout=TIMEOUT
+                                Timeout in seconds, zero for no timeout
+    -- COMMAND ARGS             Execute command with args after the test finishes
+USAGE
+    exit 1
+}
+
+wait_for()
+{
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    else
+        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
+    fi
+    WAITFORIT_start_ts=$(date +%s)
+    while :
+    do
+        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
+            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
+            WAITFORIT_result=$?
+        else
+            (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
+            WAITFORIT_result=$?
+        fi
+        if [[ $WAITFORIT_result -eq 0 ]]; then
+            WAITFORIT_end_ts=$(date +%s)
+            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
+            break
+        fi
+        sleep 1
+    done
+    return $WAITFORIT_result
+}
+
+wait_for_wrapper()
+{
+    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
+    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    else
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    fi
+    WAITFORIT_PID=$!
+    trap "kill -INT -$WAITFORIT_PID" INT
+    wait $WAITFORIT_PID
+    WAITFORIT_RESULT=$?
+    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    fi
+    return $WAITFORIT_RESULT
+}
+
+# process arguments
+while [[ $# -gt 0 ]]
+do
+    case "$1" in
+        *:* )
+        WAITFORIT_hostport=(${1//:/ })
+        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
+        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
+        shift 1
+        ;;
+        --child)
+        WAITFORIT_CHILD=1
+        shift 1
+        ;;
+        -q | --quiet)
+        WAITFORIT_QUIET=1
+        shift 1
+        ;;
+        -s | --strict)
+        WAITFORIT_STRICT=1
+        shift 1
+        ;;
+        -h)
+        WAITFORIT_HOST="$2"
+        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
+        shift 2
+        ;;
+        --host=*)
+        WAITFORIT_HOST="${1#*=}"
+        shift 1
+        ;;
+        -p)
+        WAITFORIT_PORT="$2"
+        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --port=*)
+        WAITFORIT_PORT="${1#*=}"
+        shift 1
+        ;;
+        -t)
+        WAITFORIT_TIMEOUT="$2"
+        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --timeout=*)
+        WAITFORIT_TIMEOUT="${1#*=}"
+        shift 1
+        ;;
+        --)
+        shift
+        WAITFORIT_CLI=("$@")
+        break
+        ;;
+        --help)
+        usage
+        ;;
+        *)
+        echoerr "Unknown argument: $1"
+        usage
+        ;;
+    esac
+done
+
+if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
+    echoerr "Error: you need to provide a host and port to test."
+    usage
+fi
+
+WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
+WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
+WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
+WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
+
+# check to see if timeout is from busybox?
+WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
+WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
+if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
+        WAITFORIT_ISBUSY=1
+        WAITFORIT_BUSYTIMEFLAG="-t"
+
+else
+        WAITFORIT_ISBUSY=0
+        WAITFORIT_BUSYTIMEFLAG=""
+fi
+
+if [[ $WAITFORIT_CHILD -gt 0 ]]; then
+    wait_for
+    WAITFORIT_RESULT=$?
+    exit $WAITFORIT_RESULT
+else
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        wait_for_wrapper
+        WAITFORIT_RESULT=$?
+    else
+        wait_for
+        WAITFORIT_RESULT=$?
+    fi
+fi
+
+if [[ $WAITFORIT_CLI != "" ]]; then
+    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
+        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
+        exit $WAITFORIT_RESULT
+    fi
+    exec "${WAITFORIT_CLI[@]}"
+else
+    exit $WAITFORIT_RESULT
+fi
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6df76ea5774835059d7fa66dd217cf8a4feeb9c7
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,207 @@
+#
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <https://www.gnu.org/licenses/>.
+
+variables:
+  # Image from CaosDB.jl pipeline (note that caosdb-cpplib may have to
+  # be rebuilt.)
+  JULIALIB_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/src/caosdb-julialib/testenv:$CI_COMMIT_REF_NAME
+  # Image where all the CaosDB containers are started
+  CI_REGISTRY_IMAGE_BASE: $CI_REGISTRY/caosdb/src/caosdb-pyinttest/base:latest
+
+  # Image in which the tests are run
+  JULIAINTTEST_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/src/caosdb-juliainttest/testenv:$CI_COMMIT_REF_NAME
+  # Name of the image in the /image-cache/ directory
+  JULIAINTTEST_IMAGE_CACHE: caosdb-juliainttest-testenv:$CI_COMMIT_REF_NAME.tar
+
+  ## FOR DEBUGGING
+  TRIGGERED_BY_REPO: JULIAINTTEST
+  TRIGGERED_BY_REF: $CI_COMMIT_REF_NAME
+  TRIGGERED_BY_HASH: $CI_COMMIT_SHORT_SHA
+
+  # This is the caosdb-deploy branch which build the images for caosdb-server and caosdb-mysql
+  # TODO change to dev after merge of f-grpc to dev
+  DEPLOY_REF: f-grpc
+  DEFAULT_CAOSDB_TAG: f-grpc
+
+  # The defalt branch to use with caosdb-cpplib
+  CPP_DEFAULT_BRANCH: f-extern-c
+  
+stages:
+  - info
+  - code-style
+  - setup
+  - cert
+  - test
+
+info:
+  tags: [ docker ]
+  image: docker:20.10
+  stage: info
+  needs: []
+  script:
+    - echo "Pipeline triggered by $TRIGGERED_BY_REPO@$TRIGGERED_BY_REF ($TRIGGERED_BY_HASH)"
+    - echo "JULIALIB_REGISTRY_IMAGE = $JULIALIB_REGISTRY_IMAGE"
+    - echo "CI_REGISTRY_IMAGE_BASE = $CI_REGISTRY_IMAGE_BASE"
+    - echo "JULIAINTTEST_REGISTRY_IMAGE = $JULIAINTTEST_REGISTRY_IMAGE"
+    - echo "JULIAINTTEST_IMAGE_CACHE = $JULIAINTTEST_IMAGE_CACHE"
+    - echo "CAOSDB_TAG = $CAOSDB_TAG"
+    - echo "REFTAG = $REFTAG"
+    - echo "F_BRANCH = $F_BRANCH"
+    - ls -lah /image-cache/
+
+code-style:
+  stage: code-style
+  image: julia:1.6
+  tags: [ docker ]
+  script:
+    # install git
+    - apt-get update && apt-get install -y git
+    # install JuliaFormatter
+    - julia  -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter"))'
+    # try and format the files
+    - julia  -e 'using JuliaFormatter; format(".", verbose=true)'
+    # check the git diff for possible changes due to the formatting
+    - julia -e '
+      out = Cmd(`git diff --name-only`) |> read |> String;
+      if out == ""
+          exit(0);
+      else
+          @error "Some files have not been formatted !!!";
+          write(stdout, out);
+          exit(1);
+      end'
+  allow_failure: true
+
+build-testenv: &build-testenv
+  tags: [ cached-dind ]
+  image: docker:20.10.6
+  stage: setup
+  timeout: 2h
+  needs: []
+  script:
+    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+    - docker pull $JULIALIB_REGISTRY_IMAGE || true
+    - docker build
+      --build-arg JULIALIB_REGISTRY_IMAGE=$JULIALIB_REGISTRY_IMAGE
+      --build-arg F_BRANCH=$F_BRANCH
+      --build-arg CPP_DEFAULT_BRANCH=$CPP_DEFAULT_BRANCH
+      --file .docker/Dockerfile
+      --pull
+      --cache-from $JULIAINTTEST_REGISTRY_IMAGE
+      --tag $JULIAINTTEST_REGISTRY_IMAGE .
+    - docker push $JULIAINTTEST_REGISTRY_IMAGE
+    - docker save $JULIAINTTEST_REGISTRY_IMAGE > /image-cache/${JULIAINTTEST_IMAGE_CACHE}
+
+cert:
+  tags: [ docker ]
+  stage: cert
+  image: $JULIAINTTEST_REGISTRY_IMAGE
+  artifacts:
+    paths:
+      - .docker/cert/
+    expire_in: 1 days
+  script:
+    - cd .docker
+    - CAOSHOSTNAME=caosdb-server ./cert.sh
+
+test:
+  tags: [ cached-dind ]
+  services:
+    - docker:20.10.3-dind
+  variables:
+    # This is a workaround for the gitlab-runner health check
+    # mechanism when using docker-dind service. The runner will
+    # otherwise guess the port wrong and the health check will
+    # timeout.
+    SERVICE_PORT_2376_TCP_PORT: 2375
+  stage: test
+  image: $CI_REGISTRY_IMAGE_BASE
+  needs:
+    - job: cert
+      artifacts: true
+  script:
+    - echo $PWD
+    - ls -la
+    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+    - if [[ "$CAOSDB_TAG" == "" ]]; then
+        if echo "$F_BRANCH" | grep -c "^f-" ; then
+          CAOSDB_TAG=${DEPLOY_REF}_F_${F_BRANCH};
+          docker pull $CI_REGISTRY/caosdb/src/caosdb-deploy:$CAOSDB_TAG || CAOSDB_TAG="${DEFAULT_CAOSDB_TAG}_F_dev" ;
+        elif [ "$F_BRANCH" == "main" ] ; then
+          CAOSDB_TAG=${DEFAULT_CAOSDB_TAG}_F_main ;
+        else
+          CAOSDB_TAG="${DEFAULT_CAOSDB_TAG}_F_dev" ;
+        fi;
+      fi
+    # Set the F_BRANCH variable to the current branch name if it
+    # hasn't been set externally
+    - if [[ "$F_BRANCH" == "" ]]; then
+        F_BRANCH=$CI_COMMIT_REF_NAME;
+      fi;
+
+    - echo "F_BRANCH = $F_BRANCH"
+    - echo "CAOSDB_TAG = $CAOSDB_TAG"
+    - echo "DEPLOY_REF = $DEPLOY_REF"
+    - docker load < /image-cache/caosdb-${F_BRANCH}.tar || true
+    - docker pull $CI_REGISTRY/caosdb/src/caosdb-deploy:$CAOSDB_TAG || CAOSDB_TAG=${DEFAULT_CAOSDB_TAG}_F_dev ;
+    - echo "CAOSDB_TAG = $CAOSDB_TAG"
+    
+    - docker load < /image-cache/${JULIAINTTEST_IMAGE_CACHE} || true
+    - docker pull $JULIAINTTEST_REGISTRY_IMAGE
+
+    - docker load < /image-cache/mariadb.tar || true # load any(!!!) mariadb image
+
+    - cd .docker
+    - ls -la
+    - ls -la cert
+
+    # here the server and the mysql backend docker are being started
+    - CAOSDB_TAG=$CAOSDB_TAG docker-compose  up -d
+    # store versions of CaosDB parts
+    - docker exec -u 0 -t docker_caosdb-server_1 cat /opt/caosdb/git/caosdb_server_commit | tee hash_server
+    - docker exec -u 0 -t docker_caosdb-server_1 cat /opt/caosdb/git/caosdb_mysqlbackend_commit | tee hash_mysql
+    - docker exec -u 0 -t docker_caosdb-server_1 cat /opt/caosdb/git/caosdb_proto_commit | tee hash_proto
+
+    # the Julia integration test docker writes the return value of the
+    # tests into the file result
+    - F_BRANCH=$F_BRANCH /bin/sh ./run.sh
+
+    # collect log files
+    - docker logs docker_caosdb-server_1 &> ../caosdb_log.txt
+    - docker logs docker_sqldb_1 &> ../mariadb_log.txt
+
+    # shut down the CaosDB server
+    - CAOSDB_TAG=$CAOSDB_TAG docker-compose -f docker-compose.yml down
+
+    # collect results
+    - rc=`cat result`
+    # renew mariadb image in the cache (why? to renew the timestamp???)
+    - echo mariadb:$(docker image ls mariadb | grep mariadb | awk '{print $2}')
+    - docker save mariadb:$(docker image ls mariadb | grep mariadb | awk '{print $2}') > /image-cache/mariadb.tar || true
+    # exit with fail when errors exist
+    - exit $rc
+  timeout: 3h
+  artifacts:
+    when: on_failure
+    paths:
+      - caosdb_log.txt
+      - mariadb_log.txt
+      - .docker/hash_*
+    expire_in: 1 week
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10652f17f2f515bb408d91ffc2f18564c7452f1d..1000198a0834b85a9a7eb129a3d08463f5a56e63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+* Basic CI infrastructure
+* Simple test for the connection to a CaosDB server
+
 ### Changed
 
 ### Deprecated
diff --git a/Manifest.toml b/Manifest.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f45eecff031feeb9bb67f87c4794b44cc53a293d
--- /dev/null
+++ b/Manifest.toml
@@ -0,0 +1,2 @@
+# This file is machine-generated - editing it directly is not advised
+
diff --git a/Project.toml b/Project.toml
new file mode 100644
index 0000000000000000000000000000000000000000..497c1787edd6fd6415ca560e1d9cda0dd0109d1a
--- /dev/null
+++ b/Project.toml
@@ -0,0 +1,4 @@
+name = "CaosDBIntegrationTests"
+uuid = "c3438055-fe44-4932-b8ed-926ba38cadad"
+authors = ["florian <f.spreckelsen@inidscale.com>"]
+version = "0.0.1"
diff --git a/README_SETUP.md b/README_SETUP.md
index c484b3dc791d0c3f13ada3553a328d3c02897a8c..34c8700130df950d1dab9c1bbc3943edba288af5 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -14,9 +14,37 @@ i.e., without any explicit arguments to the call to `connect`.
 
 # Tests
 
+## Testing against a remote CaosDB.jl
+
 After set-up, the integration tests can be executed by
 
 ```julia-repl
 julia> ]activate "/path/to/caosdb-juliainttest"
 julia> ]test
 ```
+
+This will test them with the remote `CaosDB.jl` in a branch as
+specified in `src/CaosDBIntegrationTests.jl`, which has the main
+purpose of automatic desting in the gitlab CI.
+
+## Testing your local CaosDB.jl
+
+If you want to run the integration tests against your local
+`CaosDB.jl` installation, either use the `runtest.jl` outside of the
+package management, i.e., by executing
+
+```sh
+julia test/runtests.jl
+```
+
+(note that without specifing the verbosity, succeeding tests will not
+generate output, only failing tests will) or you add the local
+`CaosDB.jl` to the test dependencies manually and run the tests
+afterwards:
+
+```julia-repl
+julia> ] activate ./test
+julia> ] add path/to/local/caosdb-julialib
+julia> ] activate .
+julia> ] test
+```
diff --git a/src/CaosDBIntegrationTests.jl b/src/CaosDBIntegrationTests.jl
new file mode 100644
index 0000000000000000000000000000000000000000..c50770137e214f61a38ae529c711f6ca2dee5bfb
--- /dev/null
+++ b/src/CaosDBIntegrationTests.jl
@@ -0,0 +1,45 @@
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+# Start by installing CaosDB.jl in the correct branch
+
+"""
+This module only has the puprose of setting up the integration tests
+with the correct version (and branch) of CaosDB.jl.
+"""
+module CaosDBIntegrationTests
+
+repo_url = "https://gitlab.indiscale.com/caosdb/src/caosdb-julialib.git"
+
+# Get possible special branch name
+if haskey(ENV, "F_BRANCH")
+    rev = ENV["F_BRANCH"]
+else
+    rev = ""
+end
+
+# Use default branch if no specific branch has been given
+if rev == ""
+    rev = nothing
+end
+
+end # module
diff --git a/test/Manifest.toml b/test/Manifest.toml
new file mode 100644
index 0000000000000000000000000000000000000000..4b81b147b6e4cf11e60f3a518a7776ddfb34b46b
--- /dev/null
+++ b/test/Manifest.toml
@@ -0,0 +1,114 @@
+# This file is machine-generated - editing it directly is not advised
+
+[[ArgTools]]
+uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
+
+[[Artifacts]]
+uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
+
+[[Base64]]
+uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
+
+[[Dates]]
+deps = ["Printf"]
+uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
+
+[[Downloads]]
+deps = ["ArgTools", "LibCURL", "NetworkOptions"]
+uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
+
+[[InteractiveUtils]]
+deps = ["Markdown"]
+uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
+
+[[LibCURL]]
+deps = ["LibCURL_jll", "MozillaCACerts_jll"]
+uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
+
+[[LibCURL_jll]]
+deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
+uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
+
+[[LibGit2]]
+deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
+uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
+
+[[LibSSH2_jll]]
+deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
+uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
+
+[[Libdl]]
+uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
+
+[[Logging]]
+uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
+
+[[Markdown]]
+deps = ["Base64"]
+uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
+
+[[MbedTLS_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
+
+[[MozillaCACerts_jll]]
+uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
+
+[[NetworkOptions]]
+uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
+
+[[Pkg]]
+deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
+uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
+
+[[Printf]]
+deps = ["Unicode"]
+uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
+
+[[REPL]]
+deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
+uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
+
+[[Random]]
+deps = ["Serialization"]
+uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
+
+[[SHA]]
+uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
+
+[[Serialization]]
+uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
+
+[[Sockets]]
+uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
+
+[[TOML]]
+deps = ["Dates"]
+uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
+
+[[Tar]]
+deps = ["ArgTools", "SHA"]
+uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
+
+[[Test]]
+deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
+uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+
+[[UUIDs]]
+deps = ["Random", "SHA"]
+uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
+
+[[Unicode]]
+uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
+
+[[Zlib_jll]]
+deps = ["Libdl"]
+uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
+
+[[nghttp2_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
+
+[[p7zip_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
diff --git a/test/Project.toml b/test/Project.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0df42424c4ce05b41abef67ed1a8759ef45f5373
--- /dev/null
+++ b/test/Project.toml
@@ -0,0 +1,4 @@
+[deps]
+Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
+Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
+Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/test/runtests.jl b/test/runtests.jl
new file mode 100644
index 0000000000000000000000000000000000000000..61a23b18bb951ec0e5eda31f98d96f8de9dc53f9
--- /dev/null
+++ b/test/runtests.jl
@@ -0,0 +1,59 @@
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+
+using Test
+using Logging
+# first try and load a local CaosDB installation; if that fails,
+# set-up CaosDB.jl with repo and branch specified in
+# `CaosDBIntegrationTests`. The latter is mainly useful for running
+# this in a pipeline.
+try
+    using CaosDB
+catch e
+    if isa(e, ArgumentError)
+        # Install from repo if it cannot be loaded
+        using CaosDBIntegrationTests
+        repo_url = CaosDBIntegrationTests.repo_url
+        rev = CaosDBIntegrationTests.rev
+        println("Using remote CaosDB.jl from $repo_url in branch $rev.")
+        using Pkg
+        try
+            # Try first with the current rev
+            Pkg.add(url = repo_url, rev = rev)
+        catch e
+            if isa(e, Pkg.Types.PkgError)
+                @warn "Couldn't find remote branch $rev, using dev branch instead."
+                Pkg.add(url = repo_url, rev = "dev")
+            else
+                throw(e)
+            end
+        end
+        using CaosDB
+    else
+        # throw any other possible error
+        throw(e)
+    end
+end
+
+# test whether connection can be established without an error
+@test CaosDB.Connection.connect() != nothing