diff --git a/api/controllers/container.go b/api/controllers/container.go
new file mode 100644
index 0000000..d8e634a
--- /dev/null
+++ b/api/controllers/container.go
@@ -0,0 +1,74 @@
+package controllers
+
+import (
+	"bytes"
+	"log"
+	"magmise/models"
+	"net/http"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/gorilla/websocket"
+)
+
+var upgrader = websocket.Upgrader{}
+
+type Container struct {
+}
+
+func (c Container) Start(w http.ResponseWriter, r *http.Request) {
+	name := chi.URLParam(r, "name")
+	if name == "" {
+		http.Error(w, "Missing container name", http.StatusNotFound)
+		return
+	}
+	id, err := models.GetId(name)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusNotFound)
+		return
+	}
+	models.ContainerStart(id)
+}
+
+func (c Container) Stop(w http.ResponseWriter, r *http.Request) {
+	name := chi.URLParam(r, "name")
+	if name == "" {
+		http.Error(w, "Missing container name", http.StatusNotFound)
+		return
+	}
+	id, err := models.GetId(name)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusNotFound)
+		return
+	}
+	models.ContainerStop(id)
+}
+
+func (c Container) Logs(w http.ResponseWriter, r *http.Request) {
+	name := chi.URLParam(r, "name")
+	if name == "" {
+		http.Error(w, "Missing container name", http.StatusNotFound)
+		return
+	}
+	id, err := models.GetId(name)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusNotFound)
+		return
+	}
+	ws, err := upgrader.Upgrade(w, r, nil)
+	if err != nil {
+		log.Print("upgrade:", err)
+		return
+	}
+	defer ws.Close()
+	logs, err := models.ContainerLogs(id, true)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusNotFound)
+		return
+	}
+	buf := new(bytes.Buffer)
+	for {
+
+		buf.ReadFrom(logs)
+		ws.WriteMessage(0, buf.Bytes())
+	}
+}