diff --git a/build.gradle b/build.gradle
index 398816052..71d6bf6c2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -158,7 +158,7 @@ jib {
run{
def envs = findProperty('micronautEnvs')
- def args = ["-Dmicronaut.environments=$envs","-Djdk.tracePinnedThreads=short"]
+ def args = ["-Dmicronaut.environments=$envs","-Djdk.tracePinnedThreads=short", "--add-opens","java.base/java.lang=ALL-UNNAMED"]
if( environment['JVM_OPTS'] ) args.add(environment['JVM_OPTS'])
jvmArgs args
systemProperties 'DOCKER_USER': project.findProperty('DOCKER_USER') ?: environment['DOCKER_USER'],
diff --git a/src/main/groovy/io/seqera/wave/metrics/ExecutorsMetricsBinder.groovy b/src/main/groovy/io/seqera/wave/metrics/ExecutorsMetricsBinder.groovy
new file mode 100644
index 000000000..2f1a1000e
--- /dev/null
+++ b/src/main/groovy/io/seqera/wave/metrics/ExecutorsMetricsBinder.groovy
@@ -0,0 +1,73 @@
+/*
+ * Wave, containers provisioning service
+ * Copyright (c) 2023-2024, Seqera Labs
+ *
+ * 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 .
+ */
+
+package io.seqera.wave.metrics
+
+import java.lang.reflect.Field
+import java.util.concurrent.ForkJoinPool
+
+import groovy.transform.CompileStatic
+import groovy.util.logging.Slf4j
+import io.micrometer.core.instrument.MeterRegistry
+import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics
+import io.micronaut.context.annotation.Context
+import jakarta.annotation.PostConstruct
+import jakarta.inject.Inject
+/**
+ * Register Micrometer metrics for ForkJoin commonPool and virtual threads scheduler
+ *
+ * @author Paolo Di Tommaso
+ */
+@Slf4j
+@Context
+@CompileStatic
+class ExecutorsMetricsBinder {
+
+ @Inject
+ private MeterRegistry registry
+
+ @PostConstruct
+ void register() {
+ log.info "+ Registering executor metrics binder"
+ registerCommonPoolMetrics(registry)
+ registerVirtualThreadPoolMetrics(registry)
+ }
+
+ void registerCommonPoolMetrics(MeterRegistry registry) {
+ final commonPool = ForkJoinPool.commonPool()
+ ExecutorServiceMetrics.monitor(registry, commonPool, "ForkJoin.commonPool")
+ }
+
+ void registerVirtualThreadPoolMetrics(MeterRegistry registry) {
+ try {
+ // Create a virtual thread executor
+ Class> VirtualThread = Class.forName("java.lang.VirtualThread");
+
+ // Use reflection to get the internal ForkJoinPool
+ Field poolField = VirtualThread.getDeclaredField("DEFAULT_SCHEDULER");
+ poolField.setAccessible(true);
+ ForkJoinPool virtualThreadPool = (ForkJoinPool) poolField.get(null);
+
+ // Register metrics for the virtual thread pool
+ ExecutorServiceMetrics.monitor(registry, virtualThreadPool, "ForkJoin.virtualPool")
+ }
+ catch (Exception e) {
+ log.warn "Unable to registry carrier threads pool metrics", e
+ }
+ }
+}