diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 78148940d55e22c3ef483a55e08fe7559027ae62..2c3ba90bf6ab38d50ad0f0b83581feb0777d11c5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,8 +16,21 @@ include:
       # Import g5k default stages
       - '/g5k-default-stages.yml'
 
+.template-refrepo:
+  extends: .template-test
+  parallel:
+    matrix:
+      - DEBIAN_VERSION: [bullseye, bookworm]
+  image: debian:$DEBIAN_VERSION
+  before_script:
+    # This cache is configured in '.base'
+    - export APT_CACHE_DIR=`pwd`/vendor/apt && mkdir -pv $APT_CACHE_DIR
+    - apt-get update && apt-get -o dir::cache::archives="$APT_CACHE_DIR" -y --no-install-recommends install build-essential wget git ruby ruby-dev bundler rake gpg clustershell graphviz
+    # Call the original before_script section
+    - !reference [.base, before_script]
+
 validate-data:
-  extends: .test-for-bullseye
+  extends: .template-refrepo
   stage: validate
   script:
     # Add G5K CA certificate
@@ -27,15 +40,17 @@ validate-data:
     - bundle exec rake valid:duplicates
 
 wikigen:
-  extends: .test-for-bullseye
+  extends: .template-test
   stage: validate
   allow_failure: true
   parallel:
     matrix:
       - GENERATOR: [cpu_parameters, disk_reservation, environments, group_storage, hardware, kwollect_metrics, oar_properties, status]
         SITE: global
+        DEBIAN_VERSION: [bullseye, bookworm]
       - GENERATOR: [site_hardware, site_network]
         SITE: [grenoble, lille, luxembourg, lyon, nancy, nantes, rennes, sophia, toulouse]
+        DEBIAN_VERSION: [bullseye, bookworm]
   script:
     - echo "$GRID5000_API" > "${HOME}/.grid5000_api.yml"
     - bundle exec rake gen:wiki NAME=${GENERATOR} SITE=${SITE} DO=diff
@@ -44,7 +59,7 @@ wikigen:
       - master
 
 generate-reference-api:
-  extends: .test-for-bullseye
+  extends: .template-refrepo
   stage: generate
   script:
     - export TZ=Europe/Paris
@@ -63,14 +78,14 @@ deploy:
 
 rspec:
   stage: deploy  # we use 'deploy' here to avoid blocking on this when updating the ref-repo
-  extends: .test-for-bullseye
+  extends: .template-refrepo
   script:
     - export TZ=Europe/Paris
     - bundle exec rspec
 
 valid-homogeneity:
   stage: deploy
-  extends: .test-for-bullseye
+  extends: .template-refrepo
   script:
     - wget --no-check-certificate -q https://www.grid5000.fr/certs/ca2019.grid5000.fr.crt -O /usr/local/share/ca-certificates/ca2019.grid5000.fr.crt
     - /usr/sbin/update-ca-certificates
diff --git a/.ruby-version b/.ruby-version
index 30c59922eb906eb9b33584c430ba903741e2a84b..872e1208191c20d425a6c44a79bf9720e3b76cde 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-ruby-2.7.4
+ruby-3.1.0
diff --git a/lib/refrepo/input_loader.rb b/lib/refrepo/input_loader.rb
index 228927ba3bcd93da0ebd8723b7f67d70a2ba7c51..59de7309280b1df083d776fb65457474caeee975 100644
--- a/lib/refrepo/input_loader.rb
+++ b/lib/refrepo/input_loader.rb
@@ -16,14 +16,21 @@ def load_yaml_file_hierarchy(directory = File.expand_path("../../input/grid5000/
     # => List deepest files first as they have lowest priority when hash keys are duplicated.
     list_of_yaml_files = Dir['**/*.y*ml', '**/*.y*ml.erb'].sort_by { |x| -x.count('/') }
 
+    load_args = {}
+    if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new("3.1.0")
+      # Fix compatibility with ruby 3.1
+      load_args[:permitted_classes] = [Date, Time]
+      load_args[:aliases] = true
+    end
+
     list_of_yaml_files.each { |filename|
       begin
         # Load YAML
         if /\.y.*ml\.erb$/.match(filename)
           # For files with .erb.yaml extensions, process the template before loading the YAML.
-          file_hash = YAML::load(ERB.new(File.read(filename)).result(binding))
+          file_hash = YAML::load(ERB.new(File.read(filename)).result(binding), **load_args)
         else
-          file_hash = YAML::load_file(filename)
+          file_hash = YAML::load_file(filename, **load_args)
         end
       if not file_hash
         raise StandardError.new("loaded hash is empty")