diff --git a/corsika/detail/setup/SetupEnvironment.inl b/corsika/detail/setup/SetupEnvironment.inl
deleted file mode 100644
index 39b129bd18e1df946b807ec3c1b204d89522e9c1..0000000000000000000000000000000000000000
--- a/corsika/detail/setup/SetupEnvironment.inl
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#pragma once
-
-#include <corsika/framework/geometry/Point.hpp>
-#include <corsika/framework/geometry/CoordinateSystem.hpp>
-
-#include <limits>
diff --git a/corsika/detail/setup/SetupStack.hpp b/corsika/detail/setup/SetupStack.hpp
index 2b40ccbdb0eafcc2c48ead0dc437c1314d6e2286..2409dd97ee9363f32ec2ab26080a8ba3a0d28cef 100644
--- a/corsika/detail/setup/SetupStack.hpp
+++ b/corsika/detail/setup/SetupStack.hpp
@@ -14,67 +14,84 @@
 #include <corsika/stack/WeightStackExtension.hpp>
 #include <corsika/stack/history/HistorySecondaryProducer.hpp>
 #include <corsika/stack/history/HistoryStackExtension.hpp>
-
-#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IMediumPropertyModel.hpp>
 
 namespace corsika {
 
   namespace setup::detail {
-
-    // ------------------------------------------
-    // add geometry node data to stack. This is fundamentally needed
-    // for robust tracking through multiple volumes.
-
-    // the GeometryNode stack needs to know the type of geometry-nodes from the
-    // environment:
-    template <typename TStackIter>
-    using SetupGeometryDataInterface =
-        typename node::MakeGeometryDataInterface<TStackIter, setup::Environment>::type;
-
-    // combine particle data stack with geometry information for tracking
-    template <typename TStackIter>
-    using StackWithGeometryInterface =
-        CombinedParticleInterface<VectorStack::pi_type, SetupGeometryDataInterface,
-                                  TStackIter>;
-
-    using StackWithGeometry =
-        CombinedStack<typename VectorStack::stack_data_type,
-                      node::GeometryData<setup::Environment>, StackWithGeometryInterface,
-                      DefaultSecondaryProducer>;
-
-    // ------------------------------------------
-    // add weight data to stack. This is fundamentally needed
-    // for thinning.
-
-    // the "pure" weight stack (interface)
-    template <typename TStackIter>
-    using SetupWeightDataInterface =
-        typename weights::MakeWeightDataInterface<TStackIter>::type;
-
-    // combine geometry-node-vector data stack with weight information for tracking
-    template <typename TStackIter>
-    using StackWithWeightInterface =
-        CombinedParticleInterface<StackWithGeometry::pi_type, SetupWeightDataInterface,
-                                  TStackIter>;
-
-    // the combined stack data: particle + geometry + weight
-    using StackWithWeight =
-        CombinedStack<typename StackWithGeometry::stack_data_type, weights::WeightData,
-                      StackWithWeightInterface, DefaultSecondaryProducer>;
-
-    // ------------------------------------------
-    // Add [OPTIONAL] history data to stack, too.
-    // This keeps the entire lineage of particles in memory.
-
-    template <typename TStackIter>
-    using StackWithHistoryInterface =
-        CombinedParticleInterface<StackWithWeight::pi_type,
-                                  history::HistoryEventDataInterface, TStackIter>;
-
-    using StackWithHistory =
-        CombinedStack<typename StackWithWeight::stack_data_type,
-                      history::HistoryEventData, StackWithHistoryInterface,
-                      history::HistorySecondaryProducer>;
+    template <typename TEnvironment>
+    class StackGenerator {
+    private:
+      using env_type = TEnvironment;
+
+      // ------------------------------------------
+      // add geometry node data to stack. This is fundamentally needed
+      // for robust tracking through multiple volumes.
+
+      // the GeometryNode stack needs to know the type of geometry-nodes from the
+      // environment:
+
+      template <typename TStackIter>
+      using SetupGeometryDataInterface =
+          typename node::MakeGeometryDataInterface<TStackIter, env_type>::type;
+
+      // combine particle data stack with geometry information for tracking
+      template <typename TStackIter>
+      using StackWithGeometryInterface =
+          CombinedParticleInterface<VectorStack::pi_type, SetupGeometryDataInterface,
+                                    TStackIter>;
+
+      using StackWithGeometry =
+          CombinedStack<typename VectorStack::stack_data_type,
+                        node::GeometryData<env_type>, StackWithGeometryInterface,
+                        DefaultSecondaryProducer>;
+
+      template <class T>
+      using StackWithGeometry_PI_type = typename StackWithGeometry::template pi_type<T>;
+
+      // ------------------------------------------
+      // add weight data to stack. This is fundamentally needed
+      // for thinning.
+
+      // the "pure" weight stack (interface)
+      template <typename TStackIter>
+      using SetupWeightDataInterface =
+          typename weights::MakeWeightDataInterface<TStackIter>::type;
+
+      // combine geometry-node-vector data stack with weight information for tracking
+      template <typename TStackIter>
+      using StackWithWeightInterface =
+          CombinedParticleInterface<StackWithGeometry_PI_type, SetupWeightDataInterface,
+                                    TStackIter>;
+
+    public:
+      // the combined stack data: particle + geometry + weight
+      using StackWithWeight =
+          CombinedStack<typename StackWithGeometry::stack_data_type, weights::WeightData,
+                        StackWithWeightInterface, DefaultSecondaryProducer>;
+
+    private:
+      template <typename T>
+      using StackWithWeight_PI_type = typename StackWithWeight::template pi_type<T>;
+
+      // ------------------------------------------
+      // Add [OPTIONAL] history data to stack, too.
+      // This keeps the entire lineage of particles in memory.
+
+      template <typename TStackIter>
+      using StackWithHistoryInterface =
+          CombinedParticleInterface<StackWithWeight_PI_type,
+                                    history::HistoryEventDataInterface, TStackIter>;
+
+    public:
+      using StackWithHistory =
+          CombinedStack<typename StackWithWeight::stack_data_type,
+                        history::HistoryEventData, StackWithHistoryInterface,
+                        history::HistorySecondaryProducer>;
+    };
 
   } // namespace setup::detail
 
diff --git a/corsika/detail/setup/SetupStack.inl b/corsika/detail/setup/SetupStack.inl
deleted file mode 100644
index 56fb4ff62c2172b386bc0efa9a452ccc8213947e..0000000000000000000000000000000000000000
--- a/corsika/detail/setup/SetupStack.inl
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
diff --git a/corsika/setup/SetupEnvironment.hpp b/corsika/setup/SetupEnvironment.hpp
deleted file mode 100644
index b7f59b31b6aa34d914206285f37463ce4981cb37..0000000000000000000000000000000000000000
--- a/corsika/setup/SetupEnvironment.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#pragma once
-
-#include <corsika/media/Environment.hpp>
-#include <corsika/media/IMagneticFieldModel.hpp>
-#include <corsika/media/IMediumModel.hpp>
-#include <corsika/media/IMediumPropertyModel.hpp>
-#include <corsika/media/IRefractiveIndexModel.hpp>
-
-namespace corsika::setup {
-
-  /**
-     Definition of the default environemnt model interface. Each model
-     interface provides properties of the environment in a position
-     bdependent way.
-   */
-
-  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
-  using Environment = Environment<EnvironmentInterface>;
-
-} // end namespace corsika::setup
diff --git a/corsika/setup/SetupStack.hpp b/corsika/setup/SetupStack.hpp
index 9028c2ab175127d07f3c430bbbba5edb5c82722d..7d1894e4cd8c00fa88ec290e4032a11cc5550d1b 100644
--- a/corsika/setup/SetupStack.hpp
+++ b/corsika/setup/SetupStack.hpp
@@ -24,18 +24,21 @@ namespace corsika::setup {
   /*
    * the version with history
    */
-  using Stack = detail::StackWithHistory;
+  template <typename TEnvironment>
+  using Stack = typename detail::StackGenerator<TEnvironment>::StackWithWeight;
 
 #else // WITH_HISTORY
 
   /*
    * the version without history (and geometry data and weights)
    */
-  using Stack = detail::StackWithWeight;
+  template <typename TEnvironment>
+  using Stack = typename detail::StackGenerator<TEnvironment>::StackWithWeight;
 
 #endif
 
   // the correct secondary stack view
-  using StackView = typename Stack::stack_view_type;
+  template <typename TEnvironment>
+  using StackView = typename Stack<TEnvironment>::stack_view_type;
 
 } // namespace corsika::setup
diff --git a/examples/boundary_example.cpp b/examples/boundary_example.cpp
index bffda918f64b3fc7ef79193f6470f9d104f9e711..4ede912094f627f1e590996a183ba5ce4b98e5db 100644
--- a/examples/boundary_example.cpp
+++ b/examples/boundary_example.cpp
@@ -17,7 +17,6 @@
 
 #include <corsika/output/OutputManager.hpp>
 
-#include <corsika/setup/SetupEnvironment.hpp>
 #include <corsika/setup/SetupStack.hpp>
 #include <corsika/setup/SetupTrajectory.hpp>
 
@@ -90,7 +89,8 @@ int main() {
   RNGManager<>::getInstance().registerRandomStream("cascade");
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   auto& universe = *(env.getUniverse());
 
@@ -99,8 +99,8 @@ int main() {
   // create "world" as infinite sphere filled with protons
   auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 100_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   auto const props = world->setModelProperties<MyHomogeneousModel>(
       Medium::AirDry1Atm, Vector(rootCS, 0_T, 0_T, 0_T), 1_kg / (1_m * 1_m * 1_m),
@@ -129,7 +129,7 @@ int main() {
   auto sequence = make_sequence(cut, boundaryCrossing, trackWriter);
 
   // setup particle stack, and add primary particles
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::MuPlus;
   const HEPMassType mass = get_mass(beamCode);
diff --git a/examples/cascade_example.cpp b/examples/cascade_example.cpp
index b43428ae50e482f0760b4bb133bc0cd4fbfa1c14..6d09fe1522628bc8f475b57747836fcbff44f6ef 100644
--- a/examples/cascade_example.cpp
+++ b/examples/cascade_example.cpp
@@ -27,7 +27,6 @@
 #include <corsika/media/MediumPropertyModel.hpp>
 #include <corsika/media/UniformMagneticField.hpp>
 
-#include <corsika/setup/SetupEnvironment.hpp>
 #include <corsika/setup/SetupStack.hpp>
 #include <corsika/setup/SetupTrajectory.hpp>
 
@@ -69,16 +68,17 @@ int main() {
   RNGManager<>::getInstance().registerRandomStream("cascade");
 
   // setup environment, geometry
-  setup::Environment env;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
+  EnvType env;
   auto& universe = *(env.getUniverse());
 
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
 
-  auto world =
-      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
+  auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   // fraction of oxygen
   double const fox = 0.20946;
@@ -87,15 +87,14 @@ int main() {
       1_kg / (1_m * 1_m * 1_m),
       NuclearComposition({Code::Nitrogen, Code::Oxygen}, {1. - fox, fox}));
 
-  auto innerMedium =
-      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
+  auto innerMedium = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
 
   innerMedium->setModelProperties(props);
   world->addChild(std::move(innerMedium));
   universe.addChild(std::move(world));
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const int nuclA = 4;
   const int nuclZ = int(nuclA / 2.15 + 0.7);
@@ -133,7 +132,7 @@ int main() {
 
   // setup processes, decays and interactions
   setup::Tracking tracking;
-  StackInspector<setup::Stack> stackInspect(100, true, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(100, true, E0);
 
   RNGManager<>::getInstance().registerRandomStream("sibyll");
   RNGManager<>::getInstance().registerRandomStream("pythia");
diff --git a/examples/cascade_proton_example.cpp b/examples/cascade_proton_example.cpp
index 7f41162b2feaf3efa785711697d17243a0e6ca89..bc30603fbee0af5feea6f0d08014d14743f62f5b 100644
--- a/examples/cascade_proton_example.cpp
+++ b/examples/cascade_proton_example.cpp
@@ -27,7 +27,6 @@
 #include <corsika/media/MediumPropertyModel.hpp>
 #include <corsika/media/UniformMagneticField.hpp>
 
-#include <corsika/setup/SetupEnvironment.hpp>
 #include <corsika/setup/SetupStack.hpp>
 #include <corsika/setup/SetupTrajectory.hpp>
 
@@ -72,15 +71,16 @@ int main() {
   OutputManager output("cascade_proton_outputs");
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   auto& universe = *(env.getUniverse());
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
 
   auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   world->setModelProperties<MyHomogeneousModel>(
       Medium::AirDry1Atm, MagneticFieldVector(rootCS, 0_T, 0_T, 1_mT),
@@ -89,7 +89,7 @@ int main() {
   universe.addChild(std::move(world));
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::Proton;
   const HEPMassType mass = Proton::mass;
@@ -120,7 +120,7 @@ int main() {
 
   // setup processes, decays and interactions
   setup::Tracking tracking;
-  StackInspector<setup::Stack> stackInspect(1000, true, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(1000, true, E0);
 
   RNGManager<>::getInstance().registerRandomStream("sibyll");
   RNGManager<>::getInstance().registerRandomStream("pythia");
diff --git a/examples/corsika.cpp b/examples/corsika.cpp
index 94d885b216f8fa01f76baeb34111494ebde444af..5cc04e646bca810870e07b1ac640d8c475b4a827 100644
--- a/examples/corsika.cpp
+++ b/examples/corsika.cpp
@@ -81,7 +81,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 void registerRandomStreams(int seed) {
   RNGManager<>::getInstance().registerRandomStream("cascade");
@@ -196,14 +199,13 @@ int main(int argc, char** argv) {
   registerRandomStreams(app["--seed"]->as<int>());
 
   /* === START: SETUP ENVIRONMENT AND ROOT COORDINATE SYSTEM === */
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   GeomagneticModel wmm(center, corsika_data("GeoMag/WMM.COF"));
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       wmm.getField(2022.5, 10_km, 49, 8.4));
 
@@ -331,7 +333,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted(urqmd);
-  StackInspector<setup::Stack> stackInspect(10000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(10000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -360,7 +362,7 @@ int main(int argc, char** argv) {
 
   // create the cascade object using the default stack and tracking implementation
   setup::Tracking tracking;
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   Cascade EAS(env, tracking, sequence, output, stack);
 
   // print our primary parameters all in one place
diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp
index 615b840c18d53d1d9d6bfc4ec92c0203e9c3619b..018666a46ff209067a440cef152dafd440c334d4 100644
--- a/examples/em_shower.cpp
+++ b/examples/em_shower.cpp
@@ -91,13 +91,14 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       MagneticFieldVector{rootCS, 0_T, 50_uT, 0_T});
 
@@ -110,7 +111,7 @@ int main(int argc, char** argv) {
     set_energy_production_threshold(pcode, energy);
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::Electron;
   auto const mass = get_mass(beamCode);
diff --git a/examples/hybrid_MC.cpp b/examples/hybrid_MC.cpp
index 4e695063468ad88ca2159c0642f57eb54117eced..0cb65570bd3230332d416328b5115142c401ac11 100644
--- a/examples/hybrid_MC.cpp
+++ b/examples/hybrid_MC.cpp
@@ -173,18 +173,19 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       MagneticFieldVector{rootCS, 0_T, 50_uT, 0_T});
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   unsigned short const A = std::stoi(std::string(argv[1]));
   unsigned short const Z = std::stoi(std::string(argv[2]));
@@ -298,7 +299,7 @@ int main(int argc, char** argv) {
     HEPEnergyType cutE_;
     EnergySwitch(HEPEnergyType cutE)
         : cutE_(cutE) {}
-    bool operator()(const setup::Stack::particle_type& p) const {
+    bool operator()(const setup::Stack<EnvType>::particle_type& p) const {
       return (p.getEnergy() < cutE_);
     }
   };
diff --git a/examples/mars.cpp b/examples/mars.cpp
index 84cf468233b48f08cc67f6d481d627dcbda1101b..bc7cbd4cf9b0f11eb172dd6be5923c65c72c8f55 100644
--- a/examples/mars.cpp
+++ b/examples/mars.cpp
@@ -81,7 +81,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 typedef decltype(1 * pascal) PressureType;
 typedef decltype(1 * degree_celsius) TemperatureType;
@@ -218,17 +221,16 @@ int main(int argc, char** argv) {
   registerRandomStreams(app["--seed"]->as<int>());
 
   /* === START: SETUP ENVIRONMENT AND ROOT COORDINATE SYSTEM === */
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   LengthType const radiusMars = 3389.5_km;
   auto builder =
-      make_layered_spherical_atmosphere_builder<setup::EnvironmentInterface, MyExtraEnv>::
-          create(center,
-                 radiusMars,                                   // Mars
-                 Medium::AirDry1Atm,                           // Mars, close enough
-                 MagneticFieldVector{rootCS, 0_T, 0_uT, 0_T}); // Mars
+      make_layered_spherical_atmosphere_builder<EnvironmentInterface, MyExtraEnv>::create(
+          center,
+          radiusMars,                                   // Mars
+          Medium::AirDry1Atm,                           // Mars, close enough
+          MagneticFieldVector{rootCS, 0_T, 0_uT, 0_T}); // Mars
 
   builder.setNuclearComposition(                             // Mars
       {{Code::Nitrogen, Code::Oxygen}, {1. / 3., 2. / 3.}}); // simplified
@@ -364,7 +366,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(5000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(5000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -395,7 +397,7 @@ int main(int argc, char** argv) {
 
   // create the cascade object using the default stack and tracking implementation
   setup::Tracking tracking;
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   Cascade EAS(env, tracking, sequence, output, stack);
 
   // print our primary parameters all in one place
diff --git a/examples/particle_list_example.cpp b/examples/particle_list_example.cpp
index 29e4bdc5f8e3b33a6100621ed5a230588d4b2faa..241fda60d53fbeb8d5d9e7862c3a7df51b7a1235 100644
--- a/examples/particle_list_example.cpp
+++ b/examples/particle_list_example.cpp
@@ -9,7 +9,6 @@
 #include <corsika/framework/core/ParticleProperties.hpp>
 #include <corsika/modules/QGSJetII.hpp>
 #include <corsika/modules/Sibyll.hpp>
-#include <corsika/setup/SetupEnvironment.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
 
 /*
diff --git a/examples/stopping_power.cpp b/examples/stopping_power.cpp
index 8d0641960940c34e00d110780735b4d06ecdfb9a..4aa5a0f5a504aa4a6baf5ebb2bffd14f87c01c26 100644
--- a/examples/stopping_power.cpp
+++ b/examples/stopping_power.cpp
@@ -50,7 +50,7 @@ int main() {
 
   BetheBlochPDG eLoss;
 
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
 
   std::ofstream file("dEdX.dat");
   file << "# beta*gamma, dE/dX / MeV/(g/cm²)" << std::endl;
diff --git a/examples/vertical_EAS.cpp b/examples/vertical_EAS.cpp
index 6173c460319f35e2f7dedfcc5faa630a22eb88ef..b5e5f163327fec18a28242bc8bdfc27908c7a761 100644
--- a/examples/vertical_EAS.cpp
+++ b/examples/vertical_EAS.cpp
@@ -76,7 +76,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 void registerRandomStreams(int seed) {
   RNGManager<>::getInstance().registerRandomStream("cascade");
@@ -122,14 +125,13 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   GeomagneticModel wmm(center, corsika_data("GeoMag/WMM.COF"));
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       wmm.getField(2022.5, 10_km, 49, 8.4));
 
@@ -254,7 +256,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(50000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(50000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -272,7 +274,7 @@ int main(int argc, char** argv) {
   string const cMSHist_file = "inthist_cms_verticalEAS.npz";
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
 
   stack.addParticle(std::make_tuple(
diff --git a/tests/common/SetupTestEnvironment.hpp b/tests/common/SetupTestEnvironment.hpp
index 2be9f34d53ef3a71ea93730b10c426f98d9fdac7..a118390d061467f7087fb3dc30058361f8fab4f1 100644
--- a/tests/common/SetupTestEnvironment.hpp
+++ b/tests/common/SetupTestEnvironment.hpp
@@ -11,7 +11,10 @@
 #include <corsika/framework/geometry/Point.hpp>
 #include <corsika/framework/geometry/CoordinateSystem.hpp>
 
-#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IMediumPropertyModel.hpp>
 #include <corsika/media/UniformMagneticField.hpp>
 #include <corsika/media/MediumPropertyModel.hpp>
 #include <corsika/media/HomogeneousMedium.hpp>
diff --git a/tests/common/TestStack.hpp b/tests/common/TestStack.hpp
index 299a0742964bbb42b442bc3cfac31d5fbad16ccf..2488fc1f059cfa4b4df6ed27a931feff60a62606 100644
--- a/tests/common/TestStack.hpp
+++ b/tests/common/TestStack.hpp
@@ -14,8 +14,10 @@
 #include <corsika/stack/WeightStackExtension.hpp>
 #include <corsika/stack/history/HistorySecondaryProducer.hpp>
 #include <corsika/stack/history/HistoryStackExtension.hpp>
-
-#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IMediumPropertyModel.hpp>
 
 namespace corsika {
 
diff --git a/tests/framework/testGeometry.cpp b/tests/framework/testGeometry.cpp
index b820d946f6a3131d304fe4ba25076e1e263da8e0..85fef905f80a885721a628b9cb563380a7f6b07b 100644
--- a/tests/framework/testGeometry.cpp
+++ b/tests/framework/testGeometry.cpp
@@ -21,7 +21,7 @@
 #include <corsika/framework/geometry/Sphere.hpp>
 #include <corsika/framework/geometry/StraightTrajectory.hpp>
 
-#include <corsika/setup/SetupStack.hpp>
+#include <tests/common/SetupStack.hpp>
 
 #include <PhysicalUnitsCatch2.hpp> // namespace corsike::testing
 
@@ -335,7 +335,7 @@ TEST_CASE("Geometry Trajectories") {
   Point r0(rootCS, {0_m, 0_m, 0_m});
   // Create a particle and and a stack so we can test .getTime() method
   const Code particle{Code::Electron};
-  setup::Stack stack;
+  test::Stack stack;
   // the mass of the particle
   const auto pmass{get_mass(particle)};
   // set an arbitrary energy value