diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c64175e2210a9647410997020465115851212c0c..6e146cbd01a97756cd4dfc97ed503ca4e4edf9de 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,17 +1,21 @@
 stages:
+  - base
   - build
   - test
   - tag
 
-build_image:
-  stage: build
+variables:
+  COMBINE_TAG: main
+
+build_base_image:
+  stage: base
   timeout: 3 hours
   image:
     name: gcr.io/kaniko-project/executor:debug
     entrypoint: [""]
   rules:
     - changes:
-        - Dockerfile
+        - Dockerfile_base
         - packages.txt
         - requirements.txt
         - vnc/vnc_utils.sh
@@ -19,7 +23,27 @@ build_image:
   script:
     - echo "Building image - $IMAGE_DESTINATION_TAG"
     - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
-    - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile ${CI_PROJECT_DIR}/Dockerfile --destination ${IMAGE_DESTINATION_TAG}
+    - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile ${CI_PROJECT_DIR}/Dockerfile_base --destination ${IMAGE_DESTINATION_TAG} --destination ${IMAGE_DESTINATION_LATEST}
+    - echo "Image pushed successfully to ${IMAGE_DESTINATION_TAG}"
+    - echo "IMAGE_NAME=${IMAGE_DESTINATION_TAG}" > .env
+  artifacts:
+    reports:
+      dotenv: .env
+  variables:
+    IMAGE_DESTINATION_TAG: ${CI_REGISTRY_IMAGE}:base-${CI_COMMIT_SHORT_SHA}
+    IMAGE_DESTINATION_LATEST: ${CI_REGISTRY_IMAGE}:base-latest
+
+
+build_image:
+  stage: build
+  timeout: 3 hours
+  image:
+    name: gcr.io/kaniko-project/executor:debug
+    entrypoint: [""]
+  script:
+    - echo "Building image - $IMAGE_DESTINATION_TAG"
+    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+    - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile ${CI_PROJECT_DIR}/Dockerfile --destination ${IMAGE_DESTINATION_TAG} --build-arg "COMBINE_TAG=${COMBINE_TAG}"
     - echo "Image pushed successfully to ${IMAGE_DESTINATION_TAG}"
     - echo "IMAGE_NAME=${IMAGE_DESTINATION_TAG}" > .env
   artifacts:
@@ -60,14 +84,20 @@ build_image:
     - ls -l /code/HiggsAnalysis/CombinedLimit/data/tutorials/counting/simple-counting-experiment.root
     - text2workspace.py /code/HiggsAnalysis/CombinedLimit/data/tutorials/shapes/simple-shapes-TH1.txt
     - ls -l /code/HiggsAnalysis/CombinedLimit/data/tutorials/shapes/simple-shapes-TH1.root
-    - text2workspace.py /code/HiggsAnalysis/CombinedLimit/test/multiDim/toy-hgg-125.txt -P HiggsAnalysis.CombinedLimit.LHCHCGModels:K3 -m 125
-    - ls -l /code/HiggsAnalysis/CombinedLimit/test/multiDim/toy-hgg-125.root
+    - text2workspace.py /code/HiggsAnalysis/CombinedLimit/data/tutorials/multiDim/toy-hgg-125.txt -P HiggsAnalysis.CombinedLimit.LHCHCGModels:K3 -m 125
+    - ls -l /code/HiggsAnalysis/CombinedLimit/data/tutorials/multiDim/toy-hgg-125.root
     - combine -M Significance tests/hzz4l_datacard.txt
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-2-template-analysis.txt -M HybridNew --LHCmode LHC-limits --rMax 2.0 --clsAcc 0.01
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-3-parametric-analysis.txt -M HybridNew --LHCmode LHC-significance -T 100000 --mass 125
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-3-parametric-analysis.txt -M Significance --mass 125
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-1-counting-experiment.txt -M MarkovChainMC --tries 100
+    - text2workspace.py /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-5-multi-signal.txt -P HiggsAnalysis.CombinedLimit.PhysicsModel:floatingXSHiggs --PO modes=ggH,qqH -o datacard-5-multi-signal.root --mass 125
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-5-multi-signal.root -M MultiDimFit --algo singles --mass 125
+    - combine /code/HiggsAnalysis/CombinedLimit/data/tutorials/CAT23001/datacard-5-multi-signal.txt -M ChannelCompatibilityCheck --mass 125
+
 
 test_image:
   <<: *test_image_template
-  rules:
-    - !reference [build_image, rules]
 
 tag_image_latest:
   stage: tag
diff --git a/Dockerfile b/Dockerfile
index 38a6f7fb3e72a449f652aa3281e6f7bc71ae36ad..293ea79dd1cc891fa2e689eb83fb1b63003e50e6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,3 +1,5 @@
+FROM --platform=linux/amd64 gitlab-registry.cern.ch/cms-cloud/combine-standalone:base-latest as builder
+
 FROM --platform=linux/amd64 gitlab-registry.cern.ch/linuxsupport/cc7-base:latest
 
 LABEL maintainer.name="Clemens Lange"
@@ -6,232 +8,29 @@ LABEL maintainer.email="clemens.lange@cern.ch"
 # ENV LANG=C.UTF-8
 
 ARG PLATFORM="linux/amd64"
-ARG ROOT_VERSION_SCM=6.22.08
-ARG ROOT_TARGET_BRANCH=v6-22-08
-ARG ROOT_GIT_PROJECT_URL=https://github.com/root-project/root
-ARG PYTHON_VERSION=3.8.2
-ARG GCC_VERSION="9.3.0"
-ARG VDT_VERSION="0.4.1"
-ARG PCRE_VERSION="8.43"
-ARG BOOST_VERSION="1.75.0"
-ARG BOOST_VERSION_UNDERSCORE="1_75_0"
-ARG GSL_VERSION="2.6"
-ARG XROOTD_VERSION="4.12.3"
-ARG COMBINE_TAG="v9.1.0"
-
-WORKDIR /usr/local
-
-SHELL [ "/bin/bash", "-c" ]
+ARG COMBINE_TAG="main"
 
-COPY packages.txt packages.txt
-
-# Set PATH to pickup virtualenv by default
-ENV PATH=/usr/local/venv/bin:"${PATH}"
-RUN yum install -y $(cat packages.txt) > /dev/null && \
+RUN yum install -y git sudo make libmpc-devel glibc-devel freetype libXft > /dev/null && \
     yum clean -y all && \
-    mkdir -p /usr/local/venv && \
     ln --symbolic --force /bin/bash /bin/sh && \
     ln -sf /usr/share/zoneinfo/UTC /etc/localtime
 
-ADD https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.xz /code/gcc-${GCC_VERSION}.tar.xz
+COPY --from=builder /usr/local /usr/local
+WORKDIR /usr/local
 
-WORKDIR /code
-RUN tar -xJf gcc-${GCC_VERSION}.tar.xz \
-    && mkdir build \
-    && cd build \
-    && ../gcc-${GCC_VERSION}/configure --prefix=/code/local \
-    && make -j $(($(nproc) + 1)) > /dev/null \
-    && make install -j $(($(nproc) + 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
+SHELL [ "/bin/bash", "-c" ]
 
+# Set PATH to pickup virtualenv by default
+ENV PATH=/usr/local/venv/bin:"${PATH}"
 ENV CC=/usr/local/venv/bin/gcc
 ENV CXX=/usr/local/venv/bin/g++
-ENV LD_LIBRARY_PATH=/usr/local/venv/lib64:/usr/local/venv/lib
-
-ADD http://lcgpackages.web.cern.ch/lcgpackages/tarFiles/sources/vdt-${VDT_VERSION}.tar.gz /code/vdt-${VDT_VERSION}.tar.gz
-RUN tar -xzf vdt-${VDT_VERSION}.tar.gz \
-    && cmake3 -DCMAKE_INSTALL_PREFIX=/code/local \
-        -B build -S vdt-${VDT_VERSION} \
-    && cd build \
-    && make -j $(($(nproc) + 1)) > /dev/null \
-    && make install -j $(($(nproc) + 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-WORKDIR /code
-RUN wget https://sourceforge.net/projects/pcre/files/pcre/${PCRE_VERSION}/pcre-${PCRE_VERSION}.tar.gz \
-        -O /code/pcre-${PCRE_VERSION}.tar.gz \
-    && tar -xzf pcre-${PCRE_VERSION}.tar.gz \
-    && cmake3 -DCMAKE_INSTALL_PREFIX=/code/local \
-        -B build -S pcre-${PCRE_VERSION} \
-    && cmake3 --build build --target install \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-ADD https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_UNDERSCORE}.tar.gz /code/boost_${BOOST_VERSION_UNDERSCORE}.tar.gz
-RUN tar -xzf boost_${BOOST_VERSION_UNDERSCORE}.tar.gz \
-    && cd boost_${BOOST_VERSION_UNDERSCORE} \
-    && ./bootstrap.sh --prefix=/code/local \
-    && ./b2 install \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-# Add 0mq to /usr/local
-# unfortunately, ZeroMQ doesn't have a release yet that contains the PPOLL feature that is needed for roofit parallelism
-# the ROOT team for now uses tihs specific commit hash, so we do the same 
-ARG ZMQ_TAG=7c2df78b49a3aa63e654b3f3526adf71ed091534
-# see: https://github.com/root-project/root/blob/8e9ee7f04a89156af4f9a142579e1d0c75ee398d/builtins/zeromq/libzmq/CMakeLists.txt#L17
-WORKDIR /code
-RUN git clone https://github.com/zeromq/libzmq.git 0mq \
-    && cd 0mq && git checkout "${ZMQ_TAG}" && cd - \
-    && cmake3 \
-        -DCMAKE_INSTALL_PREFIX=/code/local \
-        -S 0mq \
-        -B build \
-    && cmake3 build -LH \
-    && cmake3 \
-        --build build \
-        --clean-first \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && cmake3 --build build --target install \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-
-
-# Add eigen to /usr/local
-ARG EIGEN_TAG=011e0db31d1bed8b7f73662be6d57d9f30fa457a
-# see https://github.com/cms-analysis/HiggsAnalysis-CombinedLimit/blob/112x/env_standalone.sh#L12
-WORKDIR /code
-RUN git clone https://gitlab.com/libeigen/eigen.git eigen \
-    && cd eigen && git checkout "${EIGEN_TAG}" && cd - \
-    && cmake3 \
-        -DCMAKE_INSTALL_PREFIX=/code/local \
-        -S eigen \
-        -B build \
-    && cmake3 build -LH \
-    && cmake3 \
-        --build build \
-        --clean-first \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && cmake3 --build build --target install \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-
-WORKDIR /code
-# Add GSL to /usr/local
-ADD https://ftpmirror.gnu.org/gsl/gsl-${GSL_VERSION}.tar.gz /code/gsl-${GSL_VERSION}.tar.gz
-RUN tar -xzf gsl-${GSL_VERSION}.tar.gz \
-    && mkdir build \
-    && cd build \
-    && ../gsl-${GSL_VERSION}/configure --prefix=/code/local \
-    && make -j $(($(nproc) + 1)) > /dev/null \
-    && make install -j $(($(nproc) + 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
 ENV GSL_ROOT_DIR=/usr/local/venv
-
-
-# Install Python
-WORKDIR /code
-RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \
-    && tar -xzf Python-${PYTHON_VERSION}.tgz \
-    && cd Python-${PYTHON_VERSION} \
-    && ./configure --enable-shared --prefix=/usr/local/ \
-    && make -j $(($(nproc) + 1)) > /dev/null \
-    && make altinstall -j $(($(nproc) + 1)) > /dev/null \
-    && ln -s /usr/local/bin/python${PYTHON_VERSION%.*} /usr/local/bin/python3 \
-    && rm -rf /code
-ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib
+ENV LD_LIBRARY_PATH=/usr/local/venv/lib64:/usr/local/venv/lib::/usr/local/lib
 ENV PATH=${PATH}:/usr/local/bin
 
-
-# Install pip and numpy
-WORKDIR /code
-COPY requirements.txt requirements.txt
-RUN wget https://bootstrap.pypa.io/get-pip.py \
-    && /usr/local/bin/python3 get-pip.py \
-    && /usr/local/bin/python3 -m venv /usr/local/venv \
-    && . /usr/local/venv/bin/activate \
-    && python -m pip --no-cache-dir install --upgrade pip setuptools==57.5.0 wheel \
-    && python -m pip --no-cache-dir install --requirement requirements.txt \
-    && python -m pip list \
-    && rm -rf /code
-
-
-# Add XRootD under /usr/local
-WORKDIR /code
-RUN git clone --depth 1 https://github.com/xrootd/xrootd.git \
-        --branch "v${XROOTD_VERSION}" \
-        --single-branch \
-        xrootd \
-    && cmake3 \
-        -DCMAKE_INSTALL_PREFIX=/code/local \
-        -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/venv/bin/python \
-        -DPIP_VERBOSE=ON \
-        -S xrootd \
-        -B build \
-    && cmake3 build -LH > /dev/null \
-    && cmake3 \
-        --build build \
-        --clean-first \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && cmake3 --build build --target install \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
-    && rm -rf /code
-
-
-# Install ROOT
-WORKDIR /code
-# c.f. https://root.cern.ch/building-root#options
-RUN git clone --depth 1 "${ROOT_GIT_PROJECT_URL}" \
-        --branch "${ROOT_TARGET_BRANCH}" \
-        --single-branch \
-        root_src \
-    && cmake3 \
-        -Dsoversion=ON \
-        -Dgsl_shared=ON \
-        -Dbuiltin_gsl=OFF \
-        -Dbuiltin_fftw3=ON \
-        -Dbuiltin_cfitsio=ON \
-        -DCMAKE_CXX_STANDARD=17 \
-        -Droot7=OFF \
-        -Dfortran=ON \
-        -Droofit=ON \
-        -Droofit_multiprocess=ON \
-        -Droostats=ON \
-        -Dhistfactory=ON \
-        -Dhistfactory=ON \
-        -Dminuit2=ON \
-        -DCMAKE_CXX_FLAGS=-D__ROOFIT_NOBANNER \
-        -Dxrootd=ON \
-        -Dbuiltin_xrootd=OFF \
-        -DXROOTD_ROOT_DIR=/usr/local/venv \
-        -Dvdt=ON \
-        -Dbuiltin_vdt=ON \
-        -Dpyroot=ON \
-        -DPYTHON_EXECUTABLE=$(which python3) \
-        -DCMAKE_INSTALL_PREFIX=/code/root_install \
-        -B build \
-        root_src \
-    && cmake3 \
-        --build build \
-        --target install \
-        --parallel $(($(nproc) - 1)) > /dev/null \
-    && rsync -av --keep-dirlinks /code/root_install/ /usr/local/venv \
-    && rm -rf /code
-
 RUN echo /usr/local/venv/lib >> /etc/ld.so.conf \
     && ldconfig
 
-RUN git clone --depth 1 --single-branch https://github.com/novnc/noVNC.git /usr/local/novnc
-
 RUN groupadd -g 1000 cmsusr && adduser --uid 1000 --gid 1000 cmsusr && \
     echo "cmsusr  ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
     chown -R cmsusr:cmsusr /usr/local/venv
@@ -282,6 +81,8 @@ ENV PYTHONPATH=$PYTHONPATH:/code/HiggsAnalysis/CombinedLimit/build/lib/python
 ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/code/HiggsAnalysis/CombinedLimit/build/lib
 ENV PATH=$PATH:/code/HiggsAnalysis/CombinedLimit/build/bin
 ENV CMSSW_BASE=/code
+ENV CONDA=1
+ENV CONDA_PREFIX=/usr/local/venv
 
 ENTRYPOINT ["/bin/bash", "-l", "-c"]
 CMD ["/bin/bash"]
diff --git a/Dockerfile_base b/Dockerfile_base
new file mode 100644
index 0000000000000000000000000000000000000000..51a5de5aa5bccada97bd0a9bc66a5a815223859e
--- /dev/null
+++ b/Dockerfile_base
@@ -0,0 +1,232 @@
+FROM --platform=linux/amd64 gitlab-registry.cern.ch/linuxsupport/cc7-base:latest
+
+LABEL maintainer.name="Clemens Lange"
+LABEL maintainer.email="clemens.lange@cern.ch"
+
+# ENV LANG=C.UTF-8
+
+ARG PLATFORM="linux/amd64"
+ARG ROOT_VERSION_SCM=6.24.08
+ARG ROOT_TARGET_BRANCH=v6-24-08
+ARG ROOT_GIT_PROJECT_URL=https://github.com/root-project/root
+ARG PYTHON_VERSION=3.8.2
+ARG GCC_VERSION="9.3.0"
+ARG VDT_VERSION="0.4.1"
+ARG PCRE_VERSION="8.43"
+ARG BOOST_VERSION="1.75.0"
+ARG BOOST_VERSION_UNDERSCORE="1_75_0"
+ARG GSL_VERSION="2.6"
+ARG XROOTD_VERSION="5.6.4"
+
+WORKDIR /usr/local
+
+SHELL [ "/bin/bash", "-c" ]
+
+COPY packages.txt packages.txt
+
+# Set PATH to pickup virtualenv by default
+ENV PATH=/usr/local/venv/bin:"${PATH}"
+RUN yum install -y $(cat packages.txt) > /dev/null && \
+    yum clean -y all && \
+    mkdir -p /usr/local/venv && \
+    ln --symbolic --force /bin/bash /bin/sh && \
+    ln -sf /usr/share/zoneinfo/UTC /etc/localtime
+
+ADD https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.xz /code/gcc-${GCC_VERSION}.tar.xz
+
+WORKDIR /code
+RUN tar -xJf gcc-${GCC_VERSION}.tar.xz \
+    && mkdir build \
+    && cd build \
+    && ../gcc-${GCC_VERSION}/configure --prefix=/code/local \
+    && make -j $(($(nproc) + 1)) > /dev/null \
+    && make install -j $(($(nproc) + 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+ENV CC=/usr/local/venv/bin/gcc
+ENV CXX=/usr/local/venv/bin/g++
+ENV LD_LIBRARY_PATH=/usr/local/venv/lib64:/usr/local/venv/lib
+
+ADD http://lcgpackages.web.cern.ch/lcgpackages/tarFiles/sources/vdt-${VDT_VERSION}.tar.gz /code/vdt-${VDT_VERSION}.tar.gz
+RUN tar -xzf vdt-${VDT_VERSION}.tar.gz \
+    && cmake3 -DCMAKE_INSTALL_PREFIX=/code/local \
+        -B build -S vdt-${VDT_VERSION} \
+    && cd build \
+    && make -j $(($(nproc) + 1)) > /dev/null \
+    && make install -j $(($(nproc) + 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+WORKDIR /code
+RUN wget https://sourceforge.net/projects/pcre/files/pcre/${PCRE_VERSION}/pcre-${PCRE_VERSION}.tar.gz \
+        -O /code/pcre-${PCRE_VERSION}.tar.gz \
+    && tar -xzf pcre-${PCRE_VERSION}.tar.gz \
+    && cmake3 -DCMAKE_INSTALL_PREFIX=/code/local \
+        -B build -S pcre-${PCRE_VERSION} \
+    && cmake3 --build build --target install \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && cd /usr/local/venv/include \
+    && ln -sf eigen3/Eigen Eigen \
+    && ln -sf eigen3/unsupported unsupported \
+    && rm -rf /code
+
+ADD https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_UNDERSCORE}.tar.gz /code/boost_${BOOST_VERSION_UNDERSCORE}.tar.gz
+RUN tar -xzf boost_${BOOST_VERSION_UNDERSCORE}.tar.gz \
+    && cd boost_${BOOST_VERSION_UNDERSCORE} \
+    && ./bootstrap.sh --prefix=/code/local \
+    && ./b2 install \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+# Add 0mq to /usr/local
+# unfortunately, ZeroMQ doesn't have a release yet that contains the PPOLL feature that is needed for roofit parallelism
+# the ROOT team for now uses tihs specific commit hash, so we do the same 
+ARG ZMQ_TAG=7c2df78b49a3aa63e654b3f3526adf71ed091534
+# see: https://github.com/root-project/root/blob/8e9ee7f04a89156af4f9a142579e1d0c75ee398d/builtins/zeromq/libzmq/CMakeLists.txt#L17
+WORKDIR /code
+RUN git clone https://github.com/zeromq/libzmq.git 0mq \
+    && cd 0mq && git checkout "${ZMQ_TAG}" && cd - \
+    && cmake3 \
+        -DCMAKE_INSTALL_PREFIX=/code/local \
+        -S 0mq \
+        -B build \
+    && cmake3 build -LH \
+    && cmake3 \
+        --build build \
+        --clean-first \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && cmake3 --build build --target install \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+
+
+# Add eigen to /usr/local
+ARG EIGEN_TAG=011e0db31d1bed8b7f73662be6d57d9f30fa457a
+# see https://github.com/cms-analysis/HiggsAnalysis-CombinedLimit/blob/112x/env_standalone.sh#L12
+WORKDIR /code
+RUN git clone https://gitlab.com/libeigen/eigen.git eigen \
+    && cd eigen && git checkout "${EIGEN_TAG}" && cd - \
+    && cmake3 \
+        -DCMAKE_INSTALL_PREFIX=/code/local \
+        -S eigen \
+        -B build \
+    && cmake3 build -LH \
+    && cmake3 \
+        --build build \
+        --clean-first \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && cmake3 --build build --target install \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+
+WORKDIR /code
+# Add GSL to /usr/local
+ADD https://ftpmirror.gnu.org/gsl/gsl-${GSL_VERSION}.tar.gz /code/gsl-${GSL_VERSION}.tar.gz
+RUN tar -xzf gsl-${GSL_VERSION}.tar.gz \
+    && mkdir build \
+    && cd build \
+    && ../gsl-${GSL_VERSION}/configure --prefix=/code/local \
+    && make -j $(($(nproc) + 1)) > /dev/null \
+    && make install -j $(($(nproc) + 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+ENV GSL_ROOT_DIR=/usr/local/venv
+
+
+# Install Python
+WORKDIR /code
+RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \
+    && tar -xzf Python-${PYTHON_VERSION}.tgz \
+    && cd Python-${PYTHON_VERSION} \
+    && ./configure --enable-shared --prefix=/usr/local/ \
+    && make -j $(($(nproc) + 1)) > /dev/null \
+    && make altinstall -j $(($(nproc) + 1)) > /dev/null \
+    && ln -s /usr/local/bin/python${PYTHON_VERSION%.*} /usr/local/bin/python3 \
+    && rm -rf /code
+ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib
+ENV PATH=${PATH}:/usr/local/bin
+
+
+# Install pip and numpy
+WORKDIR /code
+COPY requirements.txt requirements.txt
+RUN wget https://bootstrap.pypa.io/get-pip.py \
+    && /usr/local/bin/python3 get-pip.py \
+    && /usr/local/bin/python3 -m venv /usr/local/venv \
+    && . /usr/local/venv/bin/activate \
+    && python -m pip --no-cache-dir install --upgrade pip setuptools==57.5.0 wheel \
+    && python -m pip --no-cache-dir install --requirement requirements.txt \
+    && python -m pip list \
+    && rm -rf /code
+
+
+# Add XRootD under /usr/local
+WORKDIR /code
+RUN git clone --depth 1 https://github.com/xrootd/xrootd.git \
+        --branch "v${XROOTD_VERSION}" \
+        --single-branch \
+        xrootd \
+    && cmake3 \
+        -DCMAKE_INSTALL_PREFIX=/code/local \
+        -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/venv/bin/python \
+        -DPIP_VERBOSE=ON \
+        -S xrootd \
+        -B build \
+    && cmake3 build -LH > /dev/null \
+    && cmake3 \
+        --build build \
+        --clean-first \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && cmake3 --build build --target install \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/local/ /usr/local/venv \
+    && rm -rf /code
+
+
+# Install ROOT
+WORKDIR /code
+# c.f. https://root.cern.ch/building-root#options
+RUN git clone --depth 1 "${ROOT_GIT_PROJECT_URL}" \
+        --branch "${ROOT_TARGET_BRANCH}" \
+        --single-branch \
+        root_src \
+    && cmake3 \
+        -Dsoversion=ON \
+        -Dgsl_shared=ON \
+        -Dbuiltin_gsl=OFF \
+        -Dbuiltin_fftw3=ON \
+        -Dbuiltin_cfitsio=ON \
+        -DCMAKE_CXX_STANDARD=17 \
+        -Droot7=OFF \
+        -Dfortran=ON \
+        -Droofit=ON \
+        -Droofit_multiprocess=ON \
+        -Droostats=ON \
+        -Dhistfactory=ON \
+        -Dhistfactory=ON \
+        -Dminuit2=ON \
+        -DCMAKE_CXX_FLAGS=-D__ROOFIT_NOBANNER \
+        -Dxrootd=ON \
+        -Dbuiltin_xrootd=OFF \
+        -DXROOTD_ROOT_DIR=/usr/local/venv \
+        -Dvdt=ON \
+        -Dbuiltin_vdt=ON \
+        -Dpyroot=ON \
+        -DPYTHON_EXECUTABLE=$(which python3) \
+        -DCMAKE_INSTALL_PREFIX=/code/root_install \
+        -B build \
+        root_src \
+    && cmake3 \
+        --build build \
+        --target install \
+        --parallel $(($(nproc) - 1)) > /dev/null \
+    && rsync -av --keep-dirlinks /code/root_install/ /usr/local/venv \
+    && rm -rf /code
+
+RUN git clone --depth 1 --single-branch https://github.com/novnc/noVNC.git /usr/local/novnc