diff --git a/.github/workflows/go_cov.yml b/.github/workflows/go_cov.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2c185c39557b56d3f89c32e528a44dd130661997
--- /dev/null
+++ b/.github/workflows/go_cov.yml
@@ -0,0 +1,33 @@
+name: Go
+on:
+  push:
+  pull_request:
+  schedule:
+    # Run every 12 hours, at the 15 minute mark. E.g.
+    # 2020-11-29 00:15:00 UTC, 2020-11-29 12:15:00 UTC, 2020-11-30 00:15:00 UTC
+    - cron:  '15 */12 * * *'
+jobs:
+
+  build:
+    name: Build and Unit Test
+    runs-on: ubuntu-latest
+    steps:
+
+      - name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ^1.13
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Build
+        run: go build -v ./...
+
+      - name: Test
+        run: go test -v -race -covermode=atomic -coverprofile=coverage.out ./...
+
+      - name: Upload Coverage
+        uses: shogo82148/actions-goveralls@v1
+        with:
+          path-to-profile: coverage.out
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
new file mode 100644
index 0000000000000000000000000000000000000000..12323d137b21b0851ab0ea7a9dc02b952526cff5
--- /dev/null
+++ b/.github/workflows/golangci-lint.yml
@@ -0,0 +1,25 @@
+name: golangci-lint
+on:
+  push:
+    tags:
+      - v*
+    branches:
+      - master
+  pull_request:
+  schedule:
+    # Run every 12 hours, at the 15 minute mark. E.g.
+    # 2020-11-29 00:15:00 UTC, 2020-11-29 12:15:00 UTC, 2020-11-30 00:15:00 UTC
+    - cron:  '15 */12 * * *'
+jobs:
+  golangci:
+    name: Lint Sourcecode
+    runs-on: ubuntu-latest
+    steps:
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Run golangci-lint
+        uses: golangci/golangci-lint-action@v2
+        with:
+          version: v1.35
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bf0c4bfe759faab9f1816764e93d75b0a82fb740..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: go
-go:
-  - 1.13
-env:
-  - "PATH=/home/travis/gopath/bin:$PATH"
-before_install:
-  - go get golang.org/x/lint/golint
-  - go get github.com/mattn/goveralls
-script:
-  - go vet 
-  - golint -set_exit_status
-  - go test -race -v
-  - $HOME/gopath/bin/goveralls -ignore main.go -v -service=travis-ci
diff --git a/api.go b/api.go
index edff33787c4e24480784ac0860a649c198736ca2..864256c35d8ab757d5bdfcc563f96d704f472917 100644
--- a/api.go
+++ b/api.go
@@ -25,14 +25,14 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
 	var err error
 	aTXT := ACMETxt{}
 	bdata, _ := ioutil.ReadAll(r.Body)
-	if bdata != nil && len(bdata) > 0 {
+	if len(bdata) > 0 {
 		err = json.Unmarshal(bdata, &aTXT)
 		if err != nil {
 			regStatus = http.StatusBadRequest
 			reg = jsonError("malformed_json_payload")
 			w.Header().Set("Content-Type", "application/json")
 			w.WriteHeader(regStatus)
-			w.Write(reg)
+			_, _ = w.Write(reg)
 			return
 		}
 	}
@@ -44,7 +44,7 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
 		reg = jsonError("invalid_allowfrom_cidr")
 		w.Header().Set("Content-Type", "application/json")
 		w.WriteHeader(regStatus)
-		w.Write(reg)
+		_, _ = w.Write(reg)
 		return
 	}
 
@@ -68,7 +68,7 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
 	}
 	w.Header().Set("Content-Type", "application/json")
 	w.WriteHeader(regStatus)
-	w.Write(reg)
+	_, _ = w.Write(reg)
 }
 
 func webUpdatePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@@ -104,7 +104,7 @@ func webUpdatePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
 	}
 	w.Header().Set("Content-Type", "application/json")
 	w.WriteHeader(updStatus)
-	w.Write(upd)
+	_, _ = w.Write(upd)
 }
 
 // Endpoint used to check the readiness and/or liveness (health) of the server.
diff --git a/api_test.go b/api_test.go
index b7cfd0ec26d1efae981c26caaa4e5e7a2a8c6684..119ab9819abed9f34dae7869d76e379efdbd8d27 100644
--- a/api_test.go
+++ b/api_test.go
@@ -325,10 +325,6 @@ func TestApiUpdateWithCredentialsMockDB(t *testing.T) {
 func TestApiManyUpdateWithCredentials(t *testing.T) {
 	validTxtData := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 
-	updateJSON := map[string]interface{}{
-		"subdomain": "",
-		"txt":       ""}
-
 	router := setupRouter(true, false)
 	server := httptest.NewServer(router)
 	defer server.Close()
@@ -370,7 +366,7 @@ func TestApiManyUpdateWithCredentials(t *testing.T) {
 		{newUserWithValidCIDR.Username.String(), newUserWithValidCIDR.Password, newUserWithValidCIDR.Subdomain, validTxtData, 200},
 		{newUser.Username.String(), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", newUser.Subdomain, validTxtData, 401},
 	} {
-		updateJSON = map[string]interface{}{
+		updateJSON := map[string]interface{}{
 			"subdomain": test.subdomain,
 			"txt":       test.txt}
 		e.POST("/update").
@@ -385,10 +381,6 @@ func TestApiManyUpdateWithCredentials(t *testing.T) {
 
 func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
 
-	updateJSON := map[string]interface{}{
-		"subdomain": "",
-		"txt":       ""}
-
 	router := setupRouter(false, false)
 	server := httptest.NewServer(router)
 	defer server.Close()
@@ -425,7 +417,7 @@ func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
 		{newUserWithIP6CIDR, "2002:c0a7:0ff::0", 401},
 		{newUserWithIP6CIDR, "2002:c0a8:d3ad:b33f:c0ff:33b4:dc0d:3b4d", 200},
 	} {
-		updateJSON = map[string]interface{}{
+		updateJSON := map[string]interface{}{
 			"subdomain": test.user.Subdomain,
 			"txt":       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
 		e.POST("/update").
diff --git a/auth.go b/auth.go
index 162f9270f9828044e01238be366486bbe0bd5a10..c09f8b42cf1993a78c3a68fbbfa6b896f8a003ff 100644
--- a/auth.go
+++ b/auth.go
@@ -50,7 +50,7 @@ func Auth(update httprouter.Handle) httprouter.Handle {
 		} else {
 			w.Header().Set("Content-Type", "application/json")
 			w.WriteHeader(http.StatusUnauthorized)
-			w.Write(jsonError("forbidden"))
+			_, _ = w.Write(jsonError("forbidden"))
 		}
 	}
 }
diff --git a/db.go b/db.go
index 358bde79011df4ccdf52947c5e1f44e39f763a83..35347281d638f171a2adfb3498e0a45342ecc887 100644
--- a/db.go
+++ b/db.go
@@ -50,7 +50,7 @@ var txtTablePG = `
 
 // getSQLiteStmt replaces all PostgreSQL prepared statement placeholders (eg. $1, $2) with SQLite variant "?"
 func getSQLiteStmt(s string) string {
-	re, _ := regexp.Compile("\\$[0-9]")
+	re, _ := regexp.Compile(`\$[0-9]`)
 	return re.ReplaceAllString(s, "?")
 }
 
@@ -68,12 +68,12 @@ func (d *acmedb) Init(engine string, connection string) error {
 	if versionString == "" {
 		versionString = "0"
 	}
-	_, err = d.DB.Exec(acmeTable)
-	_, err = d.DB.Exec(userTable)
+	_, _ = d.DB.Exec(acmeTable)
+	_, _ = d.DB.Exec(userTable)
 	if Config.Database.Engine == "sqlite3" {
-		_, err = d.DB.Exec(txtTable)
+		_, _ = d.DB.Exec(txtTable)
 	} else {
-		_, err = d.DB.Exec(txtTablePG)
+		_, _ = d.DB.Exec(txtTablePG)
 	}
 	// If everything is fine, handle db upgrade tasks
 	if err == nil {
@@ -136,10 +136,10 @@ func (d *acmedb) handleDBUpgradeTo1() error {
 	// Rollback if errored, commit if not
 	defer func() {
 		if err != nil {
-			tx.Rollback()
+			_ = tx.Rollback()
 			return
 		}
-		tx.Commit()
+		_ = tx.Commit()
 	}()
 	_, _ = tx.Exec("DELETE FROM txt")
 	for _, subdomain := range subdomains {
@@ -165,8 +165,8 @@ func (d *acmedb) handleDBUpgradeTo1() error {
 func (d *acmedb) NewTXTValuesInTransaction(tx *sql.Tx, subdomain string) error {
 	var err error
 	instr := fmt.Sprintf("INSERT INTO txt (Subdomain, LastUpdate) values('%s', 0)", subdomain)
-	_, err = tx.Exec(instr)
-	_, err = tx.Exec(instr)
+	_, _ = tx.Exec(instr)
+	_, _ = tx.Exec(instr)
 	return err
 }
 
@@ -178,10 +178,10 @@ func (d *acmedb) Register(afrom cidrslice) (ACMETxt, error) {
 	// Rollback if errored, commit if not
 	defer func() {
 		if err != nil {
-			tx.Rollback()
+			_ = tx.Rollback()
 			return
 		}
-		tx.Commit()
+		_ = tx.Commit()
 	}()
 	a := newACMETxt()
 	a.AllowFrom = cidrslice(afrom.ValidEntries())
diff --git a/dns.go b/dns.go
index 1a7e0e0d8582e4e5eb16d2df85eb65dcfdaa2952..a01fb9c5d9e82ad8724e9121a31a4e1044958295 100644
--- a/dns.go
+++ b/dns.go
@@ -108,7 +108,7 @@ func (d *DNSServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
 			d.readQuery(m)
 		}
 	}
-	w.WriteMsg(m)
+	_ = w.WriteMsg(m)
 }
 
 func (d *DNSServer) readQuery(m *dns.Msg) {
@@ -119,9 +119,7 @@ func (d *DNSServer) readQuery(m *dns.Msg) {
 				authoritative = auth
 			}
 			m.MsgHdr.Rcode = rc
-			for _, r := range rr {
-				m.Answer = append(m.Answer, r)
-			}
+			m.Answer = append(m.Answer, rr...)
 		}
 	}
 	m.MsgHdr.Authoritative = authoritative
@@ -208,9 +206,7 @@ func (d *DNSServer) answer(q dns.Question) ([]dns.RR, int, bool, error) {
 			txtRRs, err = d.answerTXT(q)
 		}
 		if err == nil {
-			for _, txtRR := range txtRRs {
-				r = append(r, txtRR)
-			}
+			r = append(r, txtRRs...)
 		}
 	}
 	if len(r) > 0 {
diff --git a/dns_test.go b/dns_test.go
index aca82bbc706c291f80dc365deb1316ab7bc50534..ba42a5d5c30dd6c92af6e6a9f88bcfe94271fd16 100644
--- a/dns_test.go
+++ b/dns_test.go
@@ -11,9 +11,6 @@ import (
 	"github.com/miekg/dns"
 )
 
-var resolv resolver
-var server *dns.Server
-
 type resolver struct {
 	server string
 }
diff --git a/main.go b/main.go
index e93f465d5a6abb11859917a68c9d49a8156381b8..6ef56f2f473615f12da2488867865cbeb9468598 100644
--- a/main.go
+++ b/main.go
@@ -92,13 +92,12 @@ func main() {
 	go startHTTPAPI(errChan, Config, dnsservers)
 
 	// block waiting for error
-	select {
-	case err = <-errChan:
+	for {
+		err = <-errChan
 		if err != nil {
 			log.Fatal(err)
 		}
 	}
-	log.Debugf("Shutting down...")
 }
 
 func startHTTPAPI(errChan chan error, config DNSConfig, dnsservers []*DNSServer) {
diff --git a/main_test.go b/main_test.go
index a8642a0136ece15962a75d024a8fc123dec1eb7f..66f4d6e4aa306f2ec2b8056f0cb481e35a58e36a 100644
--- a/main_test.go
+++ b/main_test.go
@@ -56,7 +56,7 @@ func TestMain(m *testing.M) {
 	go dnsserver.Start(make(chan error, 1))
 	wg.Wait()
 	exitval := m.Run()
-	dnsserver.Server.Shutdown()
+	_ = dnsserver.Server.Shutdown()
 	DB.Close()
 	os.Exit(exitval)
 }
diff --git a/types.go b/types.go
index 235b72a8e4fa525ea9d395e580543cd839c93606..f99b84d1dd379b72d69cf34f95226deadd3dd95e 100644
--- a/types.go
+++ b/types.go
@@ -21,9 +21,6 @@ type DNSConfig struct {
 	Logconfig logconfig
 }
 
-// Auth middleware
-type authMiddleware struct{}
-
 // Config file general section
 type general struct {
 	Listen        string
diff --git a/util.go b/util.go
index c835a0d6aa01beeef67dfb734276dc6d39d9bf7f..163683dbb1d8df6a86102c295a01598cf35b148a 100644
--- a/util.go
+++ b/util.go
@@ -59,13 +59,13 @@ func prepareConfig(conf DNSConfig) (DNSConfig, error) {
 
 func sanitizeString(s string) string {
 	// URL safe base64 alphabet without padding as defined in ACME
-	re, _ := regexp.Compile("[^A-Za-z\\-\\_0-9]+")
+	re, _ := regexp.Compile(`[^A-Za-z\-\_0-9]+`)
 	return re.ReplaceAllString(s, "")
 }
 
 func sanitizeIPv6addr(s string) string {
 	// Remove brackets from IPv6 addresses, net.ParseCIDR needs this
-	re, _ := regexp.Compile("[\\[\\]]+")
+	re, _ := regexp.Compile(`[\[\]]+`)
 	return re.ReplaceAllString(s, "")
 }
 
diff --git a/util_test.go b/util_test.go
index 4aecf00f80d9997657db350383bddb076501ff71..1eb4b677be01032e7340e5d64101698f52c9ca24 100644
--- a/util_test.go
+++ b/util_test.go
@@ -104,11 +104,11 @@ func TestFileCheckPermissionDenied(t *testing.T) {
 		t.Error("Could not create temporary file")
 	}
 	defer os.Remove(tmpfile.Name())
-	syscall.Chmod(tmpfile.Name(), 0000)
+	_ = syscall.Chmod(tmpfile.Name(), 0000)
 	if fileIsAccessible(tmpfile.Name()) {
 		t.Errorf("File should not be accessible")
 	}
-	syscall.Chmod(tmpfile.Name(), 0644)
+	_ = syscall.Chmod(tmpfile.Name(), 0644)
 }
 
 func TestFileCheckNotExists(t *testing.T) {
diff --git a/validation.go b/validation.go
index df39e8520ef0bc09a0237a072ce6dbea33294580..2cec5a98f5dedd3b5b6dd705fa09605c29adf8c3 100644
--- a/validation.go
+++ b/validation.go
@@ -2,11 +2,10 @@ package main
 
 import (
 	"unicode/utf8"
+	"regexp"
 
 	"github.com/google/uuid"
 	"golang.org/x/crypto/bcrypt"
-
-	"regexp"
 )
 
 func getValidUsername(u string) (uuid.UUID, error) {
@@ -32,7 +31,6 @@ func validSubdomain(s string) bool {
 	return RegExp.MatchString(s)
 }
 
-
 func validTXT(s string) bool {
 	sn := sanitizeString(s)
 	if utf8.RuneCountInString(s) == 43 && utf8.RuneCountInString(sn) == 43 {