diff --git a/build/cd/deploy.go b/build/cd/deploy.go
index a27d1e323184a952258040bd83d4af0c210bd7ad..e82a1231ca8bbe2a4371bcf08c41cd49899431b1 100644
--- a/build/cd/deploy.go
+++ b/build/cd/deploy.go
@@ -3,18 +3,20 @@ package main
 import (
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
 	"context"
+	log "github.com/sirupsen/logrus"
 	appv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	netv1 "k8s.io/api/networking/v1beta1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/tools/clientcmd"
-	"log"
 	"os"
 )
 
 func main() {
+	log.SetFormatter(&log.JSONFormatter{})
 	kubeconfig, err := clientcmd.BuildConfigFromFlags("https://api.ocp.fbi.h-da.de:6443", "")
 	if err != nil {
 		log.Fatal(err)
@@ -33,35 +35,27 @@ func main() {
 	default:
 		tag = os.Getenv("CI_COMMIT_SHA")
 	}
-	env := "gosdn-" + tag
-	service := createService(env)
-	config := createConfigMap(env)
-	deployment := createDeployment(env, tag)
+
 	switch os.Getenv("K8S_OP") {
 	case "create":
-		if err := create(clientset, service, config, deployment, env); err != nil {
-			log.Println(err)
+		if err := create(clientset, tag); err != nil {
+			log.Fatal(err)
 		}
 	case "delete":
-		if err := remove(clientset, env); err != nil {
-			log.Println(err)
-		}
-	case "update":
-		if err := update(clientset, service, env); err != nil {
-			log.Println(err)
-		}
-		if err := update(clientset, config, env); err != nil {
-			log.Println(err)
-		}
-		if err := update(clientset, deployment, env); err != nil {
-			log.Println(err)
+		if err := remove(clientset, tag); err != nil {
+			log.Fatal(err)
 		}
 	default:
 		log.Fatal("invalid option")
 	}
 }
 
-func create(clientset *kubernetes.Clientset, service *corev1.Service, config *corev1.ConfigMap, deployment *appv1.Deployment, env string) error {
+func create(clientset *kubernetes.Clientset, tag string) error {
+	env := "gosdn-" + tag
+	service := createService(env)
+	ingress := createIngress(env)
+	config := createConfigMap(env)
+	deployment := createDeployment(env, tag)
 	opts := metav1.CreateOptions{}
 	ctx := context.Background()
 	_, err := clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
@@ -69,35 +63,70 @@ func create(clientset *kubernetes.Clientset, service *corev1.Service, config *co
 		switch err.(type) {
 		case *errors.StatusError:
 			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				err = update(clientset, service, env)
+				if err := update(clientset, service, env); err != nil {
+					return err
+				}
+			} else {
+				log.Error(err)
 			}
 		default:
-			return err
+			log.Error(err)
 		}
+	} else {
+		log.Printf("service %v created", service.Name)
+	}
+	_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Create(ctx, ingress, opts)
+	if err != nil {
+		switch err.(type) {
+		case *errors.StatusError:
+			if err.(*errors.StatusError).ErrStatus.Code == 409 {
+				if err := update(clientset, ingress, env); err != nil {
+					log.Error(err)
+				}
+			} else {
+				log.Error(err)
+			}
+		default:
+			log.Error(err)
+		}
+	} else {
+		log.Printf("ingress %v created", ingress.Name)
 	}
 	_, err = clientset.CoreV1().ConfigMaps("cocsn").Create(ctx, config, opts)
 	if err != nil {
 		switch err.(type) {
 		case *errors.StatusError:
 			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				err = update(clientset, config, env)
+				if err := update(clientset, config, env); err != nil {
+					log.Error(err)
+				}
+			} else {
+				log.Error(err)
 			}
 		default:
-			return err
+			log.Error(err)
 		}
+	} else {
+		log.Printf("configMap %v created", config.Name)
 	}
 	_, err = clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
 	if err != nil {
 		switch err.(type) {
 		case *errors.StatusError:
 			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				err = update(clientset, deployment, env)
+				if err := update(clientset, deployment, env); err != nil {
+					log.Error(err)
+				}
+			} else {
+				log.Error(err)
 			}
 		default:
-			return err
+			log.Error(err)
 		}
+	} else {
+		log.Printf("deployment %v created", deployment.Name)
 	}
-	return err
+	return nil
 }
 
 func update(clientset *kubernetes.Clientset, resource metav1.Common, env string) error {
@@ -116,6 +145,19 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
 		if err != nil {
 			return err
 		}
+		log.Printf("service %v updated", service.Name)
+	case *netv1.Ingress:
+		ingress := resource.(*netv1.Ingress)
+		i, err := clientset.NetworkingV1beta1().Ingresses("cocsn").Get(ctx, env, getOpts)
+		if err != nil {
+			return err
+		}
+		i.DeepCopyInto(ingress)
+		_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Update(ctx, ingress, opts)
+		if err != nil {
+			return err
+		}
+		log.Printf("ingress %v updated", ingress.Name)
 	case *corev1.ConfigMap:
 		config := resource.(*corev1.ConfigMap)
 		c, err := clientset.CoreV1().ConfigMaps("cocsn").Get(ctx, env+"-config", getOpts)
@@ -127,6 +169,7 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
 		if err != nil {
 			return err
 		}
+		log.Printf("configMap %v updated", config.Name)
 	case *appv1.Deployment:
 		deployment := resource.(*appv1.Deployment)
 		d, err := clientset.AppsV1().Deployments("cocsn").Get(ctx, env, getOpts)
@@ -138,26 +181,40 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
 		if err != nil {
 			return err
 		}
+		log.Printf("deployment %v updated", deployment.Name)
 	default:
 		return &nucleus.ErrInvalidParameters{}
 	}
 	return nil
 }
 
-func remove(clientset *kubernetes.Clientset, env string) error {
+func remove(clientset *kubernetes.Clientset, tag string) error {
+	env := "gosdn-" + tag
 	opts := metav1.DeleteOptions{}
 	ctx := context.Background()
 	err := clientset.CoreV1().Services("cocsn").Delete(ctx, env, opts)
 	if err != nil {
-		log.Println(err)
+		log.Error(err)
+	} else {
+		log.Printf("service %v deleted", env)
 	}
 	err = clientset.CoreV1().ConfigMaps("cocsn").Delete(ctx, env+"-config", opts)
 	if err != nil {
-		log.Println(err)
+		log.Error(err)
+	} else {
+		log.Printf("config %v deleted", env+"-config")
 	}
 	err = clientset.AppsV1().Deployments("cocsn").Delete(ctx, env, opts)
 	if err != nil {
-		log.Println(err)
+		log.Error(err)
+	} else {
+		log.Printf("deployment %v deleted", env)
+	}
+	err = clientset.NetworkingV1beta1().Ingresses("cocsn").Delete(ctx, env, opts)
+	if err != nil {
+		log.Error(err)
+	} else {
+		log.Printf("ingress %v deleted", env)
 	}
 	return err
 }
@@ -294,3 +351,37 @@ func createConfigMap(env string) *corev1.ConfigMap {
 		Data: map[string]string{"gosdn.toml": "#empty"},
 	}
 }
+
+func createIngress(env string) *netv1.Ingress {
+	return &netv1.Ingress{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Ingress",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      env,
+			Namespace: "cocsn",
+		},
+		Spec: netv1.IngressSpec{
+			Rules: []netv1.IngressRule{
+				{
+					Host: env + ".apps.ocp.fbi.h-da.de",
+					IngressRuleValue: netv1.IngressRuleValue{
+						HTTP: &netv1.HTTPIngressRuleValue{
+							Paths: []netv1.HTTPIngressPath{
+								{
+									Path: "/api",
+									Backend: netv1.IngressBackend{
+										ServiceName: env,
+										ServicePort: intstr.IntOrString{IntVal: 8080},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		Status: netv1.IngressStatus{},
+	}
+}
diff --git a/build/ci/.deploy-k8s.yml b/build/ci/.deploy-k8s.yml
index 233052c21dfc216ae7802cb02126ec5a6b9583c7..220e5e93b9183afdbd86f9f76f16e2ed3f5cef46 100644
--- a/build/ci/.deploy-k8s.yml
+++ b/build/ci/.deploy-k8s.yml
@@ -67,4 +67,5 @@ k8s:destroy:
     - ./build/cd/k8s-bot
   needs:
     - job: "deploy:integration-test"
+    - job: "build:k8s-bot"
       artifacts: true
\ No newline at end of file
diff --git a/build/ci/.test.yml b/build/ci/.test.yml
index 2793f91ec4b968ac4da1001d8d082ca7bc4c699c..5970db14de6c93482e613c802c623bbc2a320c0d 100644
--- a/build/ci/.test.yml
+++ b/build/ci/.test.yml
@@ -1,16 +1,16 @@
 integration-test:
   image: golang:1.14
   stage: integration-test
-  needs: [ "apply" ]
+  needs:
+    - job: "apply"
+    - job: "deploy:integration-test"
   variables:
     GOSDN_LOG: "nolog"
+    GOSDN_TEST_API_ENDPOINT: http://gosdn-$CI_COMMIT_SHA.apps.ocp.fbi.h-da.de/api
   rules:
     - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
     - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH
       allow_failure: true
-    - if: $CI_COMMIT_BRANCH == "integration-test"
-      allow_failure: true
   script:
     - sleep 1m
     - go test -race ./test/integration -v -coverprofile=coverage.out
diff --git a/test/integration/cliIntegration_test.go b/test/integration/cliIntegration_test.go
index 26ddc13a2afc4404a0ee62470c205006f5a58961..d2800b0e0c434d2cf8c9bf2fd6972a03ab1d0a71 100644
--- a/test/integration/cliIntegration_test.go
+++ b/test/integration/cliIntegration_test.go
@@ -2,15 +2,9 @@ package integration
 
 import (
 	"code.fbi.h-da.de/cocsn/gosdn/cli"
-	"os"
 	"testing"
 )
 
-func TestMain(m *testing.M) {
-	testSetupIntegration()
-	os.Exit(m.Run())
-}
-
 func TestCapabilities(t *testing.T) {
 	if testing.Short() {
 		t.Skip("skipping integration test")
diff --git a/test/integration/cmdIntegration_test.go b/test/integration/cmdIntegration_test.go
index d3327abb85848d115ab6d7be1b2639748358300a..0b56c9a3d8226d8ce3b7908e61e09fae2b59f161 100644
--- a/test/integration/cmdIntegration_test.go
+++ b/test/integration/cmdIntegration_test.go
@@ -17,12 +17,18 @@ import (
 const unreachable = "203.0.113.10:6030"
 
 var testAddress = "141.100.70.171:6030"
-var testAPIEndpoint = "http://141.100.70.171:8080"
+var testAPIEndpoint = "http://141.100.70.171:8080/api"
 var testUsername = "admin"
 var testPassword = "arista"
 var defaultPath = []string{"/system/config/hostname"}
 var opt *nucleus.GnmiTransportOptions
 
+func TestMain(m *testing.M) {
+	testSetupIntegration()
+	os.Exit(m.Run())
+}
+
+
 func testSetupIntegration() {
 	if os.Getenv("GOSDN_LOG") == "nolog" {
 		log.SetLevel(log.PanicLevel)