diff --git a/Makefile b/Makefile
index 878a74716a85947c3364eb70ff368d5cda1aedd9..bff439f71064723592b7bf74fcff62417ec395cf 100644
--- a/Makefile
+++ b/Makefile
@@ -172,6 +172,9 @@ docker-push-latest:
 	docker push hunterlong/statup:latest
 	docker push hunterlong/statup:latest-v$(VERSION)
 
+docker-run-mssql:
+	docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=PaSsW0rD123' -p 1433:1433 -d microsoft/mssql-server-linux
+
 # create Postgres, and MySQL instance using Docker (used for testing)
 databases:
 	docker run --name statup_postgres -p 5432:5432 -e POSTGRES_PASSWORD=password123 -e POSTGRES_USER=root -e POSTGRES_DB=root -d postgres
diff --git a/cmd/main_test.go b/cmd/main_test.go
index da043787d8ad7673cf921dccf0323ce73572b6df..21ba79dabe945725f630e1b11b68fa121f3c2b21 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -52,7 +52,13 @@ func Clean() {
 	utils.DeleteDirectory(dir + "/logs")
 }
 
-func RunInit(t *testing.T) {
+func RunInit(db string, t *testing.T) {
+	if db == "mssql" {
+		os.Setenv("DB_DATABASE", "tempdb")
+		os.Setenv("DB_PASS", "PaSsW0rD123")
+		os.Setenv("DB_PORT", "1433")
+		os.Setenv("DB_USER", "sa")
+	}
 	source.Assets()
 	Clean()
 	route = handlers.Router()
@@ -69,7 +75,7 @@ func TestRunAll(t *testing.T) {
 
 	for _, dbt := range databases {
 		t.Run(dbt+" init", func(t *testing.T) {
-			RunInit(t)
+			RunInit(dbt, t)
 		})
 		t.Run(dbt+" Save Config", func(t *testing.T) {
 			RunSaveConfig(t, dbt)
@@ -206,6 +212,11 @@ func TestRunAll(t *testing.T) {
 		t.Run(dbt+" Cleanup", func(t *testing.T) {
 			core.Configs.Close()
 			core.DbSession = nil
+			if dbt == "mssql" {
+				os.Setenv("DB_DATABASE", "root")
+				os.Setenv("DB_PASS", "password123")
+				os.Setenv("DB_PORT", "1433")
+			}
 			//Clean()
 		})
 
@@ -220,6 +231,8 @@ func RunSaveConfig(t *testing.T, db string) {
 	port := 5432
 	if db == "mysql" {
 		port = 3306
+	} else if db == "mssql" {
+		port = 1433
 	}
 	core.Configs = &core.DbConfig{DbConfig: &types.DbConfig{
 		DbConn:      db,
diff --git a/core/services.go b/core/services.go
index 05480776da1b01cedd3230c8cf128aa6bbdd3e80..653dcace8a37b99883e983921f6f23f223fca561 100644
--- a/core/services.go
+++ b/core/services.go
@@ -163,12 +163,7 @@ func (s *Service) SmallText() string {
 }
 
 func (s *Service) DowntimeText() string {
-	lastFailure := s.lastFailure()
-	if lastFailure == nil {
-		return ""
-	}
-	got, _ := timeago.TimeAgoWithTime(time.Now().UTC().Add(s.Downtime()), time.Now().UTC())
-	return fmt.Sprintf("Reported offline %v, %v", got, lastFailure.ParseError())
+	return fmt.Sprintf("%v has been offline for %v", s.Name, utils.DurationReadable(s.Downtime()))
 }
 
 // GroupDataBy returns a SQL query as a string to group a column by a time
@@ -188,13 +183,13 @@ func GroupDataBy(column string, id int64, start, end time.Time, increment string
 // Downtime returns the amount of time of a offline service
 func (s *Service) Downtime() time.Duration {
 	hits, _ := s.Hits()
-	if len(hits) == 0 {
-		return time.Duration(0)
-	}
 	fails := s.LimitedFailures()
 	if len(fails) == 0 {
 		return time.Duration(0)
 	}
+	if len(hits) == 0 {
+		return time.Now().UTC().Sub(fails[len(fails)-1].CreatedAt.UTC())
+	}
 	since := fails[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC())
 	return since
 }
diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3683a79e162d076eba4ac4c93c8f012a966a6071
--- /dev/null
+++ b/dev/docker-compose.yml
@@ -0,0 +1,32 @@
+postgres:
+  container_name: postgres
+  image: postgres
+  restart: always
+  ports:
+    - 5432:5432
+  environment:
+    POSTGRES_PASSWORD: password123
+    POSTGRES_USER: root
+    POSTGRES_DB: root
+
+mysql:
+  container_name: mysql
+  image: mysql:5.6
+  restart: always
+  ports:
+    - 3306:3306
+  environment:
+    MYSQL_ROOT_PASSWORD: password123
+    MYSQL_DATABASE: root
+    MYSQL_USER: root
+    MYSQL_PASSWORD: password123
+
+mssql:
+  container_name: mssql
+  image: microsoft/mssql-server-linux
+  restart: always
+  ports:
+    - 1433:1433
+  environment:
+    SA_PASSWORD: PaSsW0rD123
+    ACCEPT_EULA: "Y"
diff --git a/handlers/api.go b/handlers/api.go
index f01f125794ca063aae5f74fc484a287121af6375..9502f4bbbd634bab2ecced26430ea7d4cc6f3506 100644
--- a/handlers/api.go
+++ b/handlers/api.go
@@ -23,7 +23,6 @@ import (
 	"github.com/hunterlong/statup/utils"
 	"net/http"
 	"os"
-	"time"
 )
 
 type ApiResponse struct {
@@ -94,38 +93,6 @@ func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(service)
 }
 
-func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
-	if !isAPIAuthorized(r) {
-		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
-		return
-	}
-	vars := mux.Vars(r)
-	fields := parseGet(r)
-
-	startField := utils.StringInt(fields.Get("start"))
-	endField := utils.StringInt(fields.Get("end"))
-	var start time.Time
-	var end time.Time
-	if startField == 0 {
-		start = time.Now().Add(-24 * time.Hour).UTC()
-	} else {
-		start = time.Unix(startField, 0)
-	}
-	if endField == 0 {
-		end = time.Now().UTC()
-	} else {
-		end = time.Unix(endField, 0)
-	}
-	service := core.SelectService(utils.StringInt(vars["id"]))
-	if service == nil {
-		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
-		return
-	}
-
-	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(core.GraphDataRaw(service, start, end).Array)
-}
-
 func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
 	if !isAPIAuthorized(r) {
 		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
diff --git a/handlers/api_handlers_test.go b/handlers/api_handlers_test.go
index f600041852814f95162e820a7b66915be2be477d..f7e7d39867b578be056f5ef510cf31d0f7f864d6 100644
--- a/handlers/api_handlers_test.go
+++ b/handlers/api_handlers_test.go
@@ -121,16 +121,6 @@ func TestApiServiceHandler(t *testing.T) {
 	assert.Equal(t, "https://google.com", obj.Domain)
 }
 
-func TestApiServiceDataHandler(t *testing.T) {
-	rr, err := httpRequestAPI(t, "GET", "/api/services/1/data", nil)
-	assert.Nil(t, err)
-	body := rr.Body.String()
-	var obj []*core.DateScan
-	formatJSON(body, &obj)
-	assert.Equal(t, 200, rr.Code)
-	assert.Equal(t, 60, len(obj))
-}
-
 func TestApiCreateServiceHandler(t *testing.T) {
 	rr, err := httpRequestAPI(t, "POST", "/api/services", strings.NewReader(NEW_HTTP_SERVICE))
 	assert.Nil(t, err)
diff --git a/handlers/routes.go b/handlers/routes.go
index c1449363254994b68221879bb9c96bfaca0db84a..fb81ac85162429cb481ed229b3ebc397352cc7bb 100644
--- a/handlers/routes.go
+++ b/handlers/routes.go
@@ -84,7 +84,6 @@ func Router() *mux.Router {
 	r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET")
 	r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST")
 	r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET")
-	r.Handle("/api/services/{id}/data", http.HandlerFunc(apiServiceDataHandler)).Methods("GET")
 	r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
 	r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
 
diff --git a/utils/utils.go b/utils/utils.go
index 7374f31730f188d9b1a88a211fe75e8a3ae27ec9..4824a087ddeadb3aa95fddf017e51fe656295ad5 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -190,3 +190,14 @@ func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
 		}
 	}
 }
+
+func DurationReadable(d time.Duration) string {
+	if d.Hours() >= 1 {
+		return fmt.Sprintf("%0.0f hours and %0.0f minutes", d.Hours(), d.Minutes())
+	} else if d.Minutes() >= 1 {
+		return fmt.Sprintf("%0.0f minutes", d.Minutes())
+	} else if d.Seconds() >= 1 {
+		return fmt.Sprintf("%0.0f seconds", d.Seconds())
+	}
+	return d.String()
+}