Echo server
diff --git a/apps/echo/Dockerfile b/apps/echo/Dockerfile
new file mode 100644
index 0000000..cff1435
--- /dev/null
+++ b/apps/echo/Dockerfile
@@ -0,0 +1,4 @@
+FROM alpine:latest
+
+COPY echo /usr/bin
+RUN chmod +x /usr/bin/echo
diff --git a/apps/echo/Makefile b/apps/echo/Makefile
new file mode 100644
index 0000000..3f53cbd
--- /dev/null
+++ b/apps/echo/Makefile
@@ -0,0 +1,18 @@
+build:
+	go build -o echo *.go
+
+clean:
+	rm -f echo
+
+image: clean build
+	docker build --tag=giolekva/echo .
+
+push: image
+	docker push giolekva/echo:latest
+
+
+push_arm64: export GOOS=linux
+push_arm64: export GOARCH=arm64
+push_arm64: export CGO_ENABLED=0
+push_arm64: export GO111MODULE=on
+push_arm64: push
diff --git a/apps/echo/go.mod b/apps/echo/go.mod
new file mode 100644
index 0000000..08142e0
--- /dev/null
+++ b/apps/echo/go.mod
@@ -0,0 +1,3 @@
+module github.com/giolekva/pcloud/apps/echo
+
+go 1.16
diff --git a/apps/echo/install.yaml b/apps/echo/install.yaml
new file mode 100644
index 0000000..b46a5ce
--- /dev/null
+++ b/apps/echo/install.yaml
@@ -0,0 +1,61 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: app-echo
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: echo
+  namespace: app-echo
+spec:
+  type: NodePort
+  selector:
+    app: echo
+  ports:
+  - port: 8080
+    targetPort: 1234
+    protocol: TCP
+    nodePort: 32050
+---
+# apiVersion: traefik.containo.us/v1alpha1
+# kind: IngressRoute
+# metadata:
+#   name: ingress
+#   namespace: app-echo
+# spec:
+#   entryPoints:
+#     - web
+#   routes:
+#   - kind: Rule
+#     match: PathPrefix(`/echo`)
+#     services:
+#     - kind: Service
+#       name: echo
+#       namespace: app-echo
+#       passHostHeader: true
+#       port: 80
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: echo
+  namespace: app-echo
+spec:
+  selector:
+    matchLabels:
+      app: echo
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: echo
+    spec:
+      containers:
+      - name: echo
+        image: giolekva/echo:latest
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 1234
+        command: ["echo", "--port=1234"]
diff --git a/apps/echo/main.go b/apps/echo/main.go
new file mode 100644
index 0000000..0e96d2c
--- /dev/null
+++ b/apps/echo/main.go
@@ -0,0 +1,58 @@
+// Copyright 2018 Venil Noronha. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"os"
+)
+
+var port = flag.Int("port", 3000, "Port to listen on")
+
+// main serves as the program entry point
+func main() {
+	flag.Parse()
+	// create a tcp listener on the given port
+	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
+	if err != nil {
+		fmt.Println("failed to create listener, err:", err)
+		os.Exit(1)
+	}
+	fmt.Printf("listening on %s\n", listener.Addr())
+
+	// listen for new connections
+	for {
+		conn, err := listener.Accept()
+		if err != nil {
+			fmt.Println("failed to accept connection, err:", err)
+			continue
+		}
+
+		// pass an accepted connection to a handler goroutine
+		go handleConnection(conn)
+	}
+}
+
+// handleConnection handles the lifetime of a connection
+func handleConnection(conn net.Conn) {
+	defer conn.Close()
+	reader := bufio.NewReader(conn)
+	for {
+		// read client request data
+		bytes, err := reader.ReadBytes(byte('\n'))
+		if err != nil {
+			if err != io.EOF {
+				fmt.Println("failed to read data, err:", err)
+			}
+			return
+		}
+		fmt.Printf("request: %s", bytes)
+		conn.Write(bytes)
+	}
+}