You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/07/12 01:13:26 UTC

[camel-karavan] branch main updated: DatagridService in karaan-bashi #817

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new 1989b7b1 DatagridService in karaan-bashi #817
1989b7b1 is described below

commit 1989b7b10200da93b4a109a9c74edf8ae1798ec3
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Tue Jul 11 21:13:17 2023 -0400

    DatagridService in karaan-bashi #817
---
 karavan-cloud/karavan-app/pom.xml                  |   4 -
 .../{RunnerResource.java => DevModeResource.java}  |  27 +--
 .../camel/karavan/api/KubernetesResource.java      |  31 +--
 .../camel/karavan/api/ProjectFileResource.java     |  13 +-
 .../camel/karavan/api/ProjectGitResource.java      |   2 +-
 .../apache/camel/karavan/api/ProjectResource.java  |  45 +++--
 .../apache/camel/karavan/api/StatusResource.java   |  21 +-
 .../karavan/handler/DeploymentEventHandler.java    |   2 +-
 .../camel/karavan/handler/PodEventHandler.java     |  33 ++--
 .../karavan/listener/DevModeCommandListener.java   |   6 +-
 .../apache/camel/karavan/service/CodeService.java  |  96 ++++++++-
 .../{RunnerService.java => DevModeService.java}    | 130 ++++++++----
 .../apache/camel/karavan/service/GitService.java   |   7 +-
 .../camel/karavan/service/KubernetesService.java   |   5 +-
 .../camel/karavan/service/ProjectService.java      |  61 +++---
 .../apache/camel/karavan/service/ServiceUtil.java  | 109 -----------
 .../camel/karavan/service/StatusService.java       | 218 ---------------------
 .../src/main/resources/application.properties      |   4 +-
 .../src/main/webui/src/api/KaravanApi.tsx          |  10 +-
 karavan-cloud/karavan-bashi/pom.xml                |  13 +-
 .../camel/karavan/bashi/ConductorService.java      |  41 ++--
 .../org/apache/camel/karavan/bashi/Constants.java  |   4 +-
 .../camel/karavan/bashi/RunnerStatusService.java   |  83 --------
 .../karavan/bashi/docker/DockerEventListener.java  |  13 +-
 .../bashi/infinispan/ClientRunnerListener.java     |  49 -----
 .../camel/karavan/bashi/infinispan/GroupedKey.java |  58 ------
 .../bashi/infinispan/InfinispanService.java        | 122 ------------
 .../camel/karavan/bashi/infinispan/PodStatus.java  | 209 --------------------
 .../bashi/infinispan/ProjectStoreSchema.java       |   8 -
 .../karavan/bashi/infinispan/RunnerCommand.java    |  13 --
 .../src/main/resources/application.properties      |   6 +-
 karavan-cloud/karavan-datagrid/pom.xml             |   2 +-
 .../camel/karavan/datagrid/DatagridService.java    | 153 +++++++++------
 .../karavan/datagrid/model/DeploymentStatus.java   |  22 +--
 .../karavan/datagrid/model/DevModeStatus.java      |  19 +-
 .../camel/karavan/datagrid/model/GroupedKey.java   |  38 ++--
 .../karavan/datagrid/model/KaravanSchema.java      |   3 +-
 .../camel/karavan/datagrid/model/PodStatus.java    |   9 +
 .../karavan/datagrid/model/ServiceStatus.java      |  22 +--
 karavan-cloud/pom.xml                              |   2 +-
 .../camel/karavan/generator/KaravanGenerator.java  |   2 +-
 41 files changed, 512 insertions(+), 1203 deletions(-)

diff --git a/karavan-cloud/karavan-app/pom.xml b/karavan-cloud/karavan-app/pom.xml
index 56f58bde..bc3a7a07 100644
--- a/karavan-cloud/karavan-app/pom.xml
+++ b/karavan-cloud/karavan-app/pom.xml
@@ -31,10 +31,6 @@
             <artifactId>karavan-datagrid</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-vertx</artifactId>
-        </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-smallrye-reactive-messaging</artifactId>
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
similarity index 83%
rename from karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
rename to karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
index dcd9f04f..0984076e 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
@@ -18,7 +18,7 @@ package org.apache.camel.karavan.api;
 
 import org.apache.camel.karavan.datagrid.DatagridService;
 import org.apache.camel.karavan.datagrid.model.*;
-import org.apache.camel.karavan.service.RunnerService;
+import org.apache.camel.karavan.service.DevModeService;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 import javax.inject.Inject;
@@ -34,17 +34,16 @@ import javax.ws.rs.core.Response;
 import java.time.Instant;
 import java.util.Optional;
 
-import static org.apache.camel.karavan.service.RunnerService.RUNNER_SUFFIX;
-import static org.apache.camel.karavan.service.RunnerService.STATUS_NEED_INITIAL_LOAD;
+import static org.apache.camel.karavan.service.DevModeService.DEVMODE_SUFFIX;
 
-@Path("/api/runner")
-public class RunnerResource {
+@Path("/api/devmode")
+public class DevModeResource {
 
     @ConfigProperty(name = "karavan.environment")
     String environment;
 
     @Inject
-    RunnerService runnerServices;
+    DevModeService devModeService;
 
     @Inject
     DatagridService datagridService;
@@ -54,11 +53,11 @@ public class RunnerResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{jBangOptions}")
     public Response runProjectWithJBangOptions(Project project, @PathParam("jBangOptions") String jBangOptions) {
-        String runnerName = project.getProjectId() + "-" + RUNNER_SUFFIX;
+        String runnerName = project.getProjectId() + "-" + DEVMODE_SUFFIX;
         PodStatus status = datagridService.getDevModePodStatuses(runnerName, environment);
         if (status == null) {
-            datagridService.saveDevModeStatus(new DevModeStatus(project.getProjectId(), null, false));
-            datagridService.sendDevModeCommand(project.getProjectId(), new DevModeCommand(CommandName.DELETE, Instant.now().toEpochMilli()));
+            datagridService.saveDevModeStatus(new DevModeStatus(project.getProjectId(), null, null, false));
+            datagridService.sendDevModeCommand(project.getProjectId(), new DevModeCommand(CommandName.RUN, Instant.now().toEpochMilli()));
             return Response.ok(runnerName).build();
         }
         return Response.notModified().build();
@@ -75,7 +74,10 @@ public class RunnerResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/reload/{projectId}")
     public Response reload(@PathParam("projectId") String projectId) {
-        runnerServices.reloadProjectCode(projectId);
+        devModeService.reloadProjectCode(projectId);
+        DevModeStatus dms = datagridService.getDevModeStatus(projectId);
+        dms.setCodeLoaded(true);
+        datagridService.saveDevModeStatus(dms);
         return Response.ok().build();
     }
 
@@ -85,6 +87,7 @@ public class RunnerResource {
     @Path("/{projectId}/{deletePVC}")
     public Response deleteRunner(@PathParam("projectId") String projectId, @PathParam("deletePVC") boolean deletePVC) {
         datagridService.sendDevModeCommand(projectId, new DevModeCommand(CommandName.DELETE, Instant.now().toEpochMilli()));
+        datagridService.deleteDevModeStatus(projectId);
         return Response.accepted().build();
     }
 
@@ -92,7 +95,7 @@ public class RunnerResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pod/{projectId}")
     public Response getPodStatus(@PathParam("projectId") String projectId) {
-        String runnerName = projectId + "-" + RUNNER_SUFFIX;
+        String runnerName = projectId + "-" + DEVMODE_SUFFIX;
         Optional<PodStatus> ps =  datagridService.getPodStatuses(projectId, environment).stream()
                 .filter(podStatus -> podStatus.getName().equals(runnerName))
                 .findFirst();
@@ -107,7 +110,7 @@ public class RunnerResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/console/{projectId}/{statusName}")
     public Response getCamelStatusByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("statusName") String statusName) {
-        String name = projectId + "-" + RUNNER_SUFFIX;
+        String name = projectId + "-" + DEVMODE_SUFFIX;
         CamelStatus status = datagridService.getCamelStatus(name, statusName, environment);
         if (status != null) {
             return Response.ok(status).build();
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
index 572942fb..6ff1020e 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java
@@ -19,10 +19,11 @@ package org.apache.camel.karavan.api;
 import io.smallrye.mutiny.Multi;
 import io.vertx.mutiny.core.eventbus.EventBus;
 import io.vertx.mutiny.core.eventbus.Message;
-import org.apache.camel.karavan.model.DeploymentStatus;
-import org.apache.camel.karavan.model.PodStatus;
-import org.apache.camel.karavan.model.Project;
-import org.apache.camel.karavan.model.ServiceStatus;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.DeploymentStatus;
+import org.apache.camel.karavan.datagrid.model.PodStatus;
+import org.apache.camel.karavan.datagrid.model.Project;
+import org.apache.camel.karavan.datagrid.model.ServiceStatus;
 import org.apache.camel.karavan.service.KubernetesService;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
@@ -57,7 +58,7 @@ public class KubernetesResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/pipeline/{env}")
     public String createPipeline(@PathParam("env") String env, Project project) throws Exception {
-        Project p = infinispanService.getProject(project.getProjectId());
+        Project p = datagridService.getProject(project.getProjectId());
         return kubernetesService.createPipelineRun(project);
     }
 
@@ -98,8 +99,8 @@ public class KubernetesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/deployment")
     public List<DeploymentStatus> getAllDeploymentStatuses() throws Exception {
-        return infinispanService.getDeploymentStatuses().stream()
-                .sorted(Comparator.comparing(DeploymentStatus::getName))
+        return datagridService.getDeploymentStatuses().stream()
+                .sorted(Comparator.comparing(DeploymentStatus::getProjectId))
                 .collect(Collectors.toList());
     }
 
@@ -107,8 +108,8 @@ public class KubernetesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/deployment/{env}")
     public List<DeploymentStatus> getDeploymentStatusesByEnv(@PathParam("env") String env) throws Exception {
-        return infinispanService.getDeploymentStatuses(env).stream()
-                .sorted(Comparator.comparing(DeploymentStatus::getName))
+        return datagridService.getDeploymentStatuses(env).stream()
+                .sorted(Comparator.comparing(DeploymentStatus::getProjectId))
                 .collect(Collectors.toList());
     }
 
@@ -133,8 +134,8 @@ public class KubernetesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/service")
     public List<ServiceStatus> getAllServiceStatuses() throws Exception {
-        return infinispanService.getServiceStatuses().stream()
-                .sorted(Comparator.comparing(ServiceStatus::getName))
+        return datagridService.getServiceStatuses().stream()
+                .sorted(Comparator.comparing(ServiceStatus::getProjectId))
                 .collect(Collectors.toList());
     }
 
@@ -142,8 +143,8 @@ public class KubernetesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pod/{env}")
     public List<PodStatus> getPodStatusesByEnv(@PathParam("env") String env) throws Exception {
-        return infinispanService.getPodStatuses(env).stream()
-                .sorted(Comparator.comparing(PodStatus::getName))
+        return datagridService.getPodStatuses(env).stream()
+                .sorted(Comparator.comparing(PodStatus::getProjectId))
                 .collect(Collectors.toList());
     }
 
@@ -151,8 +152,8 @@ public class KubernetesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pod/{projectId}/{env}")
     public List<PodStatus> getPodStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception {
-        return infinispanService.getPodStatuses(projectId, env).stream()
-                .filter(podStatus -> !podStatus.getRunner())
+        return datagridService.getPodStatuses(projectId, env).stream()
+                .filter(podStatus -> !podStatus.getInDevMode())
                 .sorted(Comparator.comparing(PodStatus::getName))
                 .collect(Collectors.toList());
     }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
index 651e6e67..3d693921 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
@@ -16,7 +16,8 @@
  */
 package org.apache.camel.karavan.api;
 
-import org.apache.camel.karavan.model.ProjectFile;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.ProjectFile;
 import org.apache.camel.karavan.service.CodeService;
 
 import javax.inject.Inject;
@@ -50,7 +51,7 @@ public class ProjectFileResource {
     @Path("/{projectId}")
     public List<ProjectFile> get(@HeaderParam("username") String username,
                                  @PathParam("projectId") String projectId) throws Exception {
-        return infinispanService.getProjectFiles(projectId).stream()
+        return datagridService.getProjectFiles(projectId).stream()
                 .sorted(Comparator.comparing(ProjectFile::getName))
                 .collect(Collectors.toList());
     }
@@ -60,7 +61,7 @@ public class ProjectFileResource {
     @Consumes(MediaType.APPLICATION_JSON)
     public ProjectFile save(ProjectFile file) throws Exception {
         file.setLastUpdate(Instant.now().toEpochMilli());
-        infinispanService.saveProjectFile(file);
+        datagridService.saveProjectFile(file);
         return file;
     }
 
@@ -70,7 +71,7 @@ public class ProjectFileResource {
     public void delete(@HeaderParam("username") String username,
                        @PathParam("project") String project,
                        @PathParam("filename") String filename) throws Exception {
-        infinispanService.deleteProjectFile(
+        datagridService.deleteProjectFile(
                 URLDecoder.decode(project, StandardCharsets.UTF_8.toString()),
                 URLDecoder.decode(filename, StandardCharsets.UTF_8.toString())
         );
@@ -84,11 +85,11 @@ public class ProjectFileResource {
                                    @PathParam("integrationName") String integrationName,
                                    @PathParam("generateRest") boolean generateRest,
                                    @PathParam("generateRoutes") boolean generateRoutes, ProjectFile file) throws Exception {
-        infinispanService.saveProjectFile(file);
+        datagridService.saveProjectFile(file);
         if (generateRest) {
             String yaml = codeService.generate(file.getName(), file.getCode(), generateRoutes);
             ProjectFile integration = new ProjectFile(integrationName, yaml, file.getProjectId(), Instant.now().toEpochMilli());
-            infinispanService.saveProjectFile(integration);
+            datagridService.saveProjectFile(integration);
             return file;
         }
         return file;
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java
index f2d0196f..c6c67825 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java
@@ -16,7 +16,7 @@
  */
 package org.apache.camel.karavan.api;
 
-import org.apache.camel.karavan.model.Project;
+import org.apache.camel.karavan.datagrid.model.Project;
 import org.apache.camel.karavan.service.ProjectService;
 
 import javax.inject.Inject;
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
index dde6c2cd..5d2049ca 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
@@ -16,9 +16,11 @@
  */
 package org.apache.camel.karavan.api;
 
-import org.apache.camel.karavan.model.GroupedKey;
-import org.apache.camel.karavan.model.Project;
-import org.apache.camel.karavan.model.ProjectFile;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.GroupedKey;
+import org.apache.camel.karavan.datagrid.model.Project;
+import org.apache.camel.karavan.datagrid.model.ProjectFile;
+import org.apache.camel.karavan.service.CodeService;
 import org.apache.camel.karavan.service.GitService;
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
@@ -45,10 +47,13 @@ public class ProjectResource {
     @Inject
     GitService gitService;
 
+    @Inject
+    CodeService codeService;
+
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public List<Project> getAll() throws Exception {
-        return infinispanService.getProjects().stream()
+        return datagridService.getProjects().stream()
                 .sorted((p1, p2) -> {
                     if (p1.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
                     if (p2.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)) return 1;
@@ -63,14 +68,19 @@ public class ProjectResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/{project}")
     public Project get(@PathParam("project") String project) throws Exception {
-        return infinispanService.getProject(project);
+        return datagridService.getProject(project);
     }
 
     @POST
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     public Project save(Project project) throws Exception {
-        infinispanService.saveProject(project, false);
+        boolean isNew = datagridService.getProject(project.getProjectId()) != null;
+        datagridService.saveProject(project);
+        if (isNew){
+            ProjectFile appProp = codeService.getApplicationProperties(project);
+            datagridService.saveProjectFile(appProp);
+        }
         return project;
     }
 
@@ -80,9 +90,9 @@ public class ProjectResource {
     public void delete(@HeaderParam("username") String username,
                           @PathParam("project") String project) throws Exception {
         String projectId = URLDecoder.decode(project, StandardCharsets.UTF_8.toString());
-        gitService.deleteProject(projectId, infinispanService.getProjectFiles(projectId));
-        infinispanService.getProjectFiles(projectId).forEach(file -> infinispanService.deleteProjectFile(projectId, file.getName()));
-        infinispanService.deleteProject(projectId);
+        gitService.deleteProject(projectId, datagridService.getProjectFiles(projectId));
+        datagridService.getProjectFiles(projectId).forEach(file -> datagridService.deleteProjectFile(projectId, file.getName()));
+        datagridService.deleteProject(projectId);
     }
 
     @POST
@@ -91,13 +101,20 @@ public class ProjectResource {
     @Path("/copy/{sourceProject}")
     public Project copy(@PathParam("sourceProject") String sourceProject, Project project) throws Exception {
 //        Save project
-        Project s = infinispanService.getProject(sourceProject);
+        Project s = datagridService.getProject(sourceProject);
         project.setRuntime(s.getRuntime());
-        infinispanService.saveProject(project, false);
+        datagridService.saveProject(project);
 //        Copy files
-        Map<GroupedKey, ProjectFile> map = infinispanService.getProjectFiles(sourceProject).stream()
-                .collect(Collectors.toMap(f -> new GroupedKey(project.getProjectId(), f.getName()), f -> f));
-        infinispanService.saveProjectFiles(map);
+        Map<GroupedKey, ProjectFile> map = datagridService.getProjectFilesMap(sourceProject).entrySet().stream()
+                .collect(Collectors.toMap(
+                        e -> new GroupedKey(project.getProjectId(), e.getKey().getEnv(), e.getKey().getKey()),
+                        e -> {
+                            ProjectFile file = e.getValue();
+                            file.setProjectId(project.getProjectId());
+                            return file;
+                        })
+                );
+        datagridService.saveProjectFiles(map);
         return project;
     }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
index eaac70e3..2df5a937 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
@@ -17,12 +17,8 @@
 package org.apache.camel.karavan.api;
 
 import io.vertx.core.eventbus.EventBus;
-import io.vertx.core.json.JsonObject;
-import org.apache.camel.karavan.model.CamelStatus;
-import org.apache.camel.karavan.model.DeploymentStatus;
-import org.apache.camel.karavan.model.Environment;
-import org.apache.camel.karavan.model.PipelineStatus;
-import org.apache.camel.karavan.service.StatusService;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.*;
 import org.jboss.logging.Logger;
 
 import javax.inject.Inject;
@@ -33,7 +29,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 
 @Path("/api/status")
@@ -51,7 +46,7 @@ public class StatusResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/pipeline/{projectId}/{env}")
     public Response getPipelineStatus(@PathParam("projectId") String projectId, @PathParam("env") String env) {
-        PipelineStatus status = infinispanService.getPipelineStatus(projectId, env);
+        PipelineStatus status = datagridService.getPipelineStatus(projectId, env);
         if (status != null) {
             return Response.ok(status).build();
         } else {
@@ -63,9 +58,9 @@ public class StatusResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/deployment/{name}/{env}")
     public Response getDeploymentStatus(@PathParam("name") String name, @PathParam("env") String env) {
-        Optional<Environment> environment = infinispanService.getEnvironments().stream().filter(e -> e.getName().equals(env)).findFirst();
+        Optional<Environment> environment = datagridService.getEnvironments().stream().filter(e -> e.getName().equals(env)).findFirst();
         if (environment.isPresent()){
-            DeploymentStatus status = infinispanService.getDeploymentStatus(name, environment.get().getNamespace(), environment.get().getCluster());
+            DeploymentStatus status = datagridService.getDeploymentStatus(name, env);
             if (status != null) {
                 return Response.ok(status).build();
             }
@@ -77,8 +72,7 @@ public class StatusResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/camel/{projectId}/{env}")
     public Response getCamelStatusByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) {
-        bus.publish(StatusService.CMD_COLLECT_PROJECT_STATUS, new JsonObject(Map.of("projectId", projectId, "env", env)));
-        CamelStatus status = infinispanService.getCamelStatus(projectId, env);
+        CamelStatus status = datagridService.getCamelStatus(projectId, env, CamelStatusName.context.name());
         if (status != null) {
             return Response.ok(status).build();
         } else {
@@ -90,7 +84,6 @@ public class StatusResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/camel/{env}")
     public List<CamelStatus> getCamelStatusByEnv(@PathParam("env") String env) {
-        bus.publish(StatusService.CMD_COLLECT_ALL_STATUSES, "");
-        return infinispanService.getCamelStatuses(env);
+        return datagridService.getCamelStatusesByEnv(env, CamelStatusName.context.name());
     }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/DeploymentEventHandler.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/DeploymentEventHandler.java
index 615e2e1e..8cb28816 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/DeploymentEventHandler.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/DeploymentEventHandler.java
@@ -50,7 +50,7 @@ public class DeploymentEventHandler implements ResourceEventHandler<Deployment>
                     kubernetesService.getCluster(),
                     kubernetesService.environment);
             datagridService.deleteDeploymentStatus(ds);
-            datagridService.deleteCamelStatus(ds.getName(), ds.getEnv());
+            datagridService.deleteCamelStatuses(deployment.getMetadata().getName(), ds.getEnv());
         } catch (Exception e){
             LOGGER.error(e.getMessage());
         }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
index 604682fc..3ae8a199 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java
@@ -10,8 +10,8 @@ import org.apache.camel.karavan.datagrid.model.PodStatus;
 import org.apache.camel.karavan.service.KubernetesService;
 import org.jboss.logging.Logger;
 
-import static org.apache.camel.karavan.service.RunnerService.RUNNER_SUFFIX;
-import static org.apache.camel.karavan.service.ServiceUtil.DEFAULT_CONTAINER_RESOURCES;
+import static org.apache.camel.karavan.service.CodeService.DEFAULT_CONTAINER_RESOURCES;
+import static org.apache.camel.karavan.service.DevModeService.DEVMODE_SUFFIX;
 
 public class PodEventHandler implements ResourceEventHandler<Pod> {
 
@@ -51,12 +51,8 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
         try {
             LOGGER.info("onDelete " + pod.getMetadata().getName());
             String deployment = pod.getMetadata().getLabels().get("app");
-            String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
-            PodStatus ps = new PodStatus(
-                    pod.getMetadata().getName(),
-                    project,
-                    kubernetesService.environment);
-            datagridService.deletePodStatus(ps);
+            String projectId = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
+            datagridService.deletePodStatus(projectId, kubernetesService.environment, pod.getMetadata().getName());
         } catch (Exception e){
             LOGGER.error(e.getMessage(), e.getCause());
         }
@@ -67,10 +63,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
         String deployment = pod.getMetadata().getLabels().get("app");
         String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId");
         try {
-            boolean initialized = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Initialized"));
             boolean ready = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Ready"));
-            boolean terminating = pod.getMetadata().getDeletionTimestamp() != null;
-            String creationTimestamp = pod.getMetadata().getCreationTimestamp();
 
             ResourceRequirements defaultRR = kubernetesService.getResourceRequirements(DEFAULT_CONTAINER_RESOURCES);
             ResourceRequirements resourceRequirements = pod.getSpec().getContainers().stream().findFirst()
@@ -82,27 +75,23 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             String limitCpu = resourceRequirements.getLimits().getOrDefault("cpu", new Quantity()).toString();
             return new PodStatus(
                     pod.getMetadata().getName(),
-                    pod.getStatus().getPhase(),
-                    initialized,
                     ready,
-                    terminating,
-                    pod.getStatus().getReason(),
                     deployment,
                     project,
                     kubernetesService.environment,
-                    deployment == null || pod.getMetadata().getName().endsWith(RUNNER_SUFFIX),
-                    requestMemory,
-                    requestCpu,
-                    limitMemory,
-                    limitCpu,
-                    creationTimestamp
+                    deployment == null || pod.getMetadata().getName().endsWith(DEVMODE_SUFFIX),
+                    requestMemory + " : " + limitMemory,
+                    requestCpu + " : " + limitCpu
             );
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage(), ex.getCause());
             return new PodStatus(
                     pod.getMetadata().getName(),
+                    false,
+                    null,
                     project,
-                    kubernetesService.environment);
+                    kubernetesService.environment,
+                    false);
         }
     }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
index 1c7aed52..9a66fb71 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/listener/DevModeCommandListener.java
@@ -14,7 +14,7 @@ import javax.inject.Inject;
 
 import java.util.Objects;
 
-import static org.apache.camel.karavan.service.RunnerService.RUNNER_SUFFIX;
+import static org.apache.camel.karavan.service.DevModeService.DEVMODE_SUFFIX;
 
 @ApplicationScoped
 public class DevModeCommandListener {
@@ -33,13 +33,13 @@ public class DevModeCommandListener {
         System.out.println("receiveCommand " + message);
         if (kubernetesService.inKubernetes()) {
             DevModeCommand command = message.mapTo(DevModeCommand.class);
-            String runnerName = command.getProjectId() + "-" + RUNNER_SUFFIX;
+            String runnerName = command.getProjectId() + "-" + DEVMODE_SUFFIX;
             if (Objects.equals(command.getCommandName(), CommandName.RUN)) {
                 Project p = datagridService.getProject(command.getProjectId());
                 kubernetesService.tryCreateRunner(p, runnerName, "");
             } else if (Objects.equals(command.getCommandName(), CommandName.DELETE)){
                 kubernetesService.deleteRunner(runnerName, false);
-                datagridService.deleteCamelStatus(command.getProjectId(), environment);
+                datagridService.deleteDevModeStatus(command.getProjectId());
             }
         }
     }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
index 1c9ca5cb..572d64e8 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java
@@ -27,6 +27,8 @@ import org.apache.camel.generator.openapi.RestDslGenerator;
 import org.apache.camel.impl.lw.LightweightCamelContext;
 import org.apache.camel.karavan.api.KameletResources;
 import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.GitRepo;
+import org.apache.camel.karavan.datagrid.model.GitRepoFile;
 import org.apache.camel.karavan.datagrid.model.Project;
 import org.apache.camel.karavan.datagrid.model.ProjectFile;
 import org.jboss.logging.Logger;
@@ -39,19 +41,15 @@ import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.time.Instant;
+import java.util.*;
 import java.util.stream.Collectors;
 
-import static org.apache.camel.karavan.service.ServiceUtil.APPLICATION_PROPERTIES_FILENAME;
-
 @ApplicationScoped
 public class CodeService {
 
     private static final Logger LOGGER = Logger.getLogger(CodeService.class.getName());
+    public static final String APPLICATION_PROPERTIES_FILENAME = "application.properties";
 
     @Inject
     KubernetesService kubernetesService;
@@ -66,17 +64,25 @@ public class CodeService {
     List<String> targets = List.of("openshift", "kubernetes");
     List<String> interfaces = List.of("org.apache.camel.AggregationStrategy.java", "org.apache.camel.Processor.java");
 
-    public String getApplicationProperties(Project project) {
+    public static final Map<String, String> DEFAULT_CONTAINER_RESOURCES = Map.of(
+            "requests.memory", "512Mi",
+            "requests.cpu", "500m",
+            "limits.memory", "2048Mi",
+            "limits.cpu", "2000m"
+    );
+
+    public ProjectFile getApplicationProperties(Project project) {
         String target = kubernetesService.isOpenshift() ? "openshift" : "kubernetes";
         String templateName = project.getRuntime() + "-" + target + "-" + APPLICATION_PROPERTIES_FILENAME;
         String templateText = getTemplateText(templateName);
         Template result = engine.parse(templateText);
-        return result
+        String code =  result
                 .data("projectId", project.getProjectId())
                 .data("projectName", project.getName())
                 .data("projectDescription", project.getDescription())
                 .data("namespace", kubernetesService.getNamespace())
                 .render();
+        return new ProjectFile(APPLICATION_PROPERTIES_FILENAME, code, project.getProjectId(), Instant.now().toEpochMilli());
     }
 
     private String getTemplateText(String fileName) {
@@ -159,4 +165,76 @@ public class CodeService {
         return mapper.convertValue(map, JsonNode.class);
     }
 
+    public static Map<String, String> getRunnerContainerResourcesMap(ProjectFile propertiesFile, boolean isOpenshift, boolean isQuarkus) {
+        if (!isQuarkus) {
+            return DEFAULT_CONTAINER_RESOURCES;
+        } else {
+            Map<String, String> result = new HashMap<>();
+            String patternPrefix = isOpenshift ? "quarkus.openshift.resources." : "quarkus.kubernetes.resources.";
+            String devPatternPrefix = "%dev." + patternPrefix;
+
+            List<String> lines = propertiesFile.getCode().lines().collect(Collectors.toList());
+
+            DEFAULT_CONTAINER_RESOURCES.forEach((key, value) -> {
+                Optional<String> dev = lines.stream().filter(l -> l.startsWith(devPatternPrefix + key)).findFirst();
+                if (dev.isPresent()) {
+                    result.put(key, CodeService.getValueForProperty(dev.get(), devPatternPrefix + key));
+                } else {
+                    Optional<String> prod = lines.stream().filter(l -> l.startsWith(patternPrefix + key)).findFirst();
+                    if (prod.isPresent()){
+                        result.put(key, CodeService.getValueForProperty(prod.get(), patternPrefix + key));
+                    } else {
+                        result.put(key, value);
+                    }
+                }
+            });
+            return result;
+        }
+    }
+
+    public String getPropertiesFile(GitRepo repo) {
+        try {
+            for (GitRepoFile e : repo.getFiles()){
+                if (e.getName().equalsIgnoreCase(APPLICATION_PROPERTIES_FILENAME)) {
+                    return e.getBody();
+                }
+            }
+        } catch (Exception e) {
+
+        }
+        return null;
+    }
+
+    public String capitalize(String str) {
+        if(str == null || str.isEmpty()) {
+            return str;
+        }
+        return str.substring(0, 1).toUpperCase() + str.substring(1);
+    }
+
+    public static String getProperty(String file, String property) {
+        String prefix = property + "=";
+        return  Arrays.stream(file.split(System.lineSeparator())).filter(s -> s.startsWith(prefix))
+                .findFirst().orElseGet(() -> "")
+                .replace(prefix, "");
+    }
+
+    public static String getValueForProperty(String line, String property) {
+        String prefix = property + "=";
+        return  line.replace(prefix, "");
+    }
+
+    public String getProjectDescription(String file) {
+        String description = getProperty(file, "camel.jbang.project-description");
+        return description != null && !description.isBlank() ? description : getProperty(file, "camel.karavan.project-description");
+    }
+
+    public String getProjectName(String file) {
+        String name = getProperty(file, "camel.jbang.project-name");
+        return name != null && !name.isBlank() ? name : getProperty(file, "camel.karavan.project-name");
+    }
+
+    public String getProjectRuntime(String file) {
+        return getProperty(file, "camel.jbang.runtime");
+    }
 }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/DevModeService.java
similarity index 50%
rename from karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
rename to karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/DevModeService.java
index 6e1a4d2b..da50f844 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/DevModeService.java
@@ -25,6 +25,7 @@ import io.vertx.mutiny.core.eventbus.EventBus;
 import io.vertx.mutiny.ext.web.client.HttpResponse;
 import io.vertx.mutiny.ext.web.client.WebClient;
 import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.*;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
 import org.jboss.logging.Logger;
@@ -35,14 +36,13 @@ import java.util.Arrays;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
-
 @ApplicationScoped
-public class RunnerService {
+public class DevModeService {
 
-    private static final Logger LOGGER = Logger.getLogger(RunnerService.class.getName());
-    public static final String CMD_COLLECT_RUNNER_STATUS = "collect-runner-status";
-    public static final String RUNNER_SUFFIX = "runner";
-    public static final String STATUS_NEED_INITIAL_LOAD = "NEED_INITIAL_LOAD";
+    private static final Logger LOGGER = Logger.getLogger(DevModeService.class.getName());
+    public static final String CMD_COLLECT_CAMEL_STATUS = "collect-camel-status";
+    public static final String CMD_DELETE_CAMEL_STATUS = "delete-camel-status";
+    public static final String DEVMODE_SUFFIX = "devmode";
 
     @Inject
     DatagridService datagridService;
@@ -70,10 +70,10 @@ public class RunnerService {
 
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
-        String runnerName = projectId + "-" + RUNNER_SUFFIX;
+        String containerName = projectId + "-" + DEVMODE_SUFFIX;
         try {
-            datagridService.getProjectFiles(projectId).forEach(projectFile -> putRequest(runnerName, projectFile.getName(), projectFile.getCode(), 1000));
-            reloadRequest(runnerName);
+            datagridService.getProjectFiles(projectId).forEach(projectFile -> putRequest(containerName, projectFile.getName(), projectFile.getCode(), 1000));
+            reloadRequest(containerName);
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage());
         }
@@ -81,9 +81,9 @@ public class RunnerService {
     }
 
     @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000)
-    public boolean putRequest(String runnerName, String fileName, String body, int timeout) {
+    public boolean putRequest(String containerName, String fileName, String body, int timeout) {
         try {
-            String url = getRunnerAddress(runnerName) + "/q/upload/" + fileName;
+            String url = getContainerAddress(containerName) + "/q/upload/" + fileName;
             HttpResponse<Buffer> result = getWebClient().putAbs(url)
                     .timeout(timeout).sendBuffer(Buffer.buffer(body)).subscribeAsCompletionStage().toCompletableFuture().get();
             return result.statusCode() == 200;
@@ -93,8 +93,8 @@ public class RunnerService {
         return false;
     }
 
-    public String reloadRequest(String runnerName) {
-        String url = getRunnerAddress(runnerName) + "/q/dev/reload?reload=true";
+    public String reloadRequest(String containerName) {
+        String url = getContainerAddress(containerName) + "/q/dev/reload?reload=true";
         try {
             return result(url, 1000);
         } catch (InterruptedException | ExecutionException e) {
@@ -103,46 +103,72 @@ public class RunnerService {
         return null;
     }
 
-    public String getRunnerAddress(String runnerName) {
+    public String getContainerAddress(String containerName) {
         if (kubernetesService.inKubernetes()) {
-            return "http://" + runnerName + "." + kubernetesService.getNamespace() + ".svc.cluster.local";
+            return "http://" + containerName + "." + kubernetesService.getNamespace() + ".svc.cluster.local";
         } else {
-            return "http://" + runnerName + ":8080";
+            return "http://" + containerName + ":8080";
         }
     }
 
-    @Scheduled(every = "{karavan.runner-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectRunnerStatus() {
-        System.out.println("collectRunnerStatus");
-        if (datagridService.call().getStatus().name().equals("UP")) {
-            datagridService.getPodStatuses(environment).stream().filter(PodStatus::getRunner).forEach(podStatus -> {
-                eventBus.publish(CMD_COLLECT_RUNNER_STATUS, podStatus.getName());
+    @Scheduled(every = "{karavan.devmode-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectDevModeStatuses() {
+        System.out.println("Collect DevMode Statuses");
+        if (datagridService.isReady()) {
+            datagridService.getLoadedDevModeStatuses().forEach(dms -> {
+                CamelStatusRequest csr = new CamelStatusRequest(dms.getProjectId(), dms.getContainerName());
+                eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
             });
         }
     }
 
-    @ConsumeEvent(value = CMD_COLLECT_RUNNER_STATUS, blocking = true, ordered = false)
-    public void collectRunnerStatuses(String podName) {
-        String oldContext = datagridService.getRunnerStatus(podName, RunnerStatus.NAME.context);
-        String newContext = getRunnerStatus(podName, RunnerStatus.NAME.context);
-        if (newContext != null) {
-            datagridService.saveRunnerStatus(podName, RunnerStatus.NAME.context, newContext);
-            Arrays.stream(RunnerStatus.NAME.values())
-                    .filter(name -> !name.equals(RunnerStatus.NAME.context))
-                    .forEach(statusName -> {
-                        String status = getRunnerStatus(podName, statusName);
-                        if (status != null) {
-                            datagridService.saveRunnerStatus(podName, statusName, status);
-                        }
-                    });
-            reloadCode(podName, oldContext, newContext);
-        } else {
-            datagridService.deleteRunnerStatuses(podName);
+    @Scheduled(every = "{karavan.camel-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectNonDevModeStatuses() {
+        System.out.println("Collect NonDevMode Statuses");
+        if (datagridService.isReady()) {
+            datagridService.getPodStatuses(environment).forEach(pod -> {
+                CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getName());
+                eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
+            });
         }
     }
 
+    @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true, ordered = true)
+    public void collectCamelStatuses(JsonObject data) {
+        DevModeStatus dms = data.mapTo(DevModeStatus.class);
+        Arrays.stream(CamelStatusName.values()).forEach(statusName -> {
+            String containerName = dms.getContainerName();
+            String status = getCamelStatus(containerName, statusName);
+            if (status != null) {
+                CamelStatus cs = new CamelStatus(dms.getProjectId(), containerName, statusName, status, environment);
+                datagridService.saveCamelStatus(cs);
+            }
+        });
+    }
+
+    @Scheduled(every = "{karavan.devmode-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void cleanupDevModeStatuses() {
+        System.out.println("Clean DevMode Statuses");
+        if (datagridService.isReady()) {
+            datagridService.getLoadedDevModeStatuses().forEach(dms -> {
+                PodStatus pod = datagridService.getDevModePodStatuses(dms.getProjectId(), environment);
+                if (pod == null) {
+                    eventBus.publish(CMD_DELETE_CAMEL_STATUS, JsonObject.mapFrom(dms));
+                }
+            });
+        }
+    }
+
+    @ConsumeEvent(value = CMD_DELETE_CAMEL_STATUS, blocking = true, ordered = true)
+    public void cleanupDevModeStatus(JsonObject data) {
+        DevModeStatus dms = data.mapTo(DevModeStatus.class);
+        Arrays.stream(CamelStatusName.values()).forEach(name -> {
+            datagridService.deleteCamelStatus(dms.getProjectId(), name.name(), environment);
+        });
+    }
+
     private void reloadCode(String podName, String oldContext, String newContext) {
-        String projectName = podName.replace("-" + RUNNER_SUFFIX, "");
+        String projectName = podName.replace("-" + DEVMODE_SUFFIX, "");
         String newState = getContextState(newContext);
         String oldState = getContextState(oldContext);
         if (newContext != null && !Objects.equals(newState, oldState) && "Started".equals(newState)) {
@@ -159,10 +185,10 @@ public class RunnerService {
         }
     }
 
-    public String getRunnerStatus(String podName, RunnerStatus.NAME statusName) {
-        String url = getRunnerAddress(podName) + "/q/dev/" + statusName.name();
+    public String getCamelStatus(String podName, CamelStatusName statusName) {
+        String url = getContainerAddress(podName) + "/q/dev/" + statusName.name();
         try {
-            return result(url, 1000);
+            return result(url, 500);
         } catch (InterruptedException | ExecutionException e) {
             LOGGER.error(e.getMessage());
         }
@@ -183,4 +209,22 @@ public class RunnerService {
         }
         return null;
     }
+
+    public class CamelStatusRequest {
+        private final String projectId;
+        private final String containerName;
+
+        public CamelStatusRequest(String projectId, String containerName) {
+            this.projectId = projectId;
+            this.containerName = containerName;
+        }
+
+        public String getProjectId() {
+            return projectId;
+        }
+
+        public String getContainerName() {
+            return containerName;
+        }
+    }
 }
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
index 650a5622..8930d550 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
@@ -19,12 +19,7 @@ package org.apache.camel.karavan.service;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.vertx.core.Vertx;
-import org.apache.camel.karavan.model.CommitInfo;
-import org.apache.camel.karavan.model.GitConfig;
-import org.apache.camel.karavan.model.GitRepo;
-import org.apache.camel.karavan.model.GitRepoFile;
-import org.apache.camel.karavan.model.Project;
-import org.apache.camel.karavan.model.ProjectFile;
+import org.apache.camel.karavan.datagrid.model.*;
 import org.eclipse.jgit.api.CheckoutCommand;
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.FetchCommand;
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
index 875120d8..cc02c5af 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java
@@ -46,8 +46,7 @@ import javax.inject.Inject;
 import java.util.*;
 import java.util.stream.Collectors;
 
-import static org.apache.camel.karavan.service.ServiceUtil.APPLICATION_PROPERTIES_FILENAME;
-
+import static org.apache.camel.karavan.service.CodeService.APPLICATION_PROPERTIES_FILENAME;
 
 @Default
 @Readiness
@@ -395,7 +394,7 @@ public class KubernetesService implements HealthCheck {
         Pod old = kubernetesClient().pods().inNamespace(getNamespace()).withName(runnerName).get();
         if (old == null) {
             ProjectFile properties = datagridService.getProjectFile(project.getProjectId(), APPLICATION_PROPERTIES_FILENAME);
-            Map<String, String> containerResources = ServiceUtil
+            Map<String, String> containerResources = CodeService
                     .getRunnerContainerResourcesMap(properties, isOpenshift(), project.getRuntime().equals("quarkus"));
             Pod pod = getRunnerPod(project.getProjectId(), runnerName, jBangOptions, containerResources);
             Pod result = kubernetesClient().resource(pod).createOrReplace();
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
index 16e7a71e..5a1a8952 100644
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
+++ b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
@@ -19,9 +19,10 @@ package org.apache.camel.karavan.service;
 import io.quarkus.scheduler.Scheduled;
 import io.quarkus.vertx.ConsumeEvent;
 import io.smallrye.mutiny.tuples.Tuple2;
-import org.apache.camel.karavan.model.GitRepo;
-import org.apache.camel.karavan.model.Project;
-import org.apache.camel.karavan.model.ProjectFile;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.GitRepo;
+import org.apache.camel.karavan.datagrid.model.Project;
+import org.apache.camel.karavan.datagrid.model.ProjectFile;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.eclipse.microprofile.health.HealthCheck;
@@ -75,16 +76,16 @@ public class ProjectService implements HealthCheck{
     void pullCommits() {
         if (readyToPull.get()) {
             LOGGER.info("Pull commits...");
-            Tuple2<String, Integer> lastCommit = infinispanService.getLastCommit();
+            Tuple2<String, Integer> lastCommit = datagridService.getLastCommit();
             gitService.getCommitsAfterCommit(lastCommit.getItem2()).forEach(commitInfo -> {
-                if (!infinispanService.hasCommit(commitInfo.getCommitId())) {
+                if (!datagridService.hasCommit(commitInfo.getCommitId())) {
                     commitInfo.getRepos().forEach(repo -> {
                         Project project = importProjectFromRepo(repo);
                         kubernetesService.createPipelineRun(project);
                     });
-                    infinispanService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime());
+                    datagridService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime());
                 }
-                infinispanService.saveLastCommit(commitInfo.getCommitId());
+                datagridService.saveLastCommit(commitInfo.getCommitId());
             });
         }
     }
@@ -92,15 +93,15 @@ public class ProjectService implements HealthCheck{
     void importCommits() {
         LOGGER.info("Import commits...");
         gitService.getAllCommits().forEach(commitInfo -> {
-            infinispanService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime());
-            infinispanService.saveLastCommit(commitInfo.getCommitId());
+            datagridService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime());
+            datagridService.saveLastCommit(commitInfo.getCommitId());
         });
         readyToPull.set(true);
     }
 
     @ConsumeEvent(value = IMPORT_PROJECTS, blocking = true)
     void importProjects(String data) {
-        if (infinispanService.getProjects().isEmpty()) {
+        if (datagridService.getProjects().isEmpty()) {
             importAllProjects();
         }
         addTemplatesProject();
@@ -122,11 +123,11 @@ public class ProjectService implements HealthCheck{
                 } else {
                     project = getProjectFromRepo(repo);
                 }
-                infinispanService.saveProject(project, true);
+                datagridService.saveProject(project);
 
                 repo.getFiles().forEach(repoFile -> {
                     ProjectFile file = new ProjectFile(repoFile.getName(), repoFile.getBody(), folderName, repoFile.getLastCommitTimestamp());
-                    infinispanService.saveProjectFile(file);
+                    datagridService.saveProjectFile(file);
                 });
             });
             addKameletsProject();
@@ -150,10 +151,10 @@ public class ProjectService implements HealthCheck{
         LOGGER.info("Import project from GitRepo " + repo.getName());
         try {
             Project project = getProjectFromRepo(repo);
-            infinispanService.saveProject(project, true);
+            datagridService.saveProject(project);
             repo.getFiles().forEach(repoFile -> {
                 ProjectFile file = new ProjectFile(repoFile.getName(), repoFile.getBody(), repo.getName(), repoFile.getLastCommitTimestamp());
-                infinispanService.saveProjectFile(file);
+                datagridService.saveProjectFile(file);
             });
             return project;
         } catch (Exception e) {
@@ -164,33 +165,33 @@ public class ProjectService implements HealthCheck{
 
     public Project getProjectFromRepo(GitRepo repo) {
         String folderName = repo.getName();
-        String propertiesFile = ServiceUtil.getPropertiesFile(repo);
-        String projectName = ServiceUtil.getProjectName(propertiesFile);
-        String projectDescription = ServiceUtil.getProjectDescription(propertiesFile);
-        String runtime = ServiceUtil.getProjectRuntime(propertiesFile);
+        String propertiesFile = codeService.getPropertiesFile(repo);
+        String projectName = codeService.getProjectName(propertiesFile);
+        String projectDescription = codeService.getProjectDescription(propertiesFile);
+        String runtime = codeService.getProjectRuntime(propertiesFile);
         return new Project(folderName, projectName, projectDescription, runtime, repo.getCommitId(), repo.getLastCommitTimestamp());
     }
 
     public Project commitAndPushProject(String projectId, String message) throws Exception {
-        Project p = infinispanService.getProject(projectId);
-        List<ProjectFile> files = infinispanService.getProjectFiles(projectId);
+        Project p = datagridService.getProject(projectId);
+        List<ProjectFile> files = datagridService.getProjectFiles(projectId);
         RevCommit commit = gitService.commitAndPushProject(p, files, message);
         String commitId = commit.getId().getName();
         Long lastUpdate = commit.getCommitTime() * 1000L;
         p.setLastCommit(commitId);
         p.setLastCommitTimestamp(lastUpdate);
-        infinispanService.saveProject(p, false);
-        infinispanService.saveCommit(commitId, commit.getCommitTime());
+        datagridService.saveProject(p);
+        datagridService.saveCommit(commitId, commit.getCommitTime());
         return p;
     }
 
     void addKameletsProject() {
         LOGGER.info("Add custom kamelets project if not exists");
         try {
-            Project kamelets  = infinispanService.getProject(Project.NAME_KAMELETS);
+            Project kamelets  = datagridService.getProject(Project.NAME_KAMELETS);
             if (kamelets == null) {
                 kamelets = new Project(Project.NAME_KAMELETS, "Custom Kamelets", "Custom Kamelets", "", "", Instant.now().toEpochMilli());
-                infinispanService.saveProject(kamelets, true);
+                datagridService.saveProject(kamelets);
                 commitAndPushProject(Project.NAME_KAMELETS, "Add custom kamelets");
             }
         } catch (Exception e) {
@@ -201,14 +202,14 @@ public class ProjectService implements HealthCheck{
     void addTemplatesProject() {
         LOGGER.info("Add templates project if not exists");
         try {
-            Project templates  = infinispanService.getProject(Project.NAME_TEMPLATES);
+            Project templates  = datagridService.getProject(Project.NAME_TEMPLATES);
             if (templates == null) {
                 templates = new Project(Project.NAME_TEMPLATES, "Templates", "Templates", "", "", Instant.now().toEpochMilli());
-                infinispanService.saveProject(templates, true);
+                datagridService.saveProject(templates);
 
                 codeService.getApplicationPropertiesTemplates().forEach((name, value) -> {
                     ProjectFile file = new ProjectFile(name, value, Project.NAME_TEMPLATES, Instant.now().toEpochMilli());
-                    infinispanService.saveProjectFile(file);
+                    datagridService.saveProjectFile(file);
                 });
                 commitAndPushProject(Project.NAME_TEMPLATES, "Add default templates");
             }
@@ -220,14 +221,14 @@ public class ProjectService implements HealthCheck{
     void addPipelinesProject() {
         LOGGER.info("Add pipelines project if not exists");
         try {
-            Project pipelines  = infinispanService.getProject(Project.NAME_PIPELINES);
+            Project pipelines  = datagridService.getProject(Project.NAME_PIPELINES);
             if (pipelines == null) {
                 pipelines = new Project(Project.NAME_PIPELINES, "Pipelines", "CI/CD Pipelines", "", "", Instant.now().toEpochMilli());
-                infinispanService.saveProject(pipelines, true);
+                datagridService.saveProject(pipelines);
 
                 codeService.getApplicationPropertiesTemplates().forEach((name, value) -> {
                     ProjectFile file = new ProjectFile(name, value, Project.NAME_PIPELINES, Instant.now().toEpochMilli());
-                    infinispanService.saveProjectFile(file);
+                    datagridService.saveProjectFile(file);
                 });
                 commitAndPushProject(Project.NAME_PIPELINES, "Add default pipelines");
             }
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ServiceUtil.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ServiceUtil.java
deleted file mode 100644
index a6709d91..00000000
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/ServiceUtil.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.karavan.service;
-
-import org.apache.camel.karavan.datagrid.model.GitRepo;
-import org.apache.camel.karavan.datagrid.model.GitRepoFile;
-import org.apache.camel.karavan.datagrid.model.ProjectFile;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-public class ServiceUtil {
-
-    public static final String APPLICATION_PROPERTIES_FILENAME = "application.properties";
-    public static final Map<String, String> DEFAULT_CONTAINER_RESOURCES = Map.of(
-            "requests.memory", "512Mi",
-            "requests.cpu", "500m",
-            "limits.memory", "2048Mi",
-            "limits.cpu", "2000m"
-    );
-
-
-    public static Map<String, String> getRunnerContainerResourcesMap(ProjectFile propertiesFile, boolean isOpenshift, boolean isQuarkus) {
-        if (!isQuarkus) {
-            return DEFAULT_CONTAINER_RESOURCES;
-        } else {
-            Map<String, String> result = new HashMap<>();
-            String patternPrefix = isOpenshift ? "quarkus.openshift.resources." : "quarkus.kubernetes.resources.";
-            String devPatternPrefix = "%dev." + patternPrefix;
-
-            List<String> lines = propertiesFile.getCode().lines().collect(Collectors.toList());
-
-            DEFAULT_CONTAINER_RESOURCES.forEach((key, value) -> {
-                Optional<String> dev = lines.stream().filter(l -> l.startsWith(devPatternPrefix + key)).findFirst();
-                if (dev.isPresent()) {
-                    result.put(key, ServiceUtil.getValueForProperty(dev.get(), devPatternPrefix + key));
-                } else {
-                    Optional<String> prod = lines.stream().filter(l -> l.startsWith(patternPrefix + key)).findFirst();
-                    if (prod.isPresent()){
-                        result.put(key, ServiceUtil.getValueForProperty(prod.get(), patternPrefix + key));
-                    } else {
-                        result.put(key, value);
-                    }
-                }
-            });
-            return result;
-        }
-    }
-
-    public static String getPropertiesFile(GitRepo repo) {
-        try {
-            for (GitRepoFile e : repo.getFiles()){
-                if (e.getName().equalsIgnoreCase(APPLICATION_PROPERTIES_FILENAME)) {
-                    return e.getBody();
-                }
-            }
-        } catch (Exception e) {
-
-        }
-        return null;
-    }
-
-    public static String capitalize(String str) {
-        if(str == null || str.isEmpty()) {
-            return str;
-        }
-        return str.substring(0, 1).toUpperCase() + str.substring(1);
-    }
-
-    public static String getProperty(String file, String property) {
-        String prefix = property + "=";
-        return  Arrays.stream(file.split(System.lineSeparator())).filter(s -> s.startsWith(prefix))
-                .findFirst().orElseGet(() -> "")
-                .replace(prefix, "");
-    }
-
-    public static String getValueForProperty(String line, String property) {
-        String prefix = property + "=";
-        return  line.replace(prefix, "");
-    }
-
-    public static String getProjectDescription(String file) {
-        String description = getProperty(file, "camel.jbang.project-description");
-        return description != null && !description.isBlank() ? description : getProperty(file, "camel.karavan.project-description");
-    }
-
-    public static String getProjectName(String file) {
-        String name = getProperty(file, "camel.jbang.project-name");
-        return name != null && !name.isBlank() ? name : getProperty(file, "camel.karavan.project-name");
-    }
-
-    public static String getProjectRuntime(String file) {
-        return getProperty(file, "camel.jbang.runtime");
-    }
-}
diff --git a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java b/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java
deleted file mode 100644
index 9573d34a..00000000
--- a/karavan-cloud/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.karavan.service;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.quarkus.runtime.configuration.ProfileManager;
-import io.quarkus.vertx.ConsumeEvent;
-import io.vertx.core.json.JsonObject;
-import io.vertx.mutiny.core.Vertx;
-import io.vertx.mutiny.core.buffer.Buffer;
-import io.vertx.mutiny.core.eventbus.EventBus;
-import io.vertx.mutiny.ext.web.client.HttpResponse;
-import io.vertx.mutiny.ext.web.client.WebClient;
-import org.apache.camel.karavan.datagrid.DatagridService;
-import org.apache.camel.karavan.datagrid.model.*;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.eclipse.microprofile.faulttolerance.Retry;
-import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
-import org.jboss.logging.Logger;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
-
-@ApplicationScoped
-public class StatusService {
-
-    private static final Logger LOGGER = Logger.getLogger(StatusService.class.getName());
-    public static final String CMD_COLLECT_PROJECT_STATUS = "collect-project-status";
-    public static final String CMD_COLLECT_ALL_STATUSES = "collect-all-statuses";
-    public static final String CMD_SAVE_STATUS = "save-statuses";
-
-    @Inject
-    DatagridService datagridService;
-
-    @Inject
-    KubernetesService kubernetesService;
-
-    @ConfigProperty(name = "karavan.camel-status-threshold")
-    int threshold;
-
-    @ConfigProperty(name = "karavan.environment")
-    String environment;
-
-    private Map<String, Long> lastCollect = new HashMap<>();
-    private ObjectMapper mapper = new ObjectMapper();
-    @Inject
-    Vertx vertx;
-
-    @Inject
-    EventBus eventBus;
-
-    WebClient webClient;
-
-    public WebClient getWebClient() {
-        if (webClient == null) {
-            webClient = WebClient.create(vertx);
-        }
-        return webClient;
-    }
-
-    @ConsumeEvent(value = CMD_COLLECT_PROJECT_STATUS, blocking = true, ordered = true)
-    public void collectProjectStatus(JsonObject data) {
-        String projectId = data.getString("projectId");
-        String env = data.getString("env");
-        Optional<Environment> environment = datagridService.getEnvironments().stream().filter(e -> e.getName().equals(env)).findFirst();
-        if (environment.isPresent()){
-            DeploymentStatus status = datagridService.getDeploymentStatus(projectId, environment.get().getNamespace(), environment.get().getCluster());
-            if (status != null && status.getReadyReplicas() > 0) {
-                if ((System.currentTimeMillis() - lastCollect.getOrDefault(projectId, 0L)) > threshold) {
-                    collectStatusesForProject(projectId);
-                    lastCollect.put(projectId, System.currentTimeMillis());
-                }
-            }
-        }
-    }
-
-    @ConsumeEvent(value = CMD_COLLECT_ALL_STATUSES, blocking = true, ordered = true)
-    public void collectAllStatuses(String data) {
-        String all = "ALL_PROJECTS";
-        if ((System.currentTimeMillis() - lastCollect.getOrDefault(all, 0L)) > threshold) {
-            datagridService.getDeploymentStatuses().forEach(d -> {
-                eventBus.publish(CMD_COLLECT_PROJECT_STATUS, new JsonObject(Map.of("projectId", d.getName(), "env", d.getEnv())));
-            });
-            lastCollect.put(all, System.currentTimeMillis());
-        }
-    }
-
-    @ConsumeEvent(value = CMD_SAVE_STATUS, blocking = true)
-    public void saveStatus(String status) {
-        try {
-            CamelStatus cs = mapper.readValue(status, CamelStatus.class);
-            datagridService.saveCamelStatus(cs);
-        } catch (Exception ex) {
-            LOGGER.error(ex.getMessage());
-        }
-    }
-
-    private void collectStatusesForProject(String projectId) {
-        LOGGER.info("Collect Camel status for project " + projectId);
-        Project project = datagridService.getProject(projectId);
-        String runtime = project.getRuntime();
-        String path = runtime.equalsIgnoreCase("quarkus") ? "/q/health" : "/actuator/health";
-        String separator = ProfileManager.getActiveProfile().equals("dev") ? "-" : ".";
-        String cluster = ProfileManager.getActiveProfile().equals("dev") ? kubernetesService.getCluster() : "svc.cluster.local";
-        String url = "http://" + projectId + separator + kubernetesService.getNamespace() + "." + cluster + path;
-        CamelStatus cs = getCamelStatus(projectId, url, runtime);
-        try {
-            String data = mapper.writeValueAsString(cs);
-            eventBus.send(CMD_SAVE_STATUS, data);
-        } catch (Exception ex) {
-            LOGGER.error(ex.getMessage());
-        }
-    }
-
-    @Retry(maxRetries = 6, maxDuration=100)
-    @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000)
-    public HttpResponse<Buffer> bufferResult(String url, int timeout) throws InterruptedException, ExecutionException {
-        HttpResponse<Buffer> result = getWebClient().getAbs(url).timeout(timeout).send().subscribeAsCompletionStage().toCompletableFuture().get();
-        return result;
-    }
-
-
-    private CamelStatus getCamelStatus(String projectId, String url, String runtime) {
-        // TODO: make it reactive
-        try {
-            HttpResponse<Buffer> result = bufferResult(url, 1000);
-            if (result.statusCode() == 200) {
-                JsonObject res = result.bodyAsJsonObject();
-                if (runtime.equalsIgnoreCase("quarkus")) {
-                    List<JsonObject> checks = res.getJsonArray("checks").stream().map(o -> (JsonObject) o).collect(Collectors.toList());
-
-                    JsonObject context = checks.stream().filter(o -> Objects.equals(o.getString("name"), "context")).findFirst().get();
-                    return new CamelStatus(
-                            projectId,
-                            null,
-                            CamelStatusName.context,
-                            context.getJsonObject("data").getString("context.version"),
-                            environment
-                    );
-                } else {
-                    JsonObject components = res.getJsonObject("components");
-                    JsonObject camelHealth = components.getJsonObject("camelHealth");
-                    JsonObject details = camelHealth.getJsonObject("details");
-
-                    return new CamelStatus(
-                            projectId,
-                            null,
-                            CamelStatusName.context,
-                            null,
-                            environment
-                    );
-                }
-            } else {
-                return new CamelStatus(
-                        projectId,
-                        null,
-                        CamelStatusName.context,
-                        null,
-                        environment
-                );
-            }
-        } catch (Exception ex) {
-            LOGGER.error(ex.getMessage());
-            return new CamelStatus(
-                    projectId,
-                    null,
-                    CamelStatusName.context,
-                    null,
-                    environment
-            );
-        }
-    }
-//    private CamelStatus.Status getSpringStatus(JsonObject object, String name){
-//        try {
-//            String res = object.getString(name);
-//            if (res == null) {
-//                Optional<String> fname = object.fieldNames().stream().filter(fn -> fn.startsWith(name)).findFirst();
-//                if (fname.isPresent()) {
-//                    res = object.getString(fname.get());
-//                }
-//            }
-//            return res.equals("UP") ? CamelStatus.Status.UP : CamelStatus.Status.DOWN;
-//        } catch (Exception e){
-//            return CamelStatus.Status.UNDEFINED;
-//        }
-//    }
-
-
-//    private CamelStatus.Status getQuarkusStatus(List<JsonObject> checks, String name){
-//        try {
-//            JsonObject res = checks.stream().filter(o -> o.getString("name").equals(name)).findFirst().get();
-//            return res.getString("status").equals("UP") ? CamelStatus.Status.UP : CamelStatus.Status.DOWN;
-//        } catch (Exception e){
-//            return CamelStatus.Status.UNDEFINED;
-//        }
-//    }
-}
\ No newline at end of file
diff --git a/karavan-cloud/karavan-app/src/main/resources/application.properties b/karavan-cloud/karavan-app/src/main/resources/application.properties
index 414ffac6..c7a39319 100644
--- a/karavan-cloud/karavan-app/src/main/resources/application.properties
+++ b/karavan-cloud/karavan-app/src/main/resources/application.properties
@@ -2,8 +2,8 @@ karavan.version=3.21.1-SNAPSHOT
 karavan.environment=dev
 karavan.default-runtime=quarkus
 karavan.runtimes=quarkus,spring-boot
-karavan.camel-status-threshold=2000
-karavan.runner-status-interval=2s
+karavan.camel-status-interval=2s
+karavan.devmode-status-interval=2s
 quarkus.scheduler.enabled=true
 
 # Git repository Configuration
diff --git a/karavan-cloud/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-cloud/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 8ce5abc0..c2b53c14 100644
--- a/karavan-cloud/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-cloud/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -304,7 +304,7 @@ export class KaravanApi {
     }
 
     static async getRunnerPodStatus(projectId: string, after: (res: AxiosResponse<PodStatus>) => void) {
-        instance.get('/api/runner/pod/' + projectId)
+        instance.get('/api/devmode/pod/' + projectId)
             .then(res => {
                 after(res);
             }).catch(err => {
@@ -313,7 +313,7 @@ export class KaravanApi {
     }
 
     static async getRunnerReload(projectId: string, after: (res: AxiosResponse<any>) => void) {
-        instance.get('/api/runner/reload/' + projectId)
+        instance.get('/api/devmode/reload/' + projectId)
             .then(res => {
                 after(res);
             }).catch(err => {
@@ -322,7 +322,7 @@ export class KaravanApi {
     }
 
     static async getRunnerConsoleStatus(projectId: string, statusName: string, after: (res: AxiosResponse<string>) => void) {
-        instance.get('/api/runner/console/' + projectId + "/" + statusName)
+        instance.get('/api/devmode/console/' + projectId + "/" + statusName)
             .then(res => {
                 after(res);
             }).catch(err => {
@@ -331,7 +331,7 @@ export class KaravanApi {
     }
 
     static async runProject(project: Project, verbose: boolean, after: (res: AxiosResponse<string>) => void) {
-        instance.post('/api/runner' + (verbose ? '/--verbose' : ''), project)
+        instance.post('/api/devmode' + (verbose ? '/--verbose' : ''), project)
             .then(res => {
                 after(res);
             }).catch(err => {
@@ -340,7 +340,7 @@ export class KaravanApi {
     }
 
     static async deleteRunner(name: string, deletePVC: boolean, after: (res: AxiosResponse<any>) => void) {
-        instance.delete('/api/runner/' +  name + "/" + deletePVC)
+        instance.delete('/api/devmode/' +  name + "/" + deletePVC)
             .then(res => {
                 after(res);
             }).catch(err => {
diff --git a/karavan-cloud/karavan-bashi/pom.xml b/karavan-cloud/karavan-bashi/pom.xml
index 469f8942..670c283e 100644
--- a/karavan-cloud/karavan-bashi/pom.xml
+++ b/karavan-cloud/karavan-bashi/pom.xml
@@ -13,20 +13,17 @@
 
     <dependencies>
         <dependency>
-            <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-arc</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-scheduler</artifactId>
+            <groupId>org.apache.camel.karavan</groupId>
+            <artifactId>karavan-datagrid</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-vertx</artifactId>
+            <artifactId>quarkus-arc</artifactId>
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-infinispan-client</artifactId>
+            <artifactId>quarkus-scheduler</artifactId>
         </dependency>
         <dependency>
             <groupId>com.github.docker-java</groupId>
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
index 12ebdb8b..71e2872b 100644
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
+++ b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/ConductorService.java
@@ -4,6 +4,10 @@ import com.github.dockerjava.api.model.HealthCheck;
 import io.quarkus.vertx.ConsumeEvent;
 import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.bashi.docker.DockerService;
+import org.apache.camel.karavan.datagrid.DatagridService;
+import org.apache.camel.karavan.datagrid.model.CommandName;
+import org.apache.camel.karavan.datagrid.model.DevModeCommand;
+import org.apache.camel.karavan.datagrid.model.Project;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
 
@@ -11,6 +15,7 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.apache.camel.karavan.bashi.Constants.*;
 
@@ -44,11 +49,13 @@ public class ConductorService {
     @Inject
     DockerService dockerService;
 
+    @Inject
+    DatagridService datagridService;
+
     private static final Logger LOGGER = Logger.getLogger(ConductorService.class.getName());
 
     public static final String ADDRESS_INFINISPAN_START = "ADDRESS_INFINISPAN_START";
-    public static final String ADDRESS_INFINISPAN_HEALTH = "ADDRESS_INFINISPAN_HEALTH";
-    public static final String ADDRESS_RUNNER = "ADDRESS_RUNNER";
+    public static final String ADDRESS_INFINISPAN_HEALTH = "ADDRESS_DATAGRID_HEALTH";
 
     @ConsumeEvent(value = ADDRESS_INFINISPAN_START, blocking = true, ordered = true)
     void startInfinispan(String data) throws InterruptedException {
@@ -57,15 +64,20 @@ public class ConductorService {
         HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status"))
                 .withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30);
 
-        dockerService.createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage,
+        dockerService.createContainer(DATAGRID_CONTAINER_NAME, infinispanImage,
                 List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword),
                 infinispanPort, true, healthCheck, Map.of()
         );
-        dockerService.startContainer(INFINISPAN_CONTAINER_NAME);
+        dockerService.startContainer(DATAGRID_CONTAINER_NAME);
         LOGGER.info("Infinispan is started");
     }
 
     @ConsumeEvent(value = ADDRESS_INFINISPAN_HEALTH, blocking = true, ordered = true)
+    void startDatagridService(String infinispanHealth){
+        datagridService.start();
+    }
+
+//    @ConsumeEvent(value = ADDRESS_INFINISPAN_HEALTH, blocking = true, ordered = true)
     void startKaravan(String infinispanHealth) throws InterruptedException {
         if (infinispanHealth.equals("healthy")) {
             LOGGER.info("Karavan is starting...");
@@ -84,21 +96,22 @@ public class ConductorService {
         }
     }
 
-    @ConsumeEvent(value = ADDRESS_RUNNER, blocking = true, ordered = true)
-    void manageRunner(JsonObject params) throws InterruptedException {
-        String projectId = params.getString("projectId");
-        String command = params.getString("command");
-        String runnerName = projectId + "-" + RUNNER_SUFFIX;
-        if (command.equals("run")) {
-            LOGGER.infof("Runner starting for %s", projectId);
+    @ConsumeEvent(value = DatagridService.ADDRESS_DEVMODE_COMMAND, blocking = true, ordered = true)
+    void receiveCommand(JsonObject message) throws InterruptedException {
+        System.out.println("receiveCommand " + message);
+        DevModeCommand command = message.mapTo(DevModeCommand.class);
+        String runnerName = command.getProjectId() + "-" + DEVMODE_SUFFIX;
+        if (Objects.equals(command.getCommandName(), CommandName.RUN)) {
+            Project p = datagridService.getProject(command.getProjectId());
+            LOGGER.infof("Runner starting for %s", p.getProjectId());
             dockerService.createContainer(runnerName, runnerImage,
                     List.of(), "", false, new HealthCheck(), Map.of("type", "runner")
             );
             dockerService.startContainer(runnerName);
-            LOGGER.infof("Runner started for %s", projectId);
-        } else {
+            LOGGER.infof("Runner started for %s", p.getProjectId());
+        } else if (Objects.equals(command.getCommandName(), CommandName.DELETE)){
             dockerService.stopContainer(runnerName);
             dockerService.deleteContainer(runnerName);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
index e9115f67..a0575eb2 100644
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
+++ b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/Constants.java
@@ -3,8 +3,8 @@ package org.apache.camel.karavan.bashi;
 public class Constants {
 
     public static final String NETWORK_NAME = "karavan";
-    public static final String INFINISPAN_CONTAINER_NAME = "infinispan";
+    public static final String DATAGRID_CONTAINER_NAME = "infinispan";
 
     public static final String KARAVAN_CONTAINER_NAME = "karavan";
-    public static final String RUNNER_SUFFIX = "runner";
+    public static final String DEVMODE_SUFFIX = "devmode";
 }
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/RunnerStatusService.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/RunnerStatusService.java
deleted file mode 100644
index 038d40c5..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/RunnerStatusService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.apache.camel.karavan.bashi;
-
-import com.github.dockerjava.api.model.HealthCheck;
-import com.github.dockerjava.api.model.Statistics;
-import io.quarkus.scheduler.Scheduled;
-import io.quarkus.vertx.ConsumeEvent;
-import io.vertx.core.json.JsonObject;
-import org.apache.camel.karavan.bashi.docker.DockerService;
-import org.apache.camel.karavan.bashi.infinispan.InfinispanService;
-import org.apache.camel.karavan.bashi.infinispan.PodStatus;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.jboss.logging.Logger;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import java.util.*;
-
-import static org.apache.camel.karavan.bashi.Constants.*;
-
-@ApplicationScoped
-public class RunnerStatusService {
-
-    @ConfigProperty(name = "karavan.environment")
-    String environment;
-
-    @Inject
-    DockerService dockerService;
-
-    @Inject
-    InfinispanService infinispanService;
-
-    private static final Logger LOGGER = Logger.getLogger(RunnerStatusService.class.getName());
-
-    @Scheduled(every = "{karavan.runner-status-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectRunnerStatus() {
-        System.out.println("collectRunnerStatus");
-        dockerService.getRunnerContainer().forEach(container -> {
-            String name = container.getNames()[0].replace("/", "");
-            String projectId = name.replace("-" + Constants.RUNNER_SUFFIX, "");
-            PodStatus ps = getPodStatus(container.getId(), name, projectId, container.getState(), container.getCreated());
-            infinispanService.savePodStatus(ps);
-        });
-    }
-
-    public PodStatus getPodStatus(String id, String name, String projectId, String state, Long created) {
-        try {
-            boolean initialized = Arrays.asList("running", "restarting").contains(state);
-            boolean ready = Arrays.asList("running", "restarting").contains(state);
-            boolean terminating = Arrays.asList("paused", "exited").contains(state);
-            String creationTimestamp = new Date(created).toString();
-
-            Statistics stats = dockerService.getContainerStats(id);
-
-            String requestMemory = Objects.requireNonNull(stats.getMemoryStats().getUsage()).toString();
-            String requestCpu = "N/A";
-            String limitMemory = Objects.requireNonNull(stats.getMemoryStats().getLimit()).toString();
-            String limitCpu = "N/A";
-            return new PodStatus(
-                    name,
-                    state,
-                    initialized,
-                    ready,
-                    terminating,
-                    "",
-                    name,
-                    projectId,
-                    environment,
-                    true,
-                    requestMemory,
-                    requestCpu,
-                    limitMemory,
-                    limitCpu,
-                    creationTimestamp
-            );
-        } catch (Exception ex) {
-            LOGGER.error(ex.getMessage(), ex.getCause());
-            return new PodStatus(
-                    name,
-                    projectId,
-                    environment);
-        }
-    }
-}
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
index 2ca02b23..64acd143 100644
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
+++ b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java
@@ -4,12 +4,10 @@ import com.github.dockerjava.api.async.ResultCallback;
 import com.github.dockerjava.api.model.Container;
 import com.github.dockerjava.api.model.Event;
 import com.github.dockerjava.api.model.EventType;
-import com.github.dockerjava.api.model.Statistics;
 import io.vertx.core.eventbus.EventBus;
 import org.apache.camel.karavan.bashi.ConductorService;
 import org.apache.camel.karavan.bashi.Constants;
-import org.apache.camel.karavan.bashi.infinispan.InfinispanService;
-import org.apache.camel.karavan.bashi.infinispan.PodStatus;
+import org.apache.camel.karavan.datagrid.DatagridService;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
 
@@ -18,7 +16,6 @@ import javax.inject.Inject;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.Date;
 import java.util.Objects;
 
 @ApplicationScoped
@@ -34,7 +31,7 @@ public class DockerEventListener implements ResultCallback<Event> {
     EventBus eventBus;
 
     @Inject
-    InfinispanService infinispanService;
+    DatagridService datagridService;
 
     private static final Logger LOGGER = Logger.getLogger(DockerEventListener.class.getName());
 
@@ -53,11 +50,11 @@ public class DockerEventListener implements ResultCallback<Event> {
                 String health = status.replace("health_status: ", "");
                 LOGGER.infof("Container %s health status: %s", container.getNames()[0], health);
                 eventBus.publish(ConductorService.ADDRESS_INFINISPAN_HEALTH, health);
-            } else if (container.getNames()[0].endsWith(Constants.RUNNER_SUFFIX)) {
+            } else if (container.getNames()[0].endsWith(Constants.DEVMODE_SUFFIX)) {
                 if (Arrays.asList("stop", "die", "kill", "pause", "destroy").contains(event.getStatus())) {
                     String name = container.getNames()[0].replace("/", "");
-                    String projectId = name.replace("-" + Constants.RUNNER_SUFFIX, "");
-                    infinispanService.deletePodStatus(new PodStatus(name, projectId, environment));
+                    String projectId = name.replace("-" + Constants.DEVMODE_SUFFIX, "");
+                    datagridService.deletePodStatus(projectId, environment, name);
                 } else if (Arrays.asList("start", "unpause").contains(event.getStatus())) {
 
                 }
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ClientRunnerListener.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ClientRunnerListener.java
deleted file mode 100644
index 1eae6fe3..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ClientRunnerListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.apache.camel.karavan.bashi.infinispan;
-
-import io.vertx.core.eventbus.EventBus;
-import io.vertx.core.json.JsonObject;
-import org.apache.camel.karavan.bashi.ConductorService;
-import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated;
-import org.infinispan.client.hotrod.annotation.ClientCacheEntryModified;
-import org.infinispan.client.hotrod.annotation.ClientListener;
-import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent;
-import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent;
-
-import java.util.Objects;
-
-@ClientListener
-public class ClientRunnerListener {
-
-    private final InfinispanService infinispanService;
-    private final EventBus eventBus;
-
-    public ClientRunnerListener(InfinispanService infinispanService, EventBus eventBus) {
-        this.infinispanService = infinispanService;
-        this.eventBus = eventBus;
-    }
-
-    @ClientCacheEntryCreated
-    public void entryCreated(ClientCacheEntryCreatedEvent<GroupedKey> event) {
-        System.out.println("entryCreated");
-        String command = event.getKey().getKey();
-        String projectId = event.getKey().getGroup();
-        if (Objects.equals(command, RunnerCommand.NAME.run.name())) {
-            eventBus.publish(ConductorService.ADDRESS_RUNNER, JsonObject.of("projectId", projectId, "command", command, "isRunner", true));
-        } else if (Objects.equals(command, RunnerCommand.NAME.delete.name())) {
-            eventBus.publish(ConductorService.ADDRESS_RUNNER, JsonObject.of("projectId", projectId, "command", command, "isRunner", true));
-        }
-    }
-
-    @ClientCacheEntryModified
-    public void entryModified(ClientCacheEntryModifiedEvent<GroupedKey> event) {
-        System.out.println("entryCreated");
-        String command = event.getKey().getKey();
-        String projectId = event.getKey().getGroup();
-        if (Objects.equals(command, RunnerCommand.NAME.run.name())) {
-            eventBus.publish(ConductorService.ADDRESS_RUNNER, JsonObject.of("projectId", projectId, "command", command, "isRunner", true));
-        } else if (Objects.equals(command, RunnerCommand.NAME.delete.name())) {
-            eventBus.publish(ConductorService.ADDRESS_RUNNER, JsonObject.of("projectId", projectId, "command", command, "isRunner", true));
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/GroupedKey.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/GroupedKey.java
deleted file mode 100644
index 550a1d54..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/GroupedKey.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.apache.camel.karavan.bashi.infinispan;
-
-import org.infinispan.protostream.annotations.ProtoFactory;
-import org.infinispan.protostream.annotations.ProtoField;
-
-public class GroupedKey {
-
-
-    @ProtoField(number = 1)
-    String group;
-    @ProtoField(number = 2)
-    String key;
-
-    @ProtoFactory
-    public GroupedKey(String group, String key) {
-        this.group = group;
-        this.key = key;
-    }
-
-    public static GroupedKey create(String group, String key) {
-        return new GroupedKey(group, key);
-    }
-
-
-    public void setGroup(String group) {
-        this.group = group;
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public void setKey(String key) {
-        this.key = key;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        GroupedKey that = (GroupedKey) o;
-
-        if (!group.equals(that.group)) return false;
-        return key.equals(that.key);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = group.hashCode();
-        result = 31 * result + key.hashCode();
-        return result;
-    }
-}
\ No newline at end of file
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/InfinispanService.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/InfinispanService.java
deleted file mode 100644
index a2223ae1..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/InfinispanService.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.karavan.bashi.infinispan;
-
-import io.quarkus.vertx.ConsumeEvent;
-import io.vertx.core.eventbus.EventBus;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.infinispan.client.hotrod.RemoteCache;
-import org.infinispan.client.hotrod.RemoteCacheManager;
-import org.infinispan.client.hotrod.Search;
-import org.infinispan.client.hotrod.configuration.ClientIntelligence;
-import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
-import org.infinispan.commons.api.BasicCache;
-import org.infinispan.commons.configuration.StringConfiguration;
-import org.infinispan.commons.marshall.ProtoStreamMarshaller;
-import org.infinispan.query.dsl.QueryFactory;
-import org.jboss.logging.Logger;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Default;
-import javax.inject.Inject;
-
-import java.util.List;
-
-import static org.apache.camel.karavan.bashi.ConductorService.ADDRESS_INFINISPAN_HEALTH;
-
-@Default
-@ApplicationScoped
-public class InfinispanService  {
-
-    @ConfigProperty(name ="quarkus.infinispan-client.hosts")
-    String infinispanHosts;
-    @ConfigProperty(name ="quarkus.infinispan-client.username")
-    String infinispanUsername;
-    @ConfigProperty(name ="quarkus.infinispan-client.password")
-    String infinispanPassword;
-
-    @Inject
-    EventBus eventBus;
-
-    private BasicCache<GroupedKey, PodStatus> podStatuses;
-    private BasicCache<GroupedKey, String> runnerCommands;
-
-    private static final String CACHE_CONFIG = "<distributed-cache name=\"%s\">"
-            + " <encoding media-type=\"application/x-protostream\"/>"
-            + " <groups enabled=\"true\"/>"
-            + "</distributed-cache>";
-
-    private static final Logger LOGGER = Logger.getLogger(InfinispanService.class.getName());
-
-    @ConsumeEvent(value = ADDRESS_INFINISPAN_HEALTH, blocking = true, ordered = true)
-    void startService(String infinispanHealth) {
-        if (infinispanHealth.equals("healthy")) {
-            LOGGER.info("InfinispanService is starting in remote mode");
-
-            ProtoStreamMarshaller marshaller = new ProtoStreamMarshaller();
-            marshaller.register(new ProjectStoreSchemaImpl());
-
-            ConfigurationBuilder builder = new ConfigurationBuilder();
-            builder.socketTimeout(1000)
-                    .connectionTimeout(10000)
-                    .addServers(infinispanHosts)
-                    .security()
-                        .authentication().enable()
-                        .username(infinispanUsername)
-                        .password(infinispanPassword)
-                    .clientIntelligence(ClientIntelligence.BASIC)
-                    .marshaller(marshaller);
-
-            RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
-            runnerCommands = cacheManager.administration().getOrCreateCache(RunnerCommand.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, RunnerCommand.CACHE)));
-            podStatuses = cacheManager.administration().getOrCreateCache(PodStatus.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, PodStatus.CACHE)));
-            cacheManager.getCache(RunnerCommand.CACHE).addClientListener(new ClientRunnerListener(this, eventBus));
-        }
-    }
-
-    public void sendRunnerCommand(String projectId, String runnerName, RunnerCommand.NAME command) {
-        runnerCommands.put(GroupedKey.create(projectId, runnerName), command.name());
-    }
-
-    public String getRunnerCommand(GroupedKey key) {
-        return runnerCommands.get(key);
-    }
-
-    public List<PodStatus> getPodStatuses(String projectId, String env) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) podStatuses);
-        return queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE project = :project AND env = :env")
-                .setParameter("project", projectId)
-                .setParameter("env", env)
-                .execute().list();
-    }
-
-    public List<PodStatus> getPodStatuses(String env) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) podStatuses);
-        return queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE env = :env")
-                .setParameter("env", env)
-                .execute().list();
-    }
-
-    public void savePodStatus(PodStatus status) {
-        podStatuses.put(GroupedKey.create(status.getProject(), status.getName()), status);
-    }
-
-    public void deletePodStatus(PodStatus status) {
-        podStatuses.remove(GroupedKey.create(status.getProject(), status.getName()));
-    }
-
-}
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/PodStatus.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/PodStatus.java
deleted file mode 100644
index 31ee43a8..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/PodStatus.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package org.apache.camel.karavan.bashi.infinispan;
-
-import org.infinispan.protostream.annotations.ProtoFactory;
-import org.infinispan.protostream.annotations.ProtoField;
-
-public class PodStatus {
-    public static final String CACHE = "pod_statuses";
-    @ProtoField(number = 1)
-    String name;
-    @ProtoField(number = 2)
-    String phase;
-    @ProtoField(number = 3)
-    Boolean initialized;
-    @ProtoField(number = 4)
-    Boolean ready;
-    @ProtoField(number = 5)
-    Boolean terminating;
-    @ProtoField(number = 6)
-    String reason;
-    @ProtoField(number = 7)
-    String deployment;
-    @ProtoField(number = 8)
-    String project;
-    @ProtoField(number = 9)
-    String env;
-    @ProtoField(number = 10)
-    Boolean runner;
-    @ProtoField(number = 11)
-    String requestMemory;
-    @ProtoField(number = 12)
-    String requestCpu;
-    @ProtoField(number = 13)
-    String limitMemory;
-    @ProtoField(number = 14)
-    String limitCpu;
-    @ProtoField(number = 15)
-    String creationTimestamp;
-
-    public PodStatus(String name, String project, String env) {
-        this.name = name;
-        this.phase = "";
-        this.initialized = false;
-        this.ready = false;
-        this.terminating = false;
-        this.reason = "";
-        this.project = project;
-        this.env = env;
-    }
-
-    @ProtoFactory
-    public PodStatus(String name, String phase, Boolean initialized, Boolean ready, Boolean terminating, String reason, String deployment, String project, String env, Boolean runner, String requestMemory, String requestCpu, String limitMemory, String limitCpu, String creationTimestamp) {
-        this.name = name;
-        this.phase = phase;
-        this.initialized = initialized;
-        this.ready = ready;
-        this.terminating = terminating;
-        this.reason = reason;
-        this.deployment = deployment;
-        this.project = project;
-        this.env = env;
-        this.runner = runner;
-        this.requestMemory = requestMemory;
-        this.requestCpu = requestCpu;
-        this.limitMemory = limitMemory;
-        this.limitCpu = limitCpu;
-        this.creationTimestamp = creationTimestamp;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getPhase() {
-        return phase;
-    }
-
-    public void setPhase(String phase) {
-        this.phase = phase;
-    }
-
-    public Boolean getInitialized() {
-        return initialized;
-    }
-
-    public void setInitialized(Boolean initialized) {
-        this.initialized = initialized;
-    }
-
-    public Boolean getReady() {
-        return ready;
-    }
-
-    public void setReady(Boolean ready) {
-        this.ready = ready;
-    }
-
-    public Boolean getTerminating() {
-        return terminating;
-    }
-
-    public void setTerminating(Boolean terminating) {
-        this.terminating = terminating;
-    }
-
-    public String getReason() {
-        return reason;
-    }
-
-    public void setReason(String reason) {
-        this.reason = reason;
-    }
-
-    public String getDeployment() {
-        return deployment;
-    }
-
-    public void setDeployment(String deployment) {
-        this.deployment = deployment;
-    }
-
-    public String getProject() {
-        return project;
-    }
-
-    public void setProject(String project) {
-        this.project = project;
-    }
-
-    public String getEnv() {
-        return env;
-    }
-
-    public void setEnv(String env) {
-        this.env = env;
-    }
-
-    public Boolean getRunner() {
-        return runner;
-    }
-
-    public void setRunner(Boolean runner) {
-        this.runner = runner;
-    }
-
-    public String getRequestMemory() {
-        return requestMemory;
-    }
-
-    public void setRequestMemory(String requestMemory) {
-        this.requestMemory = requestMemory;
-    }
-
-    public String getRequestCpu() {
-        return requestCpu;
-    }
-
-    public void setRequestCpu(String requestCpu) {
-        this.requestCpu = requestCpu;
-    }
-
-    public String getLimitMemory() {
-        return limitMemory;
-    }
-
-    public void setLimitMemory(String limitMemory) {
-        this.limitMemory = limitMemory;
-    }
-
-    public String getLimitCpu() {
-        return limitCpu;
-    }
-
-    public void setLimitCpu(String limitCpu) {
-        this.limitCpu = limitCpu;
-    }
-
-    public String getCreationTimestamp() {
-        return creationTimestamp;
-    }
-
-    public void setCreationTimestamp(String creationTimestamp) {
-        this.creationTimestamp = creationTimestamp;
-    }
-
-    @Override
-    public String toString() {
-        return "PodStatus{" +
-                "name='" + name + '\'' +
-                ", phase='" + phase + '\'' +
-                ", initialized=" + initialized +
-                ", ready=" + ready +
-                ", terminating=" + terminating +
-                ", reason='" + reason + '\'' +
-                ", deployment='" + deployment + '\'' +
-                ", project='" + project + '\'' +
-                ", env='" + env + '\'' +
-                ", runner=" + runner +
-                ", requestMemory='" + requestMemory + '\'' +
-                ", requestCpu='" + requestCpu + '\'' +
-                ", limitMemory='" + limitMemory + '\'' +
-                ", limitCpu='" + limitCpu + '\'' +
-                ", creationTimestamp='" + creationTimestamp + '\'' +
-                '}';
-    }
-}
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ProjectStoreSchema.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ProjectStoreSchema.java
deleted file mode 100644
index bf3ea015..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/ProjectStoreSchema.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.apache.camel.karavan.bashi.infinispan;
-
-import org.infinispan.protostream.GeneratedSchema;
-import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
-
-@AutoProtoSchemaBuilder(includeClasses = {GroupedKey.class, PodStatus.class}, schemaPackageName = "karavan")
-public interface ProjectStoreSchema extends GeneratedSchema {
-}
diff --git a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/RunnerCommand.java b/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/RunnerCommand.java
deleted file mode 100644
index 1514920d..00000000
--- a/karavan-cloud/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/infinispan/RunnerCommand.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.apache.camel.karavan.bashi.infinispan;
-
-public class RunnerCommand {
-
-    public enum NAME {
-        run,
-        delete,
-        reload
-    }
-
-    public static final String CACHE = "runner_commands";
-
-}
diff --git a/karavan-cloud/karavan-bashi/src/main/resources/application.properties b/karavan-cloud/karavan-bashi/src/main/resources/application.properties
index 932ff649..1ed3add7 100644
--- a/karavan-cloud/karavan-bashi/src/main/resources/application.properties
+++ b/karavan-cloud/karavan-bashi/src/main/resources/application.properties
@@ -11,7 +11,7 @@ karavan.port=8080:8080
 karavan.environment=dev
 karavan.default-runtime=quarkus
 karavan.runtimes=quarkus,spring-boot
-karavan.runner-status-interval=2s
+karavan.devmode-status-interval=2s
 
 # Git repository Configuration
 karavan.git-repository=${GIT_REPOSITORY}
@@ -23,4 +23,6 @@ karavan.git-branch=main
 quarkus.infinispan-client.hosts=localhost:11222
 quarkus.infinispan-client.username=admin
 quarkus.infinispan-client.password=karavan
-quarkus.infinispan-client.devservices.enabled=false
\ No newline at end of file
+quarkus.infinispan-client.devservices.enabled=false
+
+quarkus.package.type=uber-jar
\ No newline at end of file
diff --git a/karavan-cloud/karavan-datagrid/pom.xml b/karavan-cloud/karavan-datagrid/pom.xml
index 2a12db5c..b66104f2 100644
--- a/karavan-cloud/karavan-datagrid/pom.xml
+++ b/karavan-cloud/karavan-datagrid/pom.xml
@@ -40,7 +40,7 @@
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-smallrye-health</artifactId>
+            <artifactId>quarkus-vertx</artifactId>
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
index f014714b..4968ab83 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/DatagridService.java
@@ -23,9 +23,6 @@ import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.datagrid.model.*;
 import org.apache.camel.karavan.datagrid.model.KaravanSchemaImpl;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.eclipse.microprofile.health.HealthCheck;
-import org.eclipse.microprofile.health.HealthCheckResponse;
-import org.eclipse.microprofile.health.Readiness;
 import org.infinispan.client.hotrod.RemoteCache;
 import org.infinispan.client.hotrod.RemoteCacheManager;
 import org.infinispan.client.hotrod.Search;
@@ -49,9 +46,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
 @Default
-@Readiness
 @ApplicationScoped
-public class DatagridService implements HealthCheck  {
+public class DatagridService  {
 
     public static final String ADDRESS_DEVMODE_COMMAND = "ADDRESS_DEVMODE_COMMAND";
     protected static final String ADDRESS_DEVMODE_COMMAND_INTERNAL = "ADDRESS_DEVMODE_COMMAND_INTERNAL";
@@ -72,8 +68,8 @@ public class DatagridService implements HealthCheck  {
     private RemoteCache<GroupedKey, CamelStatus> camelStatuses;
     private RemoteCache<String, Environment> environments;
     private RemoteCache<String, String> commits;
-    private RemoteCache<GroupedKey, DevModeCommand> devmodeCommands;
     private RemoteCache<GroupedKey, DevModeStatus> devmodeStatuses;
+    private RemoteCache<GroupedKey, DevModeCommand> devmodeCommands;
     private final AtomicBoolean ready = new AtomicBoolean(false);
 
     RemoteCacheManager cacheManager;
@@ -81,12 +77,14 @@ public class DatagridService implements HealthCheck  {
     @Inject
     EventBus eventBus;
 
+    private static final Logger LOGGER = Logger.getLogger(DatagridService.class.getName());
+
+    private static final String DEFAULT_ENVIRONMENT = "dev";
     private static final String CACHE_CONFIG = "<distributed-cache name=\"%s\">"
             + " <encoding media-type=\"application/x-protostream\"/>"
             + " <groups enabled=\"true\"/>"
             + "</distributed-cache>";
 
-    private static final Logger LOGGER = Logger.getLogger(DatagridService.class.getName());
 
     public void start() {
         LOGGER.info("DatagridService is starting in remote mode");
@@ -117,6 +115,7 @@ public class DatagridService implements HealthCheck  {
         camelStatuses = getOrCreateCache(CamelStatus.CACHE, false);
         commits = getOrCreateCache("commits", false);
         deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE, false);
+        devmodeStatuses = getOrCreateCache(DevModeStatus.CACHE, false);
         devmodeCommands = getOrCreateCache(DevModeCommand.CACHE, true);
 
         cacheManager.getCache(DevModeCommand.CACHE).addClientListener(new ClientRunnerListener(eventBus));
@@ -124,6 +123,10 @@ public class DatagridService implements HealthCheck  {
         LOGGER.info("DatagridService is started in remote mode");
     }
 
+    public boolean isReady() {
+        return ready.get();
+    }
+
     private <K, V> RemoteCache<K, V>  getOrCreateCache(String name, boolean command) {
         String config = getResourceFile(command ? "/command-cache-config.xml" : "/data-cache-config.xml");
         return cacheManager.administration().getOrCreateCache(name, new StringConfiguration(String.format(config, name)));
@@ -150,28 +153,37 @@ public class DatagridService implements HealthCheck  {
     }
 
     public void saveProject(Project project) {
-        GroupedKey key = GroupedKey.create(project.getProjectId(), project.getProjectId());
+        GroupedKey key = GroupedKey.create(project.getProjectId(), DEFAULT_ENVIRONMENT, project.getProjectId());
         boolean isNew = !projects.containsKey(key);
         projects.put(key, project);
     }
 
     public List<ProjectFile> getProjectFiles(String projectId) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) files);
+        QueryFactory queryFactory = Search.getQueryFactory(files);
         return queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE projectId = :projectId")
                 .setParameter("projectId", projectId)
                 .execute().list();
     }
 
+    public Map<GroupedKey, ProjectFile> getProjectFilesMap(String projectId) {
+        QueryFactory queryFactory = Search.getQueryFactory(files);
+        return queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE projectId = :projectId")
+                .setParameter("projectId", projectId)
+                .execute().list().stream()
+                .collect(Collectors.toMap(f -> new GroupedKey(f.getProjectId(), DEFAULT_ENVIRONMENT, f.getName()), f -> f));
+    }
+
     public ProjectFile getProjectFile(String projectId, String filename) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) files);
-        return queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE projectId = :projectId AND name = :name")
+        QueryFactory queryFactory = Search.getQueryFactory(files);
+        List<ProjectFile> list = queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE projectId = :projectId AND name = :name")
                 .setParameter("projectId", projectId)
                 .setParameter("name", filename)
-                .execute().list().get(0);
+                .execute().list();
+        return list.size() > 0 ? list.get(0) : null;
     }
 
     public void saveProjectFile(ProjectFile file) {
-        files.put(GroupedKey.create(file.getProjectId(), file.getName()), file);
+        files.put(GroupedKey.create(file.getProjectId(), DEFAULT_ENVIRONMENT, file.getName()), file);
     }
 
     public void saveProjectFiles(Map<GroupedKey, ProjectFile> f) {
@@ -182,41 +194,40 @@ public class DatagridService implements HealthCheck  {
         files.putAll(files);
     }
 
-    public void deleteProject(String project) {
-        projects.remove(GroupedKey.create(project, project));
+    public void deleteProject(String projectId) {
+        projects.remove(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT,projectId));
     }
 
-    public void deleteProjectFile(String project, String filename) {
-        files.remove(GroupedKey.create(project, filename));
+    public void deleteProjectFile(String projectId, String filename) {
+        files.remove(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT,filename));
     }
 
-    public Project getProject(String project) {
-        return projects.get(GroupedKey.create(project, project));
+    public Project getProject(String projectId) {
+        return projects.get(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT,projectId));
     }
 
     public PipelineStatus getPipelineStatus(String projectId, String environment) {
-        return pipelineStatuses.get(GroupedKey.create(projectId, environment));
+        return pipelineStatuses.get(GroupedKey.create(projectId, environment, projectId));
     }
 
     public void savePipelineStatus(PipelineStatus status) {
-        pipelineStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv()), status);
+        pipelineStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status);
     }
 
     public void deletePipelineStatus(PipelineStatus status) {
-        pipelineStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv()));
+        pipelineStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()));
     }
 
-    public DeploymentStatus getDeploymentStatus(String name, String namespace, String cluster) {
-        String deploymentId = name + ":" + namespace + ":" + cluster;
-        return deploymentStatuses.get(GroupedKey.create(name, deploymentId));
+    public DeploymentStatus getDeploymentStatus(String projectId, String environment) {
+        return deploymentStatuses.get(GroupedKey.create(projectId, environment, projectId));
     }
 
     public void saveDeploymentStatus(DeploymentStatus status) {
-        deploymentStatuses.put(GroupedKey.create(status.getName(), status.getId()), status);
+        deploymentStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status);
     }
 
     public void deleteDeploymentStatus(DeploymentStatus status) {
-        deploymentStatuses.remove(GroupedKey.create(status.getName(), status.getId()));
+        deploymentStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()));
     }
 
     public List<DeploymentStatus> getDeploymentStatuses() {
@@ -231,11 +242,11 @@ public class DatagridService implements HealthCheck  {
     }
 
     public void saveServiceStatus(ServiceStatus status) {
-        serviceStatuses.put(GroupedKey.create(status.getName(), status.getId()), status);
+        serviceStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status);
     }
 
     public void deleteServiceStatus(ServiceStatus status) {
-        serviceStatuses.remove(GroupedKey.create(status.getName(), status.getId()));
+        serviceStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()));
     }
 
     public List<ServiceStatus> getServiceStatuses() {
@@ -243,59 +254,77 @@ public class DatagridService implements HealthCheck  {
     }
 
     public List<PodStatus> getPodStatuses(String projectId, String env) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) podStatuses);
+        QueryFactory queryFactory = Search.getQueryFactory(podStatuses);
         return queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE projectId = :projectId AND env = :env")
-                .setParameter("project", projectId)
+                .setParameter("projectId", projectId)
                 .setParameter("env", env)
                 .execute().list();
     }
 
     public PodStatus getDevModePodStatuses(String projectId, String env) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) podStatuses);
-        return queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE projectId = :projectId AND env = :env AND inDevMode = true")
-                .setParameter("project", projectId)
+        QueryFactory queryFactory = Search.getQueryFactory(podStatuses);
+        List<PodStatus> list = queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE projectId = :projectId AND env = :env AND inDevMode = true")
+                .setParameter("projectId", projectId)
                 .setParameter("env", env)
-                .execute().list().get(0);
+                .execute().list();
+        return list.size() > 0 ? list.get(0) : null;
     }
 
     public List<PodStatus> getPodStatuses(String env) {
-        QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) podStatuses);
+        QueryFactory queryFactory = Search.getQueryFactory(podStatuses);
         return queryFactory.<PodStatus>create("FROM karavan.PodStatus WHERE env = :env")
                 .setParameter("env", env)
                 .execute().list();
     }
 
     public void savePodStatus(PodStatus status) {
-        podStatuses.put(GroupedKey.create(status.getProjectId(), status.getName()), status);
+        podStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getName()), status);
     }
 
     public void deletePodStatus(PodStatus status) {
-        podStatuses.remove(GroupedKey.create(status.getProjectId(), status.getName()));
+        podStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getName()));
+    }
+
+    public void deletePodStatus(String projectId, String env, String podName) {
+        podStatuses.remove(GroupedKey.create(projectId, env, podName));
     }
 
-    public CamelStatus getCamelStatus(String projectId, String name, String env) {
+    public CamelStatus getCamelStatus(String projectId, String env, String name) {
+        GroupedKey key = GroupedKey.create(projectId, env, name);
+        return camelStatuses.get(key);
+    }
+
+    public List<CamelStatus> getCamelStatusesByEnv(String env, String name) {
         QueryFactory queryFactory = Search.getQueryFactory(camelStatuses);
-        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus WHERE projectId = :projectId AND name = :name AND env = :env")
-                .setParameter("projectId", projectId)
-                .setParameter("name", name)
+        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus WHERE env = :env AND name = :name")
                 .setParameter("env", env)
-                .execute().list().get(0);
+                .setParameter("name", name)
+                .execute().list();
     }
 
-    public List<CamelStatus> getCamelStatusesByEnv(String projectId, String env) {
+    public List<CamelStatus> getCamelStatusesByProjectIdEnv(String projectId, String name) {
         QueryFactory queryFactory = Search.getQueryFactory(camelStatuses);
-        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus WHERE projectId = :projectId AND env = :env")
+        return queryFactory.<CamelStatus>create("FROM karavan.CamelStatus WHERE projectId = :projectId AND name = :name")
                 .setParameter("projectId", projectId)
-                .setParameter("env", env)
+                .setParameter("name", name)
                 .execute().list();
     }
 
     public void saveCamelStatus(CamelStatus status) {
-        camelStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv()), status);
+        GroupedKey key = GroupedKey.create(status.getProjectId(), status.getEnv(), status.getName().name());
+        camelStatuses.put(key, status);
     }
 
-    public void deleteCamelStatus(String projectId, String env) {
-        camelStatuses.remove(GroupedKey.create(projectId, env));
+    public void deleteCamelStatus(String projectId, String name, String env) {
+        GroupedKey key = GroupedKey.create(projectId, env, name);
+        camelStatuses.remove(key);
+    }
+
+    public void deleteCamelStatuses(String projectId, String env) {
+        Arrays.stream(CamelStatusName.values()).forEach(name -> {
+            GroupedKey key = GroupedKey.create(projectId, env, name.name());
+            camelStatuses.remove(key);
+        });
     }
 
     public List<Environment> getEnvironments() {
@@ -325,22 +354,28 @@ public class DatagridService implements HealthCheck  {
     }
 
     public void saveDevModeStatus(DevModeStatus status) {
-        devmodeStatuses.put(GroupedKey.create(status.getProjectId(), status.getProjectId()), status);
+        devmodeStatuses.put(GroupedKey.create(status.getProjectId(), DEFAULT_ENVIRONMENT, status.getProjectId()), status);
     }
 
     public void deleteDevModeStatus(String projectId) {
-        devmodeStatuses.remove(GroupedKey.create(projectId, projectId));
+        devmodeStatuses.remove(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT, projectId));
     }
 
     public DevModeStatus getDevModeStatus(String projectId) {
-        return devmodeStatuses.get(GroupedKey.create(projectId, projectId));
+        return devmodeStatuses.get(GroupedKey.create(projectId,DEFAULT_ENVIRONMENT, projectId));
+    }
+
+    public List<DevModeStatus> getLoadedDevModeStatuses() {
+        QueryFactory queryFactory = Search.getQueryFactory(devmodeStatuses);
+        return queryFactory.<DevModeStatus>create("FROM karavan.DevModeStatus WHERE codeLoaded = true")
+                .execute().list();
     }
 
     public void sendDevModeCommand(String projectId, DevModeCommand command) {
         if (command.getProjectId() == null) {
             command.setProjectId(projectId);
         }
-        devmodeCommands.put(GroupedKey.create(projectId, UUID.randomUUID().toString()), command);
+        devmodeCommands.put(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT, UUID.randomUUID().toString()), command);
     }
 
     public DevModeCommand getDevModeCommand(GroupedKey key) {
@@ -348,17 +383,7 @@ public class DatagridService implements HealthCheck  {
     }
 
     public DevModeCommand getDevModeCommand(String projectId) {
-        return getDevModeCommand(GroupedKey.create(projectId, projectId));
-    }
-
-    @Override
-    public HealthCheckResponse call() {
-        if (cacheManager != null && cacheManager.isStarted() && ready.get()) {
-            return HealthCheckResponse.up("Infinispan Service is running in cluster mode.");
-        }
-        else {
-            return HealthCheckResponse.down("Infinispan Service is not running.");
-        }
+        return getDevModeCommand(GroupedKey.create(projectId, DEFAULT_ENVIRONMENT, projectId));
     }
 
     public void clearAllStatuses() {
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DeploymentStatus.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DeploymentStatus.java
index f8b3671e..a0af1d46 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DeploymentStatus.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DeploymentStatus.java
@@ -6,7 +6,7 @@ import org.infinispan.protostream.annotations.ProtoField;
 public class DeploymentStatus {
     public static final String CACHE = "deployment_statuses";
     @ProtoField(number = 1)
-    String name;
+    String projectId;
     @ProtoField(number = 2)
     String namespace;
     @ProtoField(number = 3)
@@ -22,8 +22,8 @@ public class DeploymentStatus {
     @ProtoField(number = 8)
     Integer unavailableReplicas;
 
-    public DeploymentStatus(String name, String namespace, String cluster, String env) {
-        this.name = name;
+    public DeploymentStatus(String projectId, String namespace, String cluster, String env) {
+        this.projectId = projectId;
         this.namespace = namespace;
         this.cluster = cluster;
         this.env = env;
@@ -34,8 +34,8 @@ public class DeploymentStatus {
     }
 
     @ProtoFactory
-    public DeploymentStatus(String name, String namespace, String cluster, String env, String image, Integer replicas, Integer readyReplicas, Integer unavailableReplicas) {
-        this.name = name;
+    public DeploymentStatus(String projectId, String namespace, String cluster, String env, String image, Integer replicas, Integer readyReplicas, Integer unavailableReplicas) {
+        this.projectId = projectId;
         this.namespace = namespace;
         this.env = env;
         this.cluster = cluster;
@@ -45,16 +45,12 @@ public class DeploymentStatus {
         this.unavailableReplicas = unavailableReplicas;
     }
 
-    public String getId() {
-        return name + ":" + namespace + ":" + cluster;
+    public String getProjectId() {
+        return projectId;
     }
 
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
+    public void setProjectId(String projectId) {
+        this.projectId = projectId;
     }
 
     public String getEnv() {
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeStatus.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeStatus.java
index 2c4c99d7..c040c67e 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeStatus.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/DevModeStatus.java
@@ -10,12 +10,15 @@ public class DevModeStatus {
     @ProtoField(number = 2)
     String containerName;
     @ProtoField(number = 3)
-    boolean codeLoaded;
+    String containerId;
+    @ProtoField(number = 4)
+    Boolean codeLoaded;
 
     @ProtoFactory
-    public DevModeStatus(String projectId, String containerName, boolean codeLoaded) {
+    public DevModeStatus(String projectId, String containerName, String containerId, Boolean codeLoaded) {
         this.projectId = projectId;
         this.containerName = containerName;
+        this.containerId = containerId;
         this.codeLoaded = codeLoaded;
     }
 
@@ -35,11 +38,19 @@ public class DevModeStatus {
         this.containerName = containerName;
     }
 
-    public boolean isCodeLoaded() {
+    public String getContainerId() {
+        return containerId;
+    }
+
+    public void setContainerId(String containerId) {
+        this.containerId = containerId;
+    }
+
+    public Boolean isCodeLoaded() {
         return codeLoaded;
     }
 
-    public void setCodeLoaded(boolean codeLoaded) {
+    public void setCodeLoaded(Boolean codeLoaded) {
         this.codeLoaded = codeLoaded;
     }
 }
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/GroupedKey.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/GroupedKey.java
index f0850327..ae06b8e9 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/GroupedKey.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/GroupedKey.java
@@ -2,28 +2,39 @@ package org.apache.camel.karavan.datagrid.model;
 
 import org.infinispan.protostream.annotations.ProtoFactory;
 import org.infinispan.protostream.annotations.ProtoField;
+//import org.infinispan.distribution.group.Group;
 
-public class GroupedKey {
 
+public class GroupedKey {
 
     @ProtoField(number = 1)
-    String group;
+    String projectId;
     @ProtoField(number = 2)
+    String env;
+    @ProtoField(number = 3)
     String key;
 
     @ProtoFactory
-    public GroupedKey(String group, String key) {
-        this.group = group;
+    public GroupedKey(String projectId, String env, String key) {
+        this.projectId = projectId;
+        this.env = env;
         this.key = key;
     }
 
-    public static GroupedKey create(String group, String key) {
-        return new GroupedKey(group, key);
+    public static GroupedKey create(String projectId, String env, String key) {
+        return new GroupedKey(projectId, env, key);
     }
 
+    public String getEnv() {
+        return env;
+    }
+
+    public void setEnv(String env) {
+        this.env = env;
+    }
 
-    public void setGroup(String group) {
-        this.group = group;
+    public void setProjectId(String projectId) {
+        this.projectId = projectId;
     }
 
     public String getKey() {
@@ -34,8 +45,9 @@ public class GroupedKey {
         this.key = key;
     }
 
-    public String getGroup() {
-        return group;
+//    @Group https://github.com/quarkusio/quarkus/issues/34677
+    public String getProjectId() {
+        return projectId;
     }
 
     @Override
@@ -45,13 +57,15 @@ public class GroupedKey {
 
         GroupedKey that = (GroupedKey) o;
 
-        if (!group.equals(that.group)) return false;
+        if (!projectId.equals(that.projectId)) return false;
+        if (!env.equals(that.env)) return false;
         return key.equals(that.key);
     }
 
     @Override
     public int hashCode() {
-        int result = group.hashCode();
+        int result = projectId.hashCode();
+        result = 31 * result + env.hashCode();
         result = 31 * result + key.hashCode();
         return result;
     }
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
index 39e8e27f..f4152c53 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/KaravanSchema.java
@@ -16,7 +16,8 @@ import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
                 ServiceStatus.class,
                 CommandName.class,
                 CamelStatusName.class,
-                DevModeCommand.class
+                DevModeCommand.class,
+                DevModeStatus.class
         },
         schemaPackageName = "karavan")
 public interface KaravanSchema extends GeneratedSchema {
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/PodStatus.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/PodStatus.java
index 79986b5b..ffb7b9f5 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/PodStatus.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/PodStatus.java
@@ -34,6 +34,15 @@ public class PodStatus {
         this.cpuInfo = cpuInfo;
     }
 
+    public PodStatus(String name, Boolean ready, String deployment, String projectId, String env, Boolean inDevMode) {
+        this.name = name;
+        this.ready = ready;
+        this.deployment = deployment;
+        this.projectId = projectId;
+        this.env = env;
+        this.inDevMode = inDevMode;
+    }
+
     public String getName() {
         return name;
     }
diff --git a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ServiceStatus.java b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ServiceStatus.java
index 5864c563..07316505 100644
--- a/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ServiceStatus.java
+++ b/karavan-cloud/karavan-datagrid/src/main/java/org/apache/camel/karavan/datagrid/model/ServiceStatus.java
@@ -6,7 +6,7 @@ import org.infinispan.protostream.annotations.ProtoField;
 public class ServiceStatus {
     public static final String CACHE = "service_statuses";
     @ProtoField(number = 1)
-    String name;
+    String projectId;
     @ProtoField(number = 2)
     String namespace;
     @ProtoField(number = 3)
@@ -23,8 +23,8 @@ public class ServiceStatus {
     String type;
 
     @ProtoFactory
-    public ServiceStatus(String name, String namespace, String env, String cluster, Integer port, Integer targetPort, String clusterIP, String type) {
-        this.name = name;
+    public ServiceStatus(String projectId, String namespace, String env, String cluster, Integer port, Integer targetPort, String clusterIP, String type) {
+        this.projectId = projectId;
         this.namespace = namespace;
         this.env = env;
         this.cluster = cluster;
@@ -34,23 +34,19 @@ public class ServiceStatus {
         this.type = type;
     }
 
-    public ServiceStatus(String name, String namespace, String cluster, String env) {
-        this.name = name;
+    public ServiceStatus(String projectId, String namespace, String cluster, String env) {
+        this.projectId = projectId;
         this.namespace = namespace;
         this.env = env;
         this.cluster = cluster;
     }
 
-    public String getId() {
-        return name + ":" + namespace + ":" + cluster;
+    public String getProjectId() {
+        return projectId;
     }
 
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
+    public void setProjectId(String projectId) {
+        this.projectId = projectId;
     }
 
     public String getNamespace() {
diff --git a/karavan-cloud/pom.xml b/karavan-cloud/pom.xml
index 9a48cdbd..1862fa72 100644
--- a/karavan-cloud/pom.xml
+++ b/karavan-cloud/pom.xml
@@ -26,7 +26,7 @@
         <surefire-plugin.version>3.1.0</surefire-plugin.version>
         <infinispan.version>14.0.9.Final</infinispan.version>
         <tekton.version>6.3.1</tekton.version>
-        <jgit.version>2.3.1</jgit.version>
+        <jgit.version>2.3.2</jgit.version>
         <quinoa.version>1.2.4</quinoa.version>
         <resources-plugin.version>3.3.0</resources-plugin.version>
         <kubernetes-client.version>6.7.1</kubernetes-client.version>
diff --git a/karavan-generator/src/main/java/org/apache/camel/karavan/generator/KaravanGenerator.java b/karavan-generator/src/main/java/org/apache/camel/karavan/generator/KaravanGenerator.java
index b6e518d3..425ba8f4 100644
--- a/karavan-generator/src/main/java/org/apache/camel/karavan/generator/KaravanGenerator.java
+++ b/karavan-generator/src/main/java/org/apache/camel/karavan/generator/KaravanGenerator.java
@@ -21,7 +21,7 @@ public final class KaravanGenerator {
     public static void main(String[] args) throws Exception {
         String[] paths = new String[] {
                 "karavan-designer/public",
-                "karavan-app/src/main/resources",
+                "karavan-cloud/karavan-app/src/main/resources",
                 "karavan-vscode"
         };
         if (args.length > 0) {