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/26 00:40:26 UTC

[camel-karavan] 01/02: Container management improvements #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

commit 5eafe2958b74831443eb3e552574953657e43a8f
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Tue Jul 25 17:27:28 2023 -0400

    Container management improvements #817
---
 .../apache/camel/karavan/api/DevModeResource.java  |  18 ++--
 .../camel/karavan/api/InfrastructureResource.java  |  62 +++++++++--
 .../camel/karavan/docker/DockerEventListener.java  | 111 +++++++++++---------
 .../apache/camel/karavan/docker/DockerService.java | 113 ++++++++++++++++-----
 .../karavan/kubernetes/KubernetesService.java      |   2 +-
 .../camel/karavan/kubernetes/PodEventHandler.java  |  28 +++--
 .../apache/camel/karavan/service/CamelService.java |   6 +-
 .../apache/camel/karavan/service/EventService.java |  41 ++++----
 .../camel/karavan/service/ScheduledService.java    |  32 +++++-
 .../org/apache/camel/karavan/shared/EventType.java |   2 +-
 .../src/main/resources/application.properties      |   2 +-
 .../src/main/webui/src/api/KaravanApi.tsx          |  23 +++--
 .../src/main/webui/src/api/ProjectModels.ts        |   3 +
 .../src/main/webui/src/api/ProjectService.ts       |  49 ++++++---
 .../src/main/webui/src/api/ProjectStore.ts         |   6 +-
 .../webui/src/containers/ContainerTableRow.tsx     |  71 +++++++++----
 .../main/webui/src/containers/ContainersPage.tsx   |  35 ++++---
 .../src/main/webui/src/project/DevModeToolbar.tsx  |  92 ++++++++---------
 .../src/main/webui/src/project/ProjectPage.tsx     |   2 +-
 .../webui/src/project/dashboard/InfoContainer.tsx  |   4 +-
 .../webui/src/project/pipeline/ProjectStatus.tsx   |   2 +-
 .../main/webui/src/services/ServicesTableRow.tsx   |   4 +-
 .../camel/karavan/headless/CamelService.java       |   4 +-
 .../camel/karavan/headless/HeadlessService.java    |   3 +-
 .../karavan/infinispan/InfinispanService.java      |  24 ++++-
 .../karavan/infinispan/model/ContainerStatus.java  | 104 +++++++++++++------
 .../karavan/infinispan/model/KaravanSchema.java    |   4 +-
 27 files changed, 572 insertions(+), 275 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
index 13edee0e..6379b058 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java
@@ -30,6 +30,7 @@ import javax.inject.Inject;
 import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.util.Objects;
 import java.util.Optional;
 
 @Path("/api/devmode")
@@ -58,7 +59,13 @@ public class DevModeResource {
         String containerName = project.getProjectId();
         ContainerStatus status = infinispanService.getDevModeContainerStatus(project.getProjectId(), environment);
         if (status == null) {
-            infinispanService.saveContainerStatus(ContainerStatus.createDevMode(project.getProjectId(), environment));
+            status = ContainerStatus.createDevMode(project.getProjectId(), environment);
+        }
+
+        if (!Objects.equals(status.getState(), ContainerStatus.State.running.name())){
+            status.setInTransit(true);
+            infinispanService.saveContainerStatus(status);
+
             if (ConfigService.inKubernetes()) {
                 kubernetesService.runDevModeContainer(project, "");
             } else {
@@ -93,15 +100,10 @@ public class DevModeResource {
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/{projectId}/{deletePVC}")
     public Response deleteDevMode(@PathParam("projectId") String projectId, @PathParam("deletePVC") boolean deletePVC) {
-        ContainerStatus status = infinispanService.getDevModeContainerStatus(projectId, environment);
-        if (status != null) {
-            status.setLifeCycle(ContainerStatus.Lifecycle.deleting);
-            infinispanService.saveContainerStatus(status);
-        }
+        infinispanService.setContainerStatusTransit(projectId, environment, projectId);
         if (ConfigService.inKubernetes()) {
             kubernetesService.deleteRunner(projectId, deletePVC);
         } else {
-            dockerService.stopContainer(projectId);
             dockerService.deleteContainer(projectId);
         }
         return Response.accepted().build();
@@ -109,7 +111,7 @@ public class DevModeResource {
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/pod/{projectId}")
+    @Path("/container/{projectId}")
     public Response getPodStatus(@PathParam("projectId") String projectId) throws RuntimeException {
         if (infinispanService.isReady()) {
             ContainerStatus cs = infinispanService.getDevModeContainerStatus(projectId, environment);
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
index 443c9e69..8291c7eb 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
@@ -17,8 +17,10 @@
 package org.apache.camel.karavan.api;
 
 import io.smallrye.mutiny.Multi;
+import io.vertx.core.json.JsonObject;
 import io.vertx.mutiny.core.eventbus.EventBus;
 import io.vertx.mutiny.core.eventbus.Message;
+import org.apache.camel.karavan.docker.DockerService;
 import org.apache.camel.karavan.infinispan.InfinispanService;
 import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.infinispan.model.DeploymentStatus;
@@ -50,6 +52,9 @@ public class InfrastructureResource {
     @Inject
     KubernetesService kubernetesService;
 
+    @Inject
+    DockerService dockerService;
+
     @ConfigProperty(name = "karavan.environment")
     String environment;
 
@@ -101,9 +106,13 @@ public class InfrastructureResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/deployment")
     public List<DeploymentStatus> getAllDeploymentStatuses() throws Exception {
-        return infinispanService.getDeploymentStatuses().stream()
-                .sorted(Comparator.comparing(DeploymentStatus::getProjectId))
-                .collect(Collectors.toList());
+        if (infinispanService.isReady()) {
+            return infinispanService.getDeploymentStatuses().stream()
+                    .sorted(Comparator.comparing(DeploymentStatus::getProjectId))
+                    .collect(Collectors.toList());
+        } else {
+            return List.of();
+        }
     }
 
     @GET
@@ -154,9 +163,29 @@ public class InfrastructureResource {
         }
     }
 
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("/container/{env}/{name}")
+    public Response startContainer(@PathParam("env") String env, @PathParam("name") String name, JsonObject command) throws Exception {
+        if (infinispanService.isReady()) {
+            infinispanService.setContainerStatusTransit(name, env, name);
+            if (command.containsKey("command")) {
+                if (command.getString("command").equalsIgnoreCase("start")) {
+                    dockerService.startContainer(name);
+                    return Response.ok().build();
+                } else if (command.getString("command").equalsIgnoreCase("stop")) {
+                    dockerService.stopContainer(name);
+                    return Response.ok().build();
+                }
+            }
+        }
+        return Response.notModified().build();
+    }
+
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/pod/{env}")
+    @Path("/container/{env}")
     public List<ContainerStatus> getContainerStatusesByEnv(@PathParam("env") String env) throws Exception {
         return infinispanService.getContainerStatuses(env).stream()
                 .sorted(Comparator.comparing(ContainerStatus::getProjectId))
@@ -165,10 +194,10 @@ public class InfrastructureResource {
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/pod/{projectId}/{env}")
+    @Path("/container/{projectId}/{env}")
     public List<ContainerStatus> getContainerStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception {
         return infinispanService.getContainerStatuses(projectId, env).stream()
-                .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.CType.project))
+                .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.ContainerType.project))
                 .sorted(Comparator.comparing(ContainerStatus::getContainerName))
                 .collect(Collectors.toList());
     }
@@ -176,10 +205,23 @@ public class InfrastructureResource {
     @DELETE
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
-    @Path("/pod/{env}/{name}")
-    public Response deleteContainer(@PathParam("env") String env, @PathParam("name") String name) throws Exception {
-        kubernetesService.deletePod(name, kubernetesService.getNamespace());
-        return Response.accepted().build();
+    @Path("/container/{env}/{name}")
+    public Response deleteContainer(@PathParam("env") String env, @PathParam("name") String name) {
+        if (infinispanService.isReady()) {
+            infinispanService.setContainerStatusTransit(name, env, name);
+            try {
+                if (ConfigService.inKubernetes()) {
+                    kubernetesService.deletePod(name, kubernetesService.getNamespace());
+                } else {
+                    dockerService.deleteContainer(name);
+                }
+                return Response.accepted().build();
+            } catch (Exception e) {
+                LOGGER.error(e.getMessage());
+                return Response.notModified().build();
+            }
+        }
+        return Response.notModified().build();
     }
 
     @GET
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
index b055716d..86c40428 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
@@ -17,12 +17,10 @@ import javax.inject.Inject;
 import java.io.Closeable;
 import java.io.IOException;
 import java.time.Instant;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
+import static org.apache.camel.karavan.docker.DockerService.LABEL_PROJECT_ID;
 import static org.apache.camel.karavan.docker.DockerService.LABEL_TYPE;
 import static org.apache.camel.karavan.shared.EventType.DEVMODE_CONTAINER_READY;
 import static org.apache.camel.karavan.shared.EventType.INFINISPAN_STARTED;
@@ -63,24 +61,22 @@ public class DockerEventListener implements ResultCallback<Event> {
 
     public void onContainerEvent(Event event, Container container) {
         if (infinispanService.isReady()) {
-            if (Arrays.asList("destroy", "stop", "die", "kill", "pause", "destroy", "rename").contains(event.getStatus())) {
-                onDeleteContainer(container);
-            } else if (Arrays.asList("create", "start", "unpause").contains(event.getStatus())) {
-                onCreateContainer(container, event);
-            } else {
+//             if (Arrays.asList("create", "start", "unpause", "stop", "pause").contains(event.getStatus())) {
+//                onExistingContainer(container);
+//            } else {
                 String status = event.getStatus();
                 if (status.startsWith("health_status:")) {
                     if (container.getNames()[0].equals("/infinispan")) {
                         onInfinispanHealthEvent(container, event);
                     } else if (inDevMode(container)) {
-                        onDevModeHealthEvent(container,event);
+                        onDevModeHealthEvent(container, event);
                     }
                 }
-            }
+//            }
         }
     }
 
-    private void onDeleteContainer(Container container){
+    public void onDeletedContainer(Container container) {
         String name = container.getNames()[0].replace("/", "");
         infinispanService.deleteContainerStatus(name, environment, name);
         if (inDevMode(container)) {
@@ -88,24 +84,27 @@ public class DockerEventListener implements ResultCallback<Event> {
         }
     }
 
-    protected void onCreateContainer(Container container, Event event){
-        String name = container.getNames()[0].replace("/", "");
-        List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
-        ContainerStatus.Lifecycle lc = event.getStatus().equals("create") ? ContainerStatus.Lifecycle.init : ContainerStatus.Lifecycle.ready;
-        ContainerStatus.CType type = getCtype(container.getLabels());
-        String created = Instant.ofEpochSecond(container.getCreated()).toString();
-        ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name);
-        if (ci == null) {
-            ci = ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, lc, created);
-        } else {
-            ci.setContainerId(container.getId());
-            ci.setPorts(ports);
-            ci.setType(type);
-            ci.setLifeCycle(lc);
-            ci.setCreated(created);
-            ci.setImage(container.getImage());
+    protected void onExistingContainer(Container container) {
+        if (infinispanService.isReady()) {
+            String name = container.getNames()[0].replace("/", "");
+            List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
+            List<ContainerStatus.Command> commands = getContainerCommand(container.getState());
+            ContainerStatus.ContainerType type = getContainerType(container.getLabels());
+            String created = Instant.ofEpochSecond(container.getCreated()).toString();
+            ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name);
+            if (ci == null) {
+                ci = ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created);
+            } else {
+                ci.setContainerId(container.getId());
+                ci.setPorts(ports);
+                ci.setType(type);
+                ci.setCommands(commands);
+                ci.setCreated(created);
+                ci.setState(container.getState());
+                ci.setImage(container.getImage());
+            }
+            infinispanService.saveContainerStatus(ci);
         }
-        infinispanService.saveContainerStatus(ci);
     }
 
     public void onInfinispanHealthEvent(Container container, Event event) {
@@ -116,36 +115,50 @@ public class DockerEventListener implements ResultCallback<Event> {
     }
 
     public void onDevModeHealthEvent(Container container, Event event) {
-        String name = container.getNames()[0].replace("/", "");
         String status = event.getStatus();
         String health = status.replace("health_status: ", "");
         LOGGER.infof("Container %s health status: %s", container.getNames()[0], health);
-        // update ContainerStatus: set ready and
-        ContainerStatus cs = infinispanService.getDevModeContainerStatus(name, environment);
-        if (cs != null) {
-            cs.setLifeCycle(ContainerStatus.Lifecycle.ready);
-            cs.setContainerId(container.getId());
-            infinispanService.saveContainerStatus(cs);
-            eventBus.publish(DEVMODE_CONTAINER_READY, JsonObject.mapFrom(cs));
-        }
+        eventBus.publish(DEVMODE_CONTAINER_READY, container.getLabels().get(LABEL_PROJECT_ID));
     }
 
     private boolean inDevMode(Container container) {
-        return Objects.equals(getCtype(container.getLabels()), ContainerStatus.CType.devmode);
+        return Objects.equals(getContainerType(container.getLabels()), ContainerStatus.ContainerType.devmode);
     }
 
-    private ContainerStatus.CType getCtype(Map<String, String> labels) {
+    private ContainerStatus.ContainerType getContainerType(Map<String, String> labels) {
         String type = labels.get(LABEL_TYPE);
-        if (Objects.equals(type, ContainerStatus.CType.devmode.name())) {
-            return ContainerStatus.CType.devmode;
-        } else if (Objects.equals(type, ContainerStatus.CType.devservice.name())) {
-            return ContainerStatus.CType.devservice;
-        } else if (Objects.equals(type, ContainerStatus.CType.project.name())) {
-            return ContainerStatus.CType.project;
-        } else if (Objects.equals(type, ContainerStatus.CType.internal.name())) {
-            return ContainerStatus.CType.internal;
+        if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) {
+            return ContainerStatus.ContainerType.devmode;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) {
+            return ContainerStatus.ContainerType.devservice;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) {
+            return ContainerStatus.ContainerType.project;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) {
+            return ContainerStatus.ContainerType.internal;
+        }
+        return ContainerStatus.ContainerType.unknown;
+    }
+
+    private List<ContainerStatus.Command> getContainerCommand(String state) {
+        List<ContainerStatus.Command> result = new ArrayList<>();
+        if (Objects.equals(state, ContainerStatus.State.created.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.exited.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.running.name())) {
+            result.add(ContainerStatus.Command.pause);
+            result.add(ContainerStatus.Command.stop);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.paused.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.stop);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.dead.name())) {
+            result.add(ContainerStatus.Command.delete);
         }
-        return ContainerStatus.CType.unknown;
+        return result;
     }
 
     @Override
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
index 27d5a6b3..84178d09 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java
@@ -14,7 +14,7 @@ import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
 import com.github.dockerjava.transport.DockerHttpClient;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.vertx.core.eventbus.EventBus;
-import io.vertx.core.json.JsonObject;
+import org.apache.camel.karavan.infinispan.InfinispanService;
 import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.infinispan.model.Project;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -24,6 +24,7 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import java.io.IOException;
 import java.text.DecimalFormat;
+import java.time.Instant;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
@@ -47,6 +48,9 @@ public class DockerService {
     private static final Map<String, Tuple2<Long, Long>> previousStats = new ConcurrentHashMap<>();
     private static final List<String> infinispanHealthCheckCMD = List.of("CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status");
 
+    @ConfigProperty(name = "karavan.environment")
+    String environment;
+
     @ConfigProperty(name = "karavan.devmode.image")
     String devmodeImage;
 
@@ -67,17 +71,25 @@ public class DockerService {
     @Inject
     DockerEventListener dockerEventListener;
 
+    @Inject
+    InfinispanService infinispanService;
+
     @Inject
     EventBus eventBus;
 
+    private DockerClient dockerClient;
+
     public void runDevmodeContainer(Project project, String jBangOptions) throws InterruptedException {
         String projectId = project.getProjectId();
         LOGGER.infof("DevMode starting for %s", projectId);
+
         HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:8080/q/dev/health"))
                 .withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30);
+
         createContainer(projectId, devmodeImage,
                 List.of(), null, false, false, healthCheck,
-                Map.of(LABEL_TYPE, ContainerStatus.CType.devmode.name(), LABEL_PROJECT_ID, projectId));
+                Map.of(LABEL_TYPE, ContainerStatus.ContainerType.devmode.name(), LABEL_PROJECT_ID, projectId));
+
         startContainer(projectId);
         LOGGER.infof("DevMode started for %s", projectId);
     }
@@ -91,8 +103,9 @@ public class DockerService {
 
             createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage,
                     List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword),
-                    infinispanPort, false, true, healthCheck, Map.of()
-            );
+                    infinispanPort, false, true, healthCheck,
+                    Map.of(LABEL_TYPE, ContainerStatus.ContainerType.internal.name()));
+
             startContainer(INFINISPAN_CONTAINER_NAME);
             LOGGER.info("Infinispan is started");
         } catch (Exception e) {
@@ -110,7 +123,9 @@ public class DockerService {
                             "INFINISPAN_USERNAME=" + infinispanUsername,
                             "INFINISPAN_PASSWORD=" + infinispanPassword
                     ),
-                    null, false, false, new HealthCheck(), Map.of());
+                    null, false, false, new HealthCheck(),
+                    Map.of(LABEL_TYPE, ContainerStatus.ContainerType.internal.name()));
+
             startContainer(KARAVAN_CONTAINER_NAME);
             LOGGER.info("Karavan headless is started");
         } catch (Exception e) {
@@ -127,26 +142,33 @@ public class DockerService {
         }
     }
 
-    public void collectContainersStats() {
-        getDockerClient().listContainersCmd().exec().forEach(container -> {
-            Statistics stats = getContainerStats(container.getId());
-
-            String name = container.getNames()[0].replace("/", "");
-            String memoryUsage = formatMemory(stats.getMemoryStats().getUsage());
-            String memoryLimit = formatMemory(stats.getMemoryStats().getLimit());
-            JsonObject data = JsonObject.of(
-                    "projectId", name,
-                    "memory", memoryUsage + " / " + memoryLimit,
-                    "cpu", formatCpu(name, stats)
-            );
-            eventBus.publish(CONTAINER_STATISTICS, data);
+    public List<ContainerStatus> collectContainersStatuses() {
+        List<ContainerStatus> result = new ArrayList<>();
+        getDockerClient().listContainersCmd().withShowAll(true).exec().forEach(container -> {
+            ContainerStatus containerStatus = getContainerStatus(container);
+            updateStatistics(containerStatus, container);
+            result.add(containerStatus);
         });
+        return result;
     }
 
-    public void collectContainersStatuses() {
-        getDockerClient().listContainersCmd().exec().forEach(container -> {
-            dockerEventListener.onCreateContainer(container, new Event(container.getStatus(), container.getId(), container.getImage(), container.getCreated()));
-        });
+    private ContainerStatus getContainerStatus(Container container) {
+        String name = container.getNames()[0].replace("/", "");
+        List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList());
+        List<ContainerStatus.Command> commands = getContainerCommand(container.getState());
+        ContainerStatus.ContainerType type = getContainerType(container.getLabels());
+        String created = Instant.ofEpochSecond(container.getCreated()).toString();
+        return ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created);
+    }
+
+    private void updateStatistics(ContainerStatus containerStatus, Container container) {
+        Statistics stats = getContainerStats(container.getId());
+        if (stats != null && stats.getMemoryStats() != null) {
+            String memoryUsage = formatMemory(stats.getMemoryStats().getUsage());
+            String memoryLimit = formatMemory(stats.getMemoryStats().getLimit());
+            containerStatus.setMemoryInfo(memoryUsage + " / " + memoryLimit);
+            containerStatus.setCpuInfo(formatCpu(containerStatus.getContainerName(), stats));
+        }
     }
 
     public void startListeners() {
@@ -276,10 +298,14 @@ public class DockerService {
     }
 
     public void deleteContainer(String name) {
+        long time = System.currentTimeMillis();
         List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec();
+        System.out.println("Get containers " + (System.currentTimeMillis() - time));
+        time = System.currentTimeMillis();
         if (containers.size() == 1) {
             Container container = containers.get(0);
-            getDockerClient().removeContainerCmd(container.getId()).exec();
+            getDockerClient().removeContainerCmd(container.getId()).withForce(true).exec();
+            System.out.println("removeContainerCmd " + (System.currentTimeMillis() - time));
         }
     }
 
@@ -335,7 +361,10 @@ public class DockerService {
     }
 
     private DockerClient getDockerClient() {
-        return DockerClientImpl.getInstance(getDockerClientConfig(), getDockerHttpClient());
+        if (dockerClient == null) {
+            dockerClient = DockerClientImpl.getInstance(getDockerClientConfig(), getDockerHttpClient());
+        }
+        return dockerClient;
     }
 
     private String formatMemory(Long memory) {
@@ -350,6 +379,42 @@ public class DockerService {
         }
     }
 
+    private ContainerStatus.ContainerType getContainerType(Map<String, String> labels) {
+        String type = labels.get(LABEL_TYPE);
+        if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) {
+            return ContainerStatus.ContainerType.devmode;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) {
+            return ContainerStatus.ContainerType.devservice;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) {
+            return ContainerStatus.ContainerType.project;
+        } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) {
+            return ContainerStatus.ContainerType.internal;
+        }
+        return ContainerStatus.ContainerType.unknown;
+    }
+
+    private List<ContainerStatus.Command> getContainerCommand(String state) {
+        List<ContainerStatus.Command> result = new ArrayList<>();
+        if (Objects.equals(state, ContainerStatus.State.created.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.exited.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.running.name())) {
+            result.add(ContainerStatus.Command.pause);
+            result.add(ContainerStatus.Command.stop);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.paused.name())) {
+            result.add(ContainerStatus.Command.run);
+            result.add(ContainerStatus.Command.stop);
+            result.add(ContainerStatus.Command.delete);
+        } else if (Objects.equals(state, ContainerStatus.State.dead.name())) {
+            result.add(ContainerStatus.Command.delete);
+        }
+        return result;
+    }
+
     private String formatCpu(String containerName, Statistics stats) {
         try {
             double cpuUsage = 0;
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java
index 92971460..3be6850a 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java
@@ -115,7 +115,7 @@ public class KubernetesService implements HealthCheck {
 
             SharedIndexInformer<Pod> podRunInformer = kubernetesClient().pods().inNamespace(getNamespace())
                     .withLabels(getRuntimeLabels()).inform();
-            podRunInformer.addEventHandlerWithResyncPeriod(new PodEventHandler(infinispanService, this), 30 * 1000L);
+            podRunInformer.addEventHandlerWithResyncPeriod(new PodEventHandler(infinispanService, this, eventBus), 30 * 1000L);
             informers.add(podRunInformer);
 
             LOGGER.info("Started Kubernetes Informers");
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
index e60f354b..b46af28c 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java
@@ -5,21 +5,28 @@ import io.fabric8.kubernetes.api.model.Pod;
 import io.fabric8.kubernetes.api.model.Quantity;
 import io.fabric8.kubernetes.api.model.ResourceRequirements;
 import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.mutiny.core.eventbus.EventBus;
 import org.apache.camel.karavan.infinispan.InfinispanService;
 import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.jboss.logging.Logger;
 
+import java.util.List;
+
 import static org.apache.camel.karavan.service.CodeService.DEFAULT_CONTAINER_RESOURCES;
+import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS;
 
 public class PodEventHandler implements ResourceEventHandler<Pod> {
 
     private static final Logger LOGGER = Logger.getLogger(PodEventHandler.class.getName());
     private final InfinispanService infinispanService;
     private final KubernetesService kubernetesService;
+    private final EventBus eventBus;
 
-    public PodEventHandler(InfinispanService infinispanService, KubernetesService kubernetesService) {
+    public PodEventHandler(InfinispanService infinispanService, KubernetesService kubernetesService, EventBus eventBus) {
         this.infinispanService = infinispanService;
         this.kubernetesService = kubernetesService;
+        this.eventBus = eventBus;
     }
 
     @Override
@@ -28,7 +35,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             LOGGER.info("onAdd " + pod.getMetadata().getName());
             ContainerStatus ps = getPodStatus(pod);
             if (ps != null) {
-                infinispanService.saveContainerStatus(ps);
+                eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(ps));
             }
         } catch (Exception e){
             LOGGER.error(e.getMessage(), e.getCause());
@@ -41,7 +48,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             LOGGER.info("onUpdate " + newPod.getMetadata().getName());
             ContainerStatus ps = getPodStatus(newPod);
             if (ps != null) {
-                infinispanService.saveContainerStatus(ps);
+                eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(ps));
             }
         } catch (Exception e){
             LOGGER.error(e.getMessage(), e.getCause());
@@ -76,17 +83,22 @@ public class PodEventHandler implements ResourceEventHandler<Pod> {
             String requestCpu = resourceRequirements.getRequests().getOrDefault("cpu", new Quantity()).toString();
             String limitMemory = resourceRequirements.getLimits().getOrDefault("memory", new Quantity()).toString();
             String limitCpu = resourceRequirements.getLimits().getOrDefault("cpu", new Quantity()).toString();
-            return new ContainerStatus(
+            ContainerStatus status = new ContainerStatus(
                     pod.getMetadata().getName(),
-                    ContainerStatus.Lifecycle.ready,
+                    List.of(ContainerStatus.Command.delete),
                     projectId,
                     kubernetesService.environment,
-                    pod.getMetadata().getName().equals(projectId) ? ContainerStatus.CType.devmode : ContainerStatus.CType.project,
+                    pod.getMetadata().getName().equals(projectId) ? ContainerStatus.ContainerType.devmode : ContainerStatus.ContainerType.project,
                     requestMemory + " : " + limitMemory,
                     requestCpu + " : " + limitCpu,
-                    creationTimestamp
+                    creationTimestamp);
 
-            );
+            if (ready) {
+                status.setState(ContainerStatus.State.running.name());
+            } else {
+                status.setState(ContainerStatus.State.created.name());
+            }
+            return status;
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage(), ex.getCause());
             return null;
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
index ba3afb33..884ff168 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java
@@ -37,6 +37,8 @@ import java.util.Arrays;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
+import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS;
+
 @ApplicationScoped
 public class CamelService {
 
@@ -71,12 +73,12 @@ public class CamelService {
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
         try {
-            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
             infinispanService.getProjectFiles(projectId).forEach(projectFile ->
                     putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000));
             reloadRequest(projectId);
+            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
             containerStatus.setCodeLoaded(true);
-            infinispanService.saveContainerStatus(containerStatus);
+            eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(containerStatus));
         } catch (Exception ex) {
             LOGGER.error(ex.getMessage());
         }
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
index 4d6f0b7a..f3e011f4 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java
@@ -9,11 +9,13 @@ import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.kubernetes.KubernetesService;
 import org.apache.camel.karavan.shared.ConfigService;
 import org.apache.camel.karavan.shared.EventType;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import java.io.IOException;
+import java.util.Objects;
 
 import static org.apache.camel.karavan.shared.EventType.*;
 
@@ -22,6 +24,9 @@ public class EventService {
 
     private static final Logger LOGGER = Logger.getLogger(EventService.class.getName());
 
+    @ConfigProperty(name = "karavan.environment")
+    String environment;
+
     @Inject
     InfinispanService infinispanService;
 
@@ -37,9 +42,6 @@ public class EventService {
     @Inject
     ProjectService projectService;
 
-    @Inject
-    ConfigService configService;
-
     @Inject
     EventBus bus;
 
@@ -73,29 +75,30 @@ public class EventService {
     }
 
     @ConsumeEvent(value = DEVMODE_CONTAINER_READY, blocking = true, ordered = true)
-    void receiveCommand(JsonObject message) {
-        LOGGER.info("received Status " + message);
-        ContainerStatus status = message.mapTo(ContainerStatus.class);
-        if (!status.getCodeLoaded() && status.getContainerId() != null && status.getLifeCycle().equals(ContainerStatus.Lifecycle.ready)) {
+    void receiveCommand(String projectId) {
+        LOGGER.info("DEVMODE_CONTAINER_READY " + projectId);
+        ContainerStatus status = infinispanService.getContainerStatus(projectId, environment, projectId);
+        if (!status.getCodeLoaded() && status.getContainerId() != null && status.getState().equals(ContainerStatus.State.running.name())) {
             if (ConfigService.inKubernetes()) {
-                camelService.reloadProjectCode(status.getProjectId());
+                camelService.reloadProjectCode(projectId);
             } else {
-                infinispanService.sendCodeReloadCommand(status.getProjectId());
+                infinispanService.sendCodeReloadCommand(projectId);
             }
         }
     }
 
-    @ConsumeEvent(value = CONTAINER_STATISTICS, blocking = true, ordered = true)
-    public void saveStats(JsonObject data) {
-        String projectId = data.getString("projectId");
-        String memory = data.getString("memory");
-        String cpu = data.getString("cpu");
+    @ConsumeEvent(value = CONTAINER_STATUS, blocking = true, ordered = true)
+    public void saveContainerStatus(JsonObject data) {
         if (infinispanService.isReady()) {
-            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, configService.getConfiguration().getEnvironment());
-            if (containerStatus != null) {
-                containerStatus.setCpuInfo(cpu);
-                containerStatus.setMemoryInfo(memory);
-                infinispanService.saveContainerStatus(containerStatus);
+            ContainerStatus newStatus = data.mapTo(ContainerStatus.class);
+            System.out.println(newStatus);
+            ContainerStatus oldStatus = infinispanService.getContainerStatus(newStatus.getProjectId(), newStatus.getEnv(), newStatus.getContainerName());
+            if (oldStatus == null || Objects.equals(oldStatus.getInTransit(), Boolean.FALSE)) {
+                infinispanService.saveContainerStatus(newStatus);
+            } else if (Objects.equals(oldStatus.getInTransit(), Boolean.TRUE)){
+                if (!Objects.equals(oldStatus.getState(), newStatus.getState())) {
+                    infinispanService.saveContainerStatus(newStatus);
+                }
             }
         }
     }
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
index 7886a86d..16fcbeb6 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java
@@ -17,19 +17,29 @@
 package org.apache.camel.karavan.service;
 
 import io.quarkus.scheduler.Scheduled;
+import io.vertx.core.eventbus.EventBus;
+import io.vertx.core.json.JsonObject;
 import org.apache.camel.karavan.docker.DockerService;
 import org.apache.camel.karavan.infinispan.InfinispanService;
+import org.apache.camel.karavan.infinispan.model.ContainerStatus;
 import org.apache.camel.karavan.shared.ConfigService;
+import org.apache.camel.karavan.shared.EventType;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.jboss.logging.Logger;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @ApplicationScoped
 public class ScheduledService {
 
     private static final Logger LOGGER = Logger.getLogger(ScheduledService.class.getName());
 
+    @ConfigProperty(name = "karavan.environment")
+    String environment;
+
     @Inject
     DockerService dockerService;
 
@@ -42,9 +52,25 @@ public class ScheduledService {
     @Inject
     InfinispanService infinispanService;
 
-    @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
-    void collectContainersStats() {
-        dockerService.collectContainersStats();
+    @Inject
+    EventBus eventBus;
+
+    @Scheduled(every = "{karavan.container.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
+    void collectContainersStatuses() {
+        if (infinispanService.isReady()) {
+            List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatuses();
+            List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).collect(Collectors.toList());
+            List<ContainerStatus> statusesInInfinispan = infinispanService.getContainerStatuses(environment);
+            // clean deleted
+            statusesInInfinispan.stream().filter(cs -> !namesInDocker.contains(cs.getContainerName())).forEach(containerStatus -> {
+                infinispanService.deleteContainerStatus(containerStatus);
+                infinispanService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv());
+            });
+            // send statuses to save
+            statusesInDocker.forEach(containerStatus -> {
+                eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus));
+            });
+        }
     }
 
     @Scheduled(every = "{karavan.container.infinispan.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
index ac89a948..a1085fb9 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java
@@ -10,7 +10,7 @@ public class EventType {
 
     public static final String INFINISPAN_STARTED = "INFINISPAN_STARTED";
 
-    public static final String CONTAINER_STATISTICS = "CONTAINER_STATISTICS";
+    public static final String CONTAINER_STATUS = "CONTAINER_STATUS";
     public static final String DEVMODE_CONTAINER_READY = "DEVMODE_STATUS";
 
 }
diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties
index 37a78899..93f3db4b 100644
--- a/karavan-web/karavan-app/src/main/resources/application.properties
+++ b/karavan-web/karavan-app/src/main/resources/application.properties
@@ -4,7 +4,7 @@ karavan.environments=dev
 karavan.default-runtime=quarkus
 karavan.runtimes=quarkus,spring-boot
 karavan.camel.status.interval=off
-karavan.container.statistics.interval=3s
+karavan.container.status.interval=3s
 karavan.container.infinispan.interval=5s
 karavan.devmode.image=ghcr.io/apache/camel-karavan-runner:3.21.1-snapshot
 karavan.headless.image=entropy1/karavan-headless:3.21.1-SNAPSHOT
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 7a964268..fc0ed2e8 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -304,7 +304,7 @@ export class KaravanApi {
     }
 
     static async getDevModePodStatus(projectId: string, after: (res: AxiosResponse<ContainerStatus>) => void) {
-        instance.get('/api/devmode/pod/' + projectId)
+        instance.get('/api/devmode/container/' + projectId)
             .then(res => {
                 after(res);
             }).catch(err => {
@@ -312,7 +312,7 @@ export class KaravanApi {
         });
     }
 
-    static async reloadDevMode(projectId: string, after: (res: AxiosResponse<any>) => void) {
+    static async reloadDevModeCode(projectId: string, after: (res: AxiosResponse<any>) => void) {
         instance.get('/api/devmode/reload/' + projectId)
             .then(res => {
                 after(res);
@@ -330,7 +330,7 @@ export class KaravanApi {
         });
     }
 
-    static async runProject(project: Project, verbose: boolean, after: (res: AxiosResponse<string>) => void) {
+    static async startDevModeContainer(project: Project, verbose: boolean, after: (res: AxiosResponse<string>) => void) {
         instance.post('/api/devmode' + (verbose ? '/--verbose' : ''), project)
             .then(res => {
                 after(res);
@@ -339,7 +339,7 @@ export class KaravanApi {
         });
     }
 
-    static async deleteRunner(name: string, deletePVC: boolean, after: (res: AxiosResponse<any>) => void) {
+    static async deleteDevModeContainer(name: string, deletePVC: boolean, after: (res: AxiosResponse<any>) => void) {
         instance.delete('/api/devmode/' +  name + "/" + deletePVC)
             .then(res => {
                 after(res);
@@ -453,7 +453,7 @@ export class KaravanApi {
     }
 
     static async getProjectPodStatuses(project: string, env: string, after: (statuses: ContainerStatus[]) => void) {
-        instance.get('/api/infrastructure/pod/' + project + "/" + env)
+        instance.get('/api/infrastructure/container/' + project + "/" + env)
             .then(res => {
                 if (res.status === 200) {
                     after(res.data);
@@ -463,8 +463,17 @@ export class KaravanApi {
         });
     }
 
-    static async deletePod(environment: string, name: string, after: (res: AxiosResponse<any>) => void) {
-        instance.delete('/api/infrastructure/pod/' + environment + '/' + name)
+    static async manageContainer(environment: string, name: string, command: 'run' | 'pause' | 'stop', after: (res: AxiosResponse<any>) => void) {
+        instance.post('/api/infrastructure/container/' + environment + '/' + name, {command: command})
+            .then(res => {
+                after(res);
+            }).catch(err => {
+            after(err);
+        });
+    }
+
+    static async deleteContainer(environment: string, name: string, after: (res: AxiosResponse<any>) => void) {
+        instance.delete('/api/infrastructure/container/' + environment + '/' + name)
             .then(res => {
                 after(res);
             }).catch(err => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
index fe0fb19e..3a4ced02 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts
@@ -71,6 +71,7 @@ export class ContainerStatus {
     containerName: string = '';
     containerId: string = '';
     lifeCycle: string = '';
+    state: string = '';
     deployment: string = '';
     projectId: string = '';
     env: string = '';
@@ -80,6 +81,8 @@ export class ContainerStatus {
     created: string = '';
     image: string = '';
     ports: [] = [];
+    commands: string [] = [];
+    inTransit: boolean = false;
 
     public constructor(init?: Partial<ContainerStatus>) {
         Object.assign(this, init);
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index 4fbd9da7..28f6cf2b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -14,43 +14,68 @@ import {ProjectEventBus} from "./ProjectEventBus";
 
 export class ProjectService {
 
-    public static startRunner(project: Project, verbose: boolean) {
-        useDevModeStore.setState({status: "starting"})
-        KaravanApi.runProject(project, verbose, res => {
+    public static startDevModeContainer(project: Project, verbose: boolean) {
+        useDevModeStore.setState({status: "wip"})
+        KaravanApi.startDevModeContainer(project, verbose, res => {
             if (res.status === 200 || res.status === 201) {
                 ProjectEventBus.sendLog("set", '');
                 useLogStore.setState({showLog: true, type: 'container', podName: res.data})
+                useDevModeStore.setState({status: "none"})
             } else {
                 // Todo notification
             }
         });
     }
 
-    public static reloadRunner(project: Project) {
-        useDevModeStore.setState({status: "reloading"})
-        KaravanApi.reloadDevMode(project.projectId, res => {
+    public static reloadDevModeCode(project: Project) {
+        useDevModeStore.setState({status: "wip"})
+        KaravanApi.reloadDevModeCode(project.projectId, res => {
             if (res.status === 200 || res.status === 201) {
                 // setIsReloadingPod(false);
             } else {
                 // Todo notification
                 // setIsReloadingPod(false);
             }
+            useDevModeStore.setState({status: "none"})
         });
     }
 
-    public static deleteRunner(project: Project) {
-        useDevModeStore.setState({status: "deleting"})
+    public static stopDevModeContainer(project: Project) {
+        useDevModeStore.setState({status: "wip"})
+        KaravanApi.manageContainer("dev", project.projectId, 'stop', res => {
+            if (res.status === 200) {
+                useLogStore.setState({showLog: false, type: 'container', isRunning: false})
+            } else {
+                ProjectEventBus.sendAlert(new ToastMessage("Error stopping DevMode container", res.statusText, 'warning'))
+            }
+        });
+    }
+
+    public static pauseDevModeContainer(project: Project) {
+        useDevModeStore.setState({status: "wip"})
+        KaravanApi.manageContainer("dev", project.projectId, 'pause', res => {
+            if (res.status === 200) {
+                useLogStore.setState({showLog: false, type: 'container', isRunning: false})
+            } else {
+                ProjectEventBus.sendAlert(new ToastMessage("Error stopping DevMode container", res.statusText, 'warning'))
+            }
+        });
+    }
+
+    public static deleteDevModeContainer(project: Project) {
+        useDevModeStore.setState({status: "wip"})
         ProjectEventBus.sendLog("set", '');
-        KaravanApi.deleteRunner(project.projectId, false, res => {
+        KaravanApi.deleteDevModeContainer(project.projectId, false, res => {
             if (res.status === 202) {
                 useLogStore.setState({showLog: false, type: 'container', isRunning: false})
             } else {
                 ProjectEventBus.sendAlert(new ToastMessage("Error delete runner", res.statusText, 'warning'))
             }
+            useDevModeStore.setState({status: "none"})
         });
     }
 
-    public static getDevModePodStatus(project: Project) {
+    public static getDevModeStatus(project: Project) {
         const projectId = project.projectId;
         KaravanApi.getDevModePodStatus(projectId, res => {
             if (res.status === 200) {
@@ -59,8 +84,8 @@ export class ProjectService {
                     if (useDevModeStore.getState().podName !== podStatus.containerName){
                         useDevModeStore.setState({podName: podStatus.containerName})
                     }
-                    if (useDevModeStore.getState().status !== "running"){
-                        useDevModeStore.setState({status: "running"})
+                    if (useDevModeStore.getState().status !== "wip"){
+                        useDevModeStore.setState({status: "wip"})
                         useLogStore.setState({isRunning: true})
                     }
                     useProjectStore.setState({containerStatus: res.data});
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
index 418d1848..80c99593 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -137,14 +137,14 @@ export const useFileStore = create<FileState>((set) => ({
 
 interface DevModeState {
     podName?: string,
-    status: "none" | "starting" | "deleting"| "reloading" | "running",
-    setStatus: (status: "none" | "starting" | "deleting"| "reloading" | "running") => void,
+    status: "none" | "wip",
+    setStatus: (status: "none" | "wip") => void,
 }
 
 export const useDevModeStore = create<DevModeState>((set) => ({
     podName: undefined,
     status: "none",
-    setStatus: (status: "none" | "starting" | "deleting"| "reloading" | "running") =>  {
+    setStatus: (status: "none" | "wip") =>  {
         set((state: DevModeState) => ({
             status: status,
         }));
diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
index 29241c44..660c2f0d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
@@ -2,13 +2,16 @@ import React, {useState} from 'react';
 import {
     Button,
     Tooltip,
-    Flex, FlexItem, Label, Badge
+    Flex, FlexItem, Label, Badge, Spinner
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {ExpandableRowContent, Tbody, Td, Tr} from "@patternfly/react-table";
 import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon";
 import PlayIcon from "@patternfly/react-icons/dist/esm/icons/play-icon";
 import {ContainerStatus} from "../api/ProjectModels";
+import PauseIcon from "@patternfly/react-icons/dist/esm/icons/pause-icon";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import {KaravanApi} from "../api/KaravanApi";
 
 interface Props {
     index: number
@@ -18,14 +21,13 @@ interface Props {
 export const ContainerTableRow = (props: Props) => {
 
     const [isExpanded, setIsExpanded] = useState<boolean>(false);
-    const [running, setRunning] = useState<boolean>(false);
 
     const container = props.container;
-    const env = container.env;
+    const commands = container.commands;
     const ports = container.ports;
-    const icon = running ? <StopIcon/> : <PlayIcon/>;
-    const tooltip = running ? "Stop container" : "Start container";
-    const color = container.lifeCycle === 'ready' ? "green" : "grey";
+    const isRunning = container.state === 'running';
+    const inTransit = container.inTransit;
+    const color = container.state === 'running' ? "green" : "grey";
     return (
         <Tbody isExpanded={isExpanded}>
             <Tr key={container.containerName}>
@@ -48,23 +50,52 @@ export const ContainerTableRow = (props: Props) => {
                 </Td>
                 <Td>{container.image}</Td>
                 <Td>
-                    <Label color={color}>{container.cpuInfo}</Label>
+                    {isRunning && container.cpuInfo && <Label color={color}>{container.cpuInfo}</Label>}
                 </Td>
                 <Td>
-                    <Label color={color}>{container.memoryInfo}</Label>
+                    {isRunning && container.memoryInfo && <Label color={color}>{container.memoryInfo}</Label>}
+                </Td>
+                <Td>
+                    {!inTransit && <Label color={color}>{container.state}</Label>}
+                    {inTransit && <Spinner isSVG size="md" aria-label="spinner"/>}
                 </Td>
-                {/*<Td>{container.environment}</Td>*/}
                 <Td className="project-action-buttons">
-                    <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}}
-                          spaceItems={{default: 'spaceItemsNone'}}>
-                        <FlexItem>
-                            <Tooltip content={tooltip} position={"bottom"}>
-                                <Button variant={"plain"} icon={icon} onClick={e => {
-                                    // setProject(project, "delete");
-                                }}></Button>
-                            </Tooltip>
-                        </FlexItem>
-                    </Flex>
+                    {container.type !== 'internal' &&
+                        <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}}
+                              spaceItems={{default: 'spaceItemsNone'}}>
+                            <FlexItem>
+                                <Tooltip content={"Start container"} position={"bottom"}>
+                                    <Button variant={"plain"} icon={<PlayIcon/>} isDisabled={!commands.includes('run') || inTransit}
+                                            onClick={e => {
+                                                KaravanApi.manageContainer(container.env, container.containerName, 'run', res => {});
+                                            }}></Button>
+                                </Tooltip>
+                            </FlexItem>
+                            <FlexItem>
+                                <Tooltip content={"Pause container"} position={"bottom"}>
+                                    <Button variant={"plain"} icon={<PauseIcon/>} isDisabled={!commands.includes('pause') || inTransit}
+                                            onClick={e => {
+                                                KaravanApi.manageContainer(container.env, container.containerName, 'pause', res => {});
+                                            }}></Button>
+                                </Tooltip>
+                            </FlexItem>
+                            <FlexItem>
+                                <Tooltip content={"Stop container"} position={"bottom"}>
+                                    <Button variant={"plain"} icon={<StopIcon/>} isDisabled={!commands.includes('stop') || inTransit}
+                                            onClick={e => {
+                                                KaravanApi.manageContainer(container.env, container.containerName, 'stop', res => {});
+                                            }}></Button>
+                                </Tooltip>
+                            </FlexItem>
+                            <FlexItem>
+                                <Tooltip content={"Delete container"} position={"bottom"}>
+                                    <Button variant={"plain"} icon={<DeleteIcon/>} isDisabled={!commands.includes('delete') || inTransit}
+                                            onClick={e => {
+                                                KaravanApi.deleteContainer(container.env, container.containerName, res => {});
+                                            }}></Button>
+                                </Tooltip>
+                            </FlexItem>
+                        </Flex>}
                 </Td>
             </Tr>
             {<Tr isExpanded={isExpanded}>
@@ -84,7 +115,7 @@ export const ContainerTableRow = (props: Props) => {
                 <Td colSpan={2}>
                     <ExpandableRowContent>
                         <Flex direction={{default: "row"}} cellPadding={"0px"}>
-                            {ports.map(port => <FlexItem>{port}</FlexItem>)}
+                            {ports.map((port, index) => <FlexItem key={index}>{port}</FlexItem>)}
                         </Flex>
                     </ExpandableRowContent>
                 </Td>
diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
index 34d44ff7..9d1f6607 100644
--- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
@@ -40,7 +40,7 @@ export const ContainersPage = () => {
     useEffect(() => {
         const interval = setInterval(() => {
             onGetProjects()
-        }, 2000);
+        }, 700);
         return () => {
             clearInterval(interval)
         };
@@ -111,21 +111,23 @@ export const ContainersPage = () => {
 
     function getEmptyState() {
         return (
-            <Tr>
-                <Td colSpan={8}>
-                    <Bullseye>
-                        {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>}
-                        {!loading &&
-                            <EmptyState variant={EmptyStateVariant.small}>
-                                <EmptyStateIcon icon={SearchIcon}/>
-                                <Title headingLevel="h2" size="lg">
-                                    No results found
-                                </Title>
-                            </EmptyState>
-                        }
-                    </Bullseye>
-                </Td>
-            </Tr>
+            <Tbody>
+                <Tr>
+                    <Td colSpan={8}>
+                        <Bullseye>
+                            {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>}
+                            {!loading &&
+                                <EmptyState variant={EmptyStateVariant.small}>
+                                    <EmptyStateIcon icon={SearchIcon}/>
+                                    <Title headingLevel="h2" size="lg">
+                                        No results found
+                                    </Title>
+                                </EmptyState>
+                            }
+                        </Bullseye>
+                    </Td>
+                </Tr>
+            </Tbody>
         )
     }
 
@@ -145,6 +147,7 @@ export const ContainersPage = () => {
                             <Th key='image'>Image</Th>
                             <Th key='cpuInfo'>CPU</Th>
                             <Th key='memoryInfo'>Memory</Th>
+                            <Th key='state'>State</Th>
                             <Th key='action'></Th>
                         </Tr>
                     </Thead>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
index 4f202b92..510372e5 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
@@ -3,12 +3,13 @@ import {Button, Flex, FlexItem, Label, Switch, Tooltip, TooltipPosition} from '@
 import '../designer/karavan.css';
 import RocketIcon from "@patternfly/react-icons/dist/esm/icons/rocket-icon";
 import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon";
-import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
+import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";
 import {useDevModeStore, useLogStore, useProjectStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
 import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
 import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
+import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon";
 
 
 interface Props {
@@ -18,29 +19,18 @@ interface Props {
 export const DevModeToolbar = (props: Props) => {
 
     const [status] = useDevModeStore((state) => [state.status], shallow)
-    const [project,containerStatus ] = useProjectStore((state) => [state.project, state.containerStatus], shallow)
+    const [project, containerStatus ] = useProjectStore((state) => [state.project, state.containerStatus], shallow)
     const [verbose, setVerbose] = useState(false);
 
-
-    function getColor() {
-        return getRunning() ? "green" : "grey";
-    }
-
-    function getRunning(): boolean {
-        return containerStatus.lifeCycle === 'ready';
-    }
-
-    function getIcon() {
-        return (getRunning() ? <UpIcon/> : <DownIcon/>)
-    }
-
-    const isRunning = status === "running";
-    const isStartingPod = status === "starting";
-    const isReloadingPod = status === "reloading";
-    const isDeletingPod = status === "deleting";
+    const commands = containerStatus.commands;
+    const ports = containerStatus.ports;
+    const isRunning = containerStatus.state === 'running';
+    const inTransit = containerStatus.inTransit;
+    const color = containerStatus.state === 'running' ? "green" : "grey";
+    const icon = isRunning ? <UpIcon/> : <DownIcon/>;
     return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}>
-        {isRunning && <FlexItem>
-            <Label icon={getIcon()} color={getColor()}>
+        {<FlexItem>
+            <Label icon={icon} color={color}>
                 <Tooltip content={"Show log"} position={TooltipPosition.bottom}>
                     <Button variant="link"
                             onClick={e =>
@@ -50,19 +40,7 @@ export const DevModeToolbar = (props: Props) => {
                 </Tooltip>
             </Label>
         </FlexItem>}
-        {(isRunning || isDeletingPod) && !isReloadingPod && props.reloadOnly !== true && <FlexItem>
-            <Tooltip content="Stop devmode" position={TooltipPosition.bottom}>
-                <Button isLoading={isDeletingPod ? true : undefined}
-                        isSmall
-                        variant={"secondary"}
-                        className="project-button"
-                        icon={!isRunning ? <DeleteIcon/> : <div></div>}
-                        onClick={() => ProjectService.deleteRunner(project)}>
-                    {isDeletingPod ? "..." : "Stop"}
-                </Button>
-            </Tooltip>
-        </FlexItem>}
-        {!isRunning && !isReloadingPod && !isDeletingPod && props.reloadOnly !== true && <FlexItem>
+        <FlexItem>
             <Tooltip content="Verbose" position={TooltipPosition.bottom}>
                 <Switch aria-label="verbose"
                         id="verbose"
@@ -70,28 +48,50 @@ export const DevModeToolbar = (props: Props) => {
                         onChange={checked => setVerbose(checked)}
                 />
             </Tooltip>
-        </FlexItem>}
-        {!isRunning && !isReloadingPod && props.reloadOnly !== true && <FlexItem>
+        </FlexItem>
+        {!isRunning && <FlexItem>
             <Tooltip content="Run in developer mode" position={TooltipPosition.bottom}>
-                <Button isLoading={isStartingPod ? true : undefined}
+                <Button isLoading={status === 'wip'}
                         isSmall
+                        isDisabled={(!(commands.length === 0) && !commands.includes('run')) || inTransit}
                         variant={"primary"}
-                        className="project-button"
-                        icon={!isStartingPod ? <RocketIcon/> : <div></div>}
-                        onClick={() => ProjectService.startRunner(project, verbose)}>
-                    {isStartingPod ? "..." : "Run"}
+                        icon={<RocketIcon/>}
+                        onClick={() => ProjectService.startDevModeContainer(project, verbose)}>
+                    {"Run"}
                 </Button>
             </Tooltip>
         </FlexItem>}
-        {(isRunning || isReloadingPod) && <FlexItem>
+        {isRunning && <FlexItem>
             <Tooltip content="Reload" position={TooltipPosition.bottom}>
-                <Button isLoading={isReloadingPod ? true : undefined}
+                <Button isLoading={status === 'wip'}
                         isSmall
+                        isDisabled={inTransit}
                         variant={"primary"}
                         className="project-button"
-                        icon={!isReloadingPod ? <ReloadIcon/> : <div></div>}
-                        onClick={() => ProjectService.reloadRunner(project)}>
-                    {isReloadingPod ? "..." : "Reload"}
+                        icon={<ReloadIcon/>}
+                        onClick={() => ProjectService.reloadDevModeCode(project)}>Reload
+                </Button>
+            </Tooltip>
+        </FlexItem>}
+        {<FlexItem>
+            <Tooltip content="Stop container" position={TooltipPosition.bottom}>
+                <Button isLoading={status === 'wip'}
+                        isSmall
+                        isDisabled={!commands.includes('stop') || inTransit}
+                        variant={"control"}
+                        icon={<StopIcon/>}
+                        onClick={() => ProjectService.stopDevModeContainer(project)}>
+                </Button>
+            </Tooltip>
+        </FlexItem>}
+        {<FlexItem>
+            <Tooltip content="Delete container" position={TooltipPosition.bottom}>
+                <Button isLoading={status === 'wip'}
+                        isSmall
+                        isDisabled={!commands.includes('delete') || inTransit}
+                        variant={"control"}
+                        icon={<DeleteIcon/>}
+                        onClick={() => ProjectService.deleteDevModeContainer(project)}>
                 </Button>
             </Tooltip>
         </FlexItem>}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 90b08ed3..cab863b6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -26,7 +26,7 @@ export const ProjectPage = () => {
     useEffect(() => {
         // TODO: make status request only when started or just opened
         const interval = setInterval(() => {
-            ProjectService.getDevModePodStatus(project);
+            ProjectService.getDevModeStatus(project);
         }, 1000);
         return () => {
             clearInterval(interval)
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
index 03bc5f9e..29c7247d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
@@ -35,7 +35,7 @@ export const InfoContainer = (props: Props) => {
     }
 
     function getRunning(): boolean {
-        return props.containerStatus.lifeCycle === 'ready';
+        return props.containerStatus.state === 'running';
     }
 
     const containerStatus = props.containerStatus;
@@ -50,7 +50,7 @@ export const InfoContainer = (props: Props) => {
             <DescriptionListGroup>
                 <DescriptionListTerm>Status</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {getPodInfoLabel(containerStatus.lifeCycle)}
+                    {getPodInfoLabel(containerStatus.state)}
                 </DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
index 491a2b80..f32d070e 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
@@ -87,7 +87,7 @@ export class ProjectStatus extends React.Component<Props, State> {
                 });
                 break;
             case "pod":
-                KaravanApi.deletePod(environment, name, (res: any) => {
+                KaravanApi.deleteContainer(environment, name, (res: any) => {
                     // if (Array.isArray(res) && Array.from(res).length > 0)
                     // this.onRefresh();
                 });
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
index 621f58a5..f6affafe 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
@@ -47,7 +47,7 @@ export const ServicesTableRow = (props: Props) => {
                 <Td>{service.image}</Td>
                 <Td>
                     <Flex direction={{default: "row"}}>
-                        {service.ports.map(port => <FlexItem>{port}</FlexItem>)}
+                        {service.ports.map(port => <FlexItem key={port}>{port}</FlexItem>)}
                     </Flex>
                 </Td>
                 {/*<Td>{service.environment}</Td>*/}
@@ -70,7 +70,7 @@ export const ServicesTableRow = (props: Props) => {
                 <Td colSpan={2}>
                     <ExpandableRowContent>
                         <Flex direction={{default: "column"}} cellPadding={"0px"}>
-                        {keys.map(key => <FlexItem>{key + ": " + env[key]}</FlexItem>)}
+                        {keys.map(key => <FlexItem key={key}>{key + ": " + env[key]}</FlexItem>)}
                         </Flex>
                     </ExpandableRowContent>
                 </Td>
diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java
index 41aa5838..089ade28 100644
--- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java
+++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/CamelService.java
@@ -66,10 +66,10 @@ public class CamelService {
     public void reloadProjectCode(String projectId) {
         LOGGER.info("Reload project code " + projectId);
         try {
-            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
             infinispanService.getProjectFiles(projectId).forEach(projectFile ->
                     putRequest(projectId, projectFile.getName(), projectFile.getCode(), 1000));
             reloadRequest(projectId);
+            ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment);
             containerStatus.setCodeLoaded(true);
             infinispanService.saveContainerStatus(containerStatus);
         } catch (Exception ex) {
@@ -106,7 +106,7 @@ public class CamelService {
     public void collectCamelStatuses() {
         if (infinispanService.isReady()) {
             infinispanService.getContainerStatuses(environment).stream()
-                    .filter(status -> status.getType().equals(ContainerStatus.CType.devmode) || status.getType().equals(ContainerStatus.CType.project))
+                    .filter(status -> status.getType().equals(ContainerStatus.ContainerType.devmode) || status.getType().equals(ContainerStatus.ContainerType.project))
                     .forEach(status -> {
                         CamelStatusRequest csr = new CamelStatusRequest(status.getProjectId(), status.getContainerName());
                         eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr));
diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java
index e6f8624f..ba2b805b 100644
--- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java
+++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java
@@ -39,12 +39,11 @@ public class HeadlessService {
     void onStart(@Observes StartupEvent ev) {
         LOGGER.info("Starting Headless Karavan");
         infinispanService.start(true);
-        infinispanService.clearAllStatuses();
     }
 
     @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
     void collectCamelStatuses() {
-        LOGGER.info("Collect info statuses");
+        LOGGER.info("Collect Camel statuses");
         // collect Camel statuses
         camelService.collectCamelStatuses();
     }
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
index efd1ee2c..ac905f64 100644
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
+++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
@@ -67,6 +67,7 @@ public class InfinispanService {
     private RemoteCache<GroupedKey, PipelineStatus> pipelineStatuses;
     private RemoteCache<GroupedKey, DeploymentStatus> deploymentStatuses;
     private RemoteCache<GroupedKey, ContainerStatus> containerStatuses;
+    private RemoteCache<GroupedKey, Boolean> transits;
     private RemoteCache<GroupedKey, ServiceStatus> serviceStatuses;
     private RemoteCache<GroupedKey, CamelStatus> camelStatuses;
     private RemoteCache<String, String> commits;
@@ -107,6 +108,7 @@ public class InfinispanService {
         serviceStatuses = getOrCreateCache(ServiceStatus.CACHE, false);
         camelStatuses = getOrCreateCache(CamelStatus.CACHE, false);
         commits = getOrCreateCache("commits", false);
+        transits = getOrCreateCache("transits", false);
         deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE, false);
         codeReloadCommands = getOrCreateCache("code_reload_commands", true);
 
@@ -250,6 +252,18 @@ public class InfinispanService {
         return new ArrayList<>(serviceStatuses.values());
     }
 
+    public List<Boolean> getTransits() {
+        return new ArrayList<>(transits.values());
+    }
+
+    public Boolean getTransit(String projectId, String env, String containerName) {
+        return transits.get(GroupedKey.create(projectId, env, containerName));
+    }
+
+    public void setTransit(String projectId, String env, String containerName) {
+        transits.put(GroupedKey.create(projectId,env,containerName), true);
+    }
+
     public List<ContainerStatus> getContainerStatuses() {
         return new ArrayList<>(containerStatuses.values());
     }
@@ -262,6 +276,12 @@ public class InfinispanService {
                 .execute().list();
     }
 
+    public void setContainerStatusTransit(String projectId, String env, String containerName) {
+        ContainerStatus cs = getContainerStatus(projectId, env, containerName);
+        cs.setInTransit(true);
+        saveContainerStatus(cs);
+    }
+
     public ContainerStatus getContainerStatus(String projectId, String env, String containerName) {
         return containerStatuses.get(GroupedKey.create(projectId, env, containerName));
     }
@@ -353,14 +373,14 @@ public class InfinispanService {
     public List<ContainerStatus> getLoadedDevModeStatuses() {
         QueryFactory queryFactory = Search.getQueryFactory(containerStatuses);
         return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type AND codeLoaded = true")
-                .setParameter("type", ContainerStatus.CType.devmode)
+                .setParameter("type", ContainerStatus.ContainerType.devmode)
                 .execute().list();
     }
 
     public List<ContainerStatus> getDevModeStatuses() {
         QueryFactory queryFactory = Search.getQueryFactory(containerStatuses);
         return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type")
-                .setParameter("type", ContainerStatus.CType.devmode)
+                .setParameter("type", ContainerStatus.ContainerType.devmode)
                 .execute().list();
     }
 
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
index de1fea4e..ef7ca779 100644
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
+++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java
@@ -9,7 +9,16 @@ import java.util.List;
 
 public class ContainerStatus {
 
-    public enum CType {
+    public enum State {
+        created,
+        running,
+        restarting,
+        paused,
+        exited,
+        dead
+    }
+
+    public enum ContainerType {
         @ProtoEnumValue(number = 0, name = "internal") internal,
         @ProtoEnumValue(number = 1, name = "devmode") devmode,
         @ProtoEnumValue(number = 2, name = "devservice") devservice,
@@ -17,10 +26,11 @@ public class ContainerStatus {
         @ProtoEnumValue(number = 5, name = "unknown") unknown,
     }
 
-    public enum Lifecycle {
-        @ProtoEnumValue(number = 0, name = "init") init,
-        @ProtoEnumValue(number = 1, name = "ready") ready,
-        @ProtoEnumValue(number = 2, name = "deleting") deleting,
+    public enum Command {
+        @ProtoEnumValue(number = 0, name = "run") run,
+        @ProtoEnumValue(number = 1, name = "pause") pause,
+        @ProtoEnumValue(number = 2, name = "stop") stop,
+        @ProtoEnumValue(number = 3, name = "delete") delete,
     }
 
     public static final String CACHE = "container_statuses";
@@ -37,7 +47,7 @@ public class ContainerStatus {
     @ProtoField(number = 6)
     String env;
     @ProtoField(number = 7)
-    CType type;
+    ContainerType type;
     @ProtoField(number = 8)
     String memoryInfo;
     @ProtoField(number = 9)
@@ -45,14 +55,16 @@ public class ContainerStatus {
     @ProtoField(number = 10)
     String created;
     @ProtoField(number = 11)
-    Lifecycle lifeCycle;
+    List<Command> commands;
     @ProtoField(number = 12)
-    Boolean codeLoaded;
+    String state;
     @ProtoField(number = 13)
-    Boolean logging;
+    Boolean codeLoaded;
+    @ProtoField(number = 14)
+    Boolean inTransit = false;
 
     @ProtoFactory
-    public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, CType type, String memoryInfo, String cpuInfo, String created, Lifecycle lifeCycle, Boolean codeLoaded, Boolean logging) {
+    public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit) {
         this.projectId = projectId;
         this.containerName = containerName;
         this.containerId = containerId;
@@ -63,14 +75,15 @@ public class ContainerStatus {
         this.memoryInfo = memoryInfo;
         this.cpuInfo = cpuInfo;
         this.created = created;
-        this.lifeCycle = lifeCycle;
+        this.commands = commands;
+        this.state = state;
         this.codeLoaded = codeLoaded;
-        this.logging = logging;
+        this.inTransit = inTransit;
     }
 
-    public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String memoryInfo, String cpuInfo, String created) {
+    public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String memoryInfo, String cpuInfo, String created) {
         this.containerName = containerName;
-        this.lifeCycle = lifeCycle;
+        this.commands = commands;
         this.projectId = projectId;
         this.env = env;
         this.type = type;
@@ -79,9 +92,9 @@ public class ContainerStatus {
         this.created = created;
     }
 
-    public ContainerStatus(String containerName, Lifecycle lifeCycle, String projectId, String env, CType type, String created) {
+    public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String created) {
         this.containerName = containerName;
-        this.lifeCycle = lifeCycle;
+        this.commands = commands;
         this.projectId = projectId;
         this.env = env;
         this.created = created;
@@ -89,17 +102,18 @@ public class ContainerStatus {
     }
 
     public static ContainerStatus createDevMode(String projectId, String env) {
-        return new ContainerStatus(projectId, projectId, null, null, null, env, CType.devmode, null, null, null,  Lifecycle.init, false, false);
+        return new ContainerStatus(projectId, projectId, null, null, null, env, ContainerType.devmode, null, null, null, List.of(Command.run), null, false, false);
     }
 
-    public static ContainerStatus createWithId(String name, String env, String containerId, String image, List<Integer> ports, CType type, Lifecycle lifeCycle, String created) {
+    public static ContainerStatus createWithId(String name, String env, String containerId, String image, List<Integer> ports, ContainerType type, List<Command> commands, String status, String created) {
         return new ContainerStatus(name, name, containerId, image, ports, env, type,
-                null, null, created,  lifeCycle, false, false);
+                null, null, created,  commands, status, false, false);
     }
 
     public ContainerStatus() {
     }
 
+
     public String getProjectId() {
         return projectId;
     }
@@ -148,11 +162,11 @@ public class ContainerStatus {
         this.env = env;
     }
 
-    public CType getType() {
+    public ContainerType getType() {
         return type;
     }
 
-    public void setType(CType type) {
+    public void setType(ContainerType type) {
         this.type = type;
     }
 
@@ -180,12 +194,20 @@ public class ContainerStatus {
         this.created = created;
     }
 
-    public Lifecycle getLifeCycle() {
-        return lifeCycle;
+    public List<Command> getCommands() {
+        return commands;
     }
 
-    public void setLifeCycle(Lifecycle lifeCycle) {
-        this.lifeCycle = lifeCycle;
+    public void setCommands(List<Command> commands) {
+        this.commands = commands;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
     }
 
     public Boolean getCodeLoaded() {
@@ -196,11 +218,31 @@ public class ContainerStatus {
         this.codeLoaded = codeLoaded;
     }
 
-    public Boolean getLogging() {
-        return logging;
-    }
-
-    public void setLogging(Boolean logging) {
-        this.logging = logging;
+    public Boolean getInTransit() {
+        return inTransit;
+    }
+
+    public void setInTransit(Boolean inTransit) {
+        this.inTransit = inTransit;
+    }
+
+    @Override
+    public String toString() {
+        return "ContainerStatus{" +
+                "projectId='" + projectId + '\'' +
+                ", containerName='" + containerName + '\'' +
+                ", containerId='" + containerId + '\'' +
+                ", image='" + image + '\'' +
+                ", ports=" + ports +
+                ", env='" + env + '\'' +
+                ", type=" + type +
+                ", memoryInfo='" + memoryInfo + '\'' +
+                ", cpuInfo='" + cpuInfo + '\'' +
+                ", created='" + created + '\'' +
+                ", commands=" + commands +
+                ", state='" + state + '\'' +
+                ", codeLoaded=" + codeLoaded +
+                ", logging=" + inTransit +
+                '}';
     }
 }
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
index c20115b3..29731935 100644
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
+++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/model/KaravanSchema.java
@@ -14,8 +14,8 @@ import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
                 CamelStatus.Name.class,
                 DeploymentStatus.class,
                 ContainerStatus.class,
-                ContainerStatus.CType.class,
-                ContainerStatus.Lifecycle.class,
+                ContainerStatus.ContainerType.class,
+                ContainerStatus.Command.class,
                 ServiceStatus.class
         },
         schemaFileName = "karavan.proto",