diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 224c9408d26f4c7064aadb496e9334f36a31a695..690783884fe50ae5b4c72e2a2c123833654f29fd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,6 +19,9 @@ variables:
   DH_REGISTRY: eicweb
   DH_PUSH: 1
 
+  ## Also export the raw builder image, will be stored on DH only
+  EXPORT_BUILDER: 1
+
   ## TLS error resiliency: number of retries and second wait between tries 
   ## (wait time is doubled with each attempt)
   DOCKER_NTRIES: 5
@@ -175,6 +178,16 @@ jug_dev:default:
     ## move spacke directory and spack.yaml into the container build directory
     - cp -r spack containers/jug
     - cp -r spack.yaml containers/jug/spack/spack.yaml
+    ## Optionally build the raw builder image
+    - test ${EXPORT_BUILDER} = 1 && docker build 
+                   -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:builder-${INTERNAL_TAG} 
+                   --target=builder
+                   -f containers/jug/dev.Dockerfile
+                   --build-arg SPACK_VERSION=${SPACK_VERSION}
+                   --build-arg CACHE_BUST=${PACKAGE_HASH}
+                   --build-arg INTERNAL_TAG=${INTERNAL_TAG}
+                   --build-arg JUG_VERSION=${INTERNAL_TAG}-$(git rev-parse HEAD)
+                   containers/jug
     ## now build our image
     - docker build -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG} 
                    -f containers/jug/dev.Dockerfile
@@ -183,6 +196,12 @@ jug_dev:default:
                    --build-arg INTERNAL_TAG=${INTERNAL_TAG}
                    --build-arg JUG_VERSION=${INTERNAL_TAG}-$(git rev-parse HEAD)
                    containers/jug
+    ## push builder image do DH if desired
+    - test ${EXPORT_BUILDER} = 1 && ./gitlab-ci/docker_push.sh 
+                                 -i ${BUILD_IMAGE} -l builder-${INTERNAL_TAG}
+                                 -n ${DOCKER_NTRIES} -t ${DOCKER_WAIT_TIME}
+                                 builder-${EXPORT_TAG} --dockerhub
+    ## standard exports
     - !reference [.build, script]
 
 jug_xl:default:
diff --git a/containers/jug/dev.Dockerfile b/containers/jug/dev.Dockerfile
index 2ce92d5369a9756f2d27caeb28c205dd7e1bb42d..df573c3b35908aaaa4222f994ccf5077138f96bc 100644
--- a/containers/jug/dev.Dockerfile
+++ b/containers/jug/dev.Dockerfile
@@ -94,6 +94,13 @@ RUN --mount=type=cache,target=/var/cache/spack-mirror                   \
  && spack install -j64 --no-check-signature                             \
  && spack clean -a                                                  
 
+## Optional, normally commented out:
+## Nuke the buildcache
+## This is useful when going to completely different containers,
+## or intermittently to keep the buildcache step from taking too much time
+#RUN --mount=type=cache,target=/var/cache/spack-mirror                   \
+  #rm -rf /var/cache/spack-mirror/*
+
 ## Update the local build cache if needed. Consists of 3 steps:
 ## 1. Remove the B010 network buildcache (silicon)
 ## 2. Get a list of all packages, and compare with what is already on
diff --git a/gitlab-ci/docker_push.sh b/gitlab-ci/docker_push.sh
index e77444d92c1d7de5bbb328fbbe49cb44450dcc8d..23806f8cec8766fe0de520ca2a68ac7a3c152c7a 100755
--- a/gitlab-ci/docker_push.sh
+++ b/gitlab-ci/docker_push.sh
@@ -13,6 +13,7 @@ function print_the_help {
   echo "          -n,--n-attempts Number of attempts, default: 5"
   echo "          -h,--help       Print this message"
   echo "          --eicweb        Publish to $CI_REGISTRY only"
+  echo "          --dockerhub     Publish to DH only"
   echo "  positional              At least one export tag (e.g., v3.0-stable)"
   echo ""
   echo "  Execute docker push from image:input_tag to REGISTRY/image:export_tag for"
@@ -29,6 +30,7 @@ EXPORT_TAGS=()
 NTRIES=5
 TIME=5
 DO_DH=${DH_PUSH}
+DO_EICWEB=1
 
 while [ $# -gt 0 ]; do
   key=$1
@@ -57,6 +59,11 @@ while [ $# -gt 0 ]; do
       DO_DH=0
       shift
       ;;
+    --dockerhub)
+      DO_EICWEB=0
+      DO_DH=1
+      shift
+      ;;
     -h|--help)
       print_the_help
       exit 0
@@ -122,10 +129,13 @@ function retry_push () {
 #echo "EXPORT_TAGS: ${EXPORT_TAGS}"
 #echo "DH_PUSH: ${DH_PUSH}"
 #echo "DO_DH: ${DO_DH}"
+#echo "DO_EICWEB: ${DO_EICWEB}"
 
 export INPUT=$CI_REGISTRY_IMAGE/${IMAGE}:${INPUT_TAG}
 for TAG in ${EXPORT_TAGS[@]}; do
-  retry_push $INPUT $CI_REGISTRY_IMAGE/${IMAGE}:${TAG}
+  if [ ${DO_EICWEB} != 0 ]; then
+    retry_push $INPUT $CI_REGISTRY_IMAGE/${IMAGE}:${TAG}
+  fi
   if [ ${DO_DH} != 0 ]; then
     retry_push $INPUT $DH_REGISTRY/${IMAGE}:${TAG}
   fi
diff --git a/spack.yaml b/spack.yaml
index ae79ad650cc585dbb4efb290f5e773f2849f9b9d..fcb7ed8f2a99541f616a134d190f73111c0ebe8f 100644
--- a/spack.yaml
+++ b/spack.yaml
@@ -24,7 +24,7 @@ spack:
     - cairo@1.16.0 +fc+ft+X+pdf+gobject
     - podio@0.13.1
     - geant4@10.7.1 cxxstd=17 +opengl +vecgeom +x11 +qt +threads ^qt +opengl
-    - dd4hep@1.17 +geant4 +assimp +hepmc3 +ipo +lcio
+    - dd4hep@1.17p1 +geant4 +assimp +hepmc3 +ipo +lcio
     - acts@8.03.0p1 +dd4hep +digitization +identification +json +tgeo +ipo
     - genfit@2.00.00
     - gaudi@36.0
diff --git a/spack/packages/dd4hep/2021-07-27.patch b/spack/packages/dd4hep/2021-07-27.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5576ca3292de41339b6e58913db554db063e3e3e
--- /dev/null
+++ b/spack/packages/dd4hep/2021-07-27.patch
@@ -0,0 +1,153 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 618812f7..8316ba67 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -46,6 +46,7 @@ ENDIF()
+ #############################################################
+ 
+ ENABLE_LANGUAGE(CXX)
++ENABLE_LANGUAGE(C)
+ 
+ # Set C++ standard
+ set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard used for compiling")
+diff --git a/DDCore/src/plugins/Compact2Objects.cpp b/DDCore/src/plugins/Compact2Objects.cpp
+index fab6267f..581f535f 100644
+--- a/DDCore/src/plugins/Compact2Objects.cpp
++++ b/DDCore/src/plugins/Compact2Objects.cpp
+@@ -805,21 +805,48 @@ template <> void Converter<PropertyTable>::operator()(xml_h e) const {
+ }
+ #endif
+ 
+-/** Convert compact visualization attribute to Detector visualization attribute
++/** Convert compact visualization attribute to Detector visualization attribute.
+  *
+  *  <vis name="SiVertexBarrelModuleVis"
+  *       alpha="1.0" r="1.0" g="0.75" b="0.76"
+  *       drawingStyle="wireframe"
+  *       showDaughters="false"
+  *       visible="true"/>
++ *
++ *  Optionally inherit an already defined VisAttr and override other properties.
++ *
++ *  <vis name="SiVertexEndcapModuleVis"
++ *       ref="SiVertexBarrelModuleVis"
++ *       alpha="0.5"/>
+  */
+ template <> void Converter<VisAttr>::operator()(xml_h e) const {
+   VisAttr attr(e.attr<string>(_U(name)));
++  float alpha = 1.0;
++  float red   = 1.0;
++  float green = 1.0;
++  float blue  = 1.0;
++  bool use_ref = false;
++  if(e.hasAttr(_U(ref))) {
++    use_ref = true;
++    auto refName = e.attr<string>(_U(ref));
++    const auto refAttr = description.visAttributes(refName);
++    if(!refAttr.isValid() )  {
++        throw runtime_error("reference VisAttr " + refName + " does not exist");
++    }
++    // Just copying things manually.
++    // I think a handle's copy constructor/assignment would reuse the underlying pointer... maybe?
++    refAttr.argb(alpha,red,green,blue);
++    attr.setColor(alpha,red,green,blue);
++    attr.setDrawingStyle( refAttr.drawingStyle());
++    attr.setLineStyle( refAttr.lineStyle());
++    attr.setShowDaughters(refAttr.showDaughters());
++    attr.setVisible(refAttr.visible());
++  }
+   xml_dim_t dim(e);
+-  float alpha = dim.alpha(1.0);
+-  float red   = dim.r(1.0);
+-  float green = dim.g(1.0);
+-  float blue  = dim.b(1.0);
++  alpha = dim.alpha(alpha);
++  red   = dim.r(red  );
++  green = dim.g(green);
++  blue  = dim.b(blue );
+ 
+   printout(s_debug.visattr ? ALWAYS : DEBUG, "Compact",
+            "++ Converting VisAttr  structure: %-16s. Alpha=%.2f R=%.3f G=%.3f B=%.3f",
+@@ -835,7 +862,8 @@ template <> void Converter<VisAttr>::operator()(xml_h e) const {
+       attr.setLineStyle(VisAttr::DASHED);
+   }
+   else {
+-    attr.setLineStyle(VisAttr::SOLID);
++    if (!use_ref)
++      attr.setLineStyle(VisAttr::SOLID);
+   }
+   if (e.hasAttr(_U(drawingStyle))) {
+     string ds = e.attr<string>(_U(drawingStyle));
+@@ -845,12 +873,15 @@ template <> void Converter<VisAttr>::operator()(xml_h e) const {
+       attr.setDrawingStyle(VisAttr::SOLID);
+   }
+   else {
+-    attr.setDrawingStyle(VisAttr::SOLID);
++    if (!use_ref)
++      attr.setDrawingStyle(VisAttr::SOLID);
+   }
+   if (e.hasAttr(_U(showDaughters)))
+     attr.setShowDaughters(e.attr<bool>(_U(showDaughters)));
+-  else
+-    attr.setShowDaughters(true);
++  else {
++    if (!use_ref)
++      attr.setShowDaughters(true);
++  }
+   description.addVisAttribute(attr);
+ }
+ 
+diff --git a/DDG4/edm4hep/Geant4Output2EDM4hep.cpp b/DDG4/edm4hep/Geant4Output2EDM4hep.cpp
+index 555e4e52..504fb8e4 100644
+--- a/DDG4/edm4hep/Geant4Output2EDM4hep.cpp
++++ b/DDG4/edm4hep/Geant4Output2EDM4hep.cpp
+@@ -20,6 +20,7 @@
+ #include "DDG4/Geant4HitCollection.h"
+ #include "DDG4/Geant4OutputAction.h"
+ #include "DDG4/Geant4SensDetAction.h"
++#include "DDG4/Geant4DataConversion.h"
+ #include "DDG4/EventParameters.h"
+ 
+ // Geant4 headers
+@@ -563,16 +564,23 @@ void Geant4Output2EDM4hep::createCollections(OutputContext<G4Event>& ctxt){
+       continue ;
+     }
+ 
++    Geant4Sensitive* sd = coll->sensitive();
++    string sd_enc = dd4hep::sim::Geant4ConversionHelper::encoding(sd->sensitiveDetector());
++
+     if( typeid( Geant4Tracker::Hit ) == coll->type().type()  ){
+ 
+-      m_store->create<edm4hep::SimTrackerHitCollection>(colName);
++      auto& sthc = m_store->create<edm4hep::SimTrackerHitCollection>(colName);
+       m_file->registerForWrite(colName);
++      auto& sthc_md = m_store->getCollectionMetaData( sthc.getID() );
++      sthc_md.setValue("CellIDEncodingString", sd_enc);
+       printout(DEBUG,"Geant4Output2EDM4hep","+++ created collection %s",colName.c_str() );
+     }
+     else if( typeid( Geant4Calorimeter::Hit ) == coll->type().type() ){
+ 
+-      m_store->create<edm4hep::SimCalorimeterHitCollection>(colName);
++      auto& schc = m_store->create<edm4hep::SimCalorimeterHitCollection>(colName);
+       m_file->registerForWrite(colName);
++      auto& schc_md = m_store->getCollectionMetaData( schc.getID() );
++      schc_md.setValue("CellIDEncodingString", sd_enc);
+       printout(DEBUG,"Geant4Output2EDM4hep","+++ created collection %s",colName.c_str() );
+ 
+       colName += "Contributions"  ;
+diff --git a/DDG4/src/Geant4ShapeConverter.cpp b/DDG4/src/Geant4ShapeConverter.cpp
+index 2a79a69a..6f4af522 100644
+--- a/DDG4/src/Geant4ShapeConverter.cpp
++++ b/DDG4/src/Geant4ShapeConverter.cpp
+@@ -205,8 +205,9 @@ namespace dd4hep {
+ 
+     template <> G4VSolid* convertShape<TGeoSphere>(const TGeoShape* shape)  {
+       const TGeoSphere* sh = (const TGeoSphere*) shape;
+-      return new G4Sphere(sh->GetName(), sh->GetRmin() * CM_2_MM, sh->GetRmax() * CM_2_MM, sh->GetPhi1() * DEGREE_2_RAD,
+-                          sh->GetPhi2() * DEGREE_2_RAD, sh->GetTheta1() * DEGREE_2_RAD, sh->GetTheta2() * DEGREE_2_RAD);
++      return new G4Sphere(sh->GetName(), sh->GetRmin() * CM_2_MM, sh->GetRmax() * CM_2_MM,
++                          sh->GetPhi1() * DEGREE_2_RAD, (sh->GetPhi2()-sh->GetPhi1()) * DEGREE_2_RAD,
++                          sh->GetTheta1() * DEGREE_2_RAD, (sh->GetTheta2()- sh->GetTheta1()) * DEGREE_2_RAD);
+     }
+ 
+     template <> G4VSolid* convertShape<TGeoTorus>(const TGeoShape* shape)  {
diff --git a/spack/packages/dd4hep/package.py b/spack/packages/dd4hep/package.py
index 2748bba8032b8cd758c4e449e74ec77df8f984d0..5934c56f54375acab4d4c6e6fe56d77647841294 100644
--- a/spack/packages/dd4hep/package.py
+++ b/spack/packages/dd4hep/package.py
@@ -24,6 +24,7 @@ class Dd4hep(CMakePackage):
     tags = ['hep']
 
     version('master', branch='master')
+    version('1.17p1', sha256='036a9908aaf1e13eaf5f2f43b6f5f4a8bdda8183ddc5befa77a4448dbb485826')
     version('1.17', sha256='036a9908aaf1e13eaf5f2f43b6f5f4a8bdda8183ddc5befa77a4448dbb485826')
     version('1.16.1', sha256='c8b1312aa88283986f89cc008d317b3476027fd146fdb586f9f1fbbb47763f1a')
     version('1.16', sha256='ea9755cd255cf1b058e0e3cd743101ca9ca5ff79f4c60be89f9ba72b1ae5ec69')
@@ -47,7 +48,12 @@ class Dd4hep(CMakePackage):
     patch('tbb2.patch', when='@1.12.1')
     # Workaround for failing build file generation in some cases
     # See https://github.com/spack/spack/issues/24232
-    patch('cmake_language.patch', when='@:1.17')
+    patch('cmake_language.patch', when='@:1.16.1')
+
+    # custom hash for the 2021-07-27 version, needed to include
+    # https://github.com/AIDASoft/DD4hep/pull/849
+    # https://github.com/AIDASoft/DD4hep/pull/851
+    patch('2021-07-27.patch', when='@1.17p1')
 
     variant('xercesc', default=False, description="Enable 'Detector Builders' based on XercesC")
     variant('geant4', default=False, description="Enable the simulation part based on Geant4")
@@ -110,7 +116,11 @@ class Dd4hep(CMakePackage):
         env.set("DD4hep_ROOT", self.prefix)
 
     def url_for_version(self, version):
-        # dd4hep releases are dashes and padded with a leading zero
+        # remove extra patch qualifiers
+        if version[-2] == 'p':
+            version=version[:-2]
+
+	# dd4hep releases are dashes and padded with a leading zero
         # the patch version is omitted when 0
         # so for example v01-12-01, v01-12 ...
         base_url = self.url.rsplit('/', 1)[0]