diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a4fa6693b95a16e2f76990b9fd497d57f9cfbb9f..a190ea429d1f6e042da752e21b7452b1665fd87f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: golang:1.10
+image: golang:1.11
 
 verify:
   script:
@@ -17,10 +17,10 @@ verify:
   - make test
 
 test using go 1.10:
+  image: golang:1.10
   <<: *test_definition
 
 test using go 1.11:
-  image: golang:1.11
   <<: *test_definition
 
 test:release:
diff --git a/Makefile b/Makefile
index 9065ccb6ec32722d86ac8de725fd0046fa298ce0..a97f239feeda5b38a340683b6c7f4f1c61823d27 100644
--- a/Makefile
+++ b/Makefile
@@ -106,7 +106,7 @@ testdata/data/group/test.git:
 	git clone --quiet --bare https://gitlab.com/gitlab-org/gitlab-test.git $@
 
 .PHONY: verify
-verify: lint vet detect-context check-formatting megacheck
+verify: lint vet detect-context check-formatting staticcheck
 
 .PHONY: lint
 lint: $(TARGET_SETUP) govendor-sync
@@ -132,11 +132,11 @@ check-formatting: $(TARGET_SETUP) install-goimports
 # Megacheck will tailor some responses given a minimum Go version, so pass that through the CLI
 # Additionally, megacheck will not return failure exit codes unless explicitely told to via the
 # `-simple.exit-non-zero` `-unused.exit-non-zero` and `-staticcheck.exit-non-zero` flags
-.PHONY: megacheck
-megacheck: $(TARGET_SETUP) govendor-sync
+.PHONY: staticcheck
+staticcheck: $(TARGET_SETUP) govendor-sync
 	$(call message,Verify: $@)
-	@command -v megacheck || go get -v honnef.co/go/tools/cmd/megacheck
-	@megacheck -go $(MINIMUM_SUPPORTED_GO_VERSION) -simple.exit-non-zero -unused.exit-non-zero -staticcheck.exit-non-zero $(LOCAL_PACKAGES)
+	@command -v staticcheck || go get -v honnef.co/go/tools/cmd/staticcheck
+	@staticcheck -go $(MINIMUM_SUPPORTED_GO_VERSION) $(LOCAL_PACKAGES)
 
 # Some vendor components, used for testing are GPL, so we don't distribute them
 # and need to go a sync before using them
diff --git a/authorization_test.go b/authorization_test.go
index f68a3b942651d2742b54535909315f79a490ff47..e46cf516da8fa8a74297653001d8737c3206136a 100644
--- a/authorization_test.go
+++ b/authorization_test.go
@@ -83,7 +83,7 @@ func TestPreAuthorizeContentTypeFailure(t *testing.T) {
 
 func TestPreAuthorizeRedirect(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		http.Redirect(w, r, "/", 301)
+		http.Redirect(w, r, "/", http.StatusMovedPermanently)
 	}))
 	defer ts.Close()
 
diff --git a/internal/api/terminal_settings.go b/internal/api/terminal_settings.go
index 5549234542fe9502f132da6f6e9f6e4fb9bc4130..26efda58364112ad3b80baeca5d11d008ffb10ff 100644
--- a/internal/api/terminal_settings.go
+++ b/internal/api/terminal_settings.go
@@ -63,20 +63,20 @@ func (t *TerminalSettings) Dial() (*websocket.Conn, *http.Response, error) {
 
 func (t *TerminalSettings) Validate() error {
 	if t == nil {
-		return fmt.Errorf("Terminal details not specified")
+		return fmt.Errorf("terminal details not specified")
 	}
 
 	if len(t.Subprotocols) == 0 {
-		return fmt.Errorf("No subprotocol specified")
+		return fmt.Errorf("no subprotocol specified")
 	}
 
 	parsedURL, err := t.URL()
 	if err != nil {
-		return fmt.Errorf("Invalid URL")
+		return fmt.Errorf("invalid URL")
 	}
 
 	if parsedURL.Scheme != "ws" && parsedURL.Scheme != "wss" {
-		return fmt.Errorf("Invalid websocket scheme: %q", parsedURL.Scheme)
+		return fmt.Errorf("invalid websocket scheme: %q", parsedURL.Scheme)
 	}
 
 	return nil
diff --git a/internal/artifacts/artifacts_upload.go b/internal/artifacts/artifacts_upload.go
index d8365ee46ef6b1f67bb72896d1a97fd095bfe1ad..0cf1d07accf333ad802ba2a8070feec185881f56 100644
--- a/internal/artifacts/artifacts_upload.go
+++ b/internal/artifacts/artifacts_upload.go
@@ -77,10 +77,10 @@ func (a *artifactsUploadProcessor) ProcessFile(ctx context.Context, formName str
 	//  ProcessFile for artifacts requires file form-data field name to eq `file`
 
 	if formName != "file" {
-		return fmt.Errorf("Invalid form field: %q", formName)
+		return fmt.Errorf("invalid form field: %q", formName)
 	}
 	if a.stored {
-		return fmt.Errorf("Artifacts request contains more than one file")
+		return fmt.Errorf("artifacts request contains more than one file")
 	}
 	a.stored = true
 
diff --git a/internal/filestore/body_uploader_test.go b/internal/filestore/body_uploader_test.go
index 7268a2dd084d12786fcda8ac0153e21b2b239a1e..65da9c8aa80f9de38d27c84cf2fbc02fde56541f 100644
--- a/internal/filestore/body_uploader_test.go
+++ b/internal/filestore/body_uploader_test.go
@@ -150,7 +150,7 @@ func (a *alwaysLocalPreparer) Prepare(_ *api.Response) (*filestore.SaveFileOpts,
 
 type alwaysFailsVerifier struct{}
 
-func (_ alwaysFailsVerifier) Verify(handler *filestore.FileHandler) error {
+func (alwaysFailsVerifier) Verify(handler *filestore.FileHandler) error {
 	return fmt.Errorf("Verification failed")
 }
 
diff --git a/internal/filestore/file_handler.go b/internal/filestore/file_handler.go
index 5fae3fe9b8d85c91de0d4e8b5fb70b6f3659cd4c..437947c863727ddec756d9a72b835cd0837b758c 100644
--- a/internal/filestore/file_handler.go
+++ b/internal/filestore/file_handler.go
@@ -15,7 +15,7 @@ import (
 type SizeError error
 
 // ErrEntityTooLarge means that the uploaded content is bigger then maximum allowed size
-var ErrEntityTooLarge = errors.New("Entity is too large")
+var ErrEntityTooLarge = errors.New("entity is too large")
 
 // FileHandler represent a file that has been processed for upload
 // it may be either uploaded to an ObjectStore and/or saved on local path.
@@ -125,7 +125,7 @@ func SaveFileFromReader(ctx context.Context, reader io.Reader, size int64, opts
 	}
 
 	if len(writers) == 1 {
-		return nil, errors.New("Missing upload destination")
+		return nil, errors.New("missing upload destination")
 	}
 
 	multiWriter := io.MultiWriter(writers...)
@@ -135,7 +135,7 @@ func SaveFileFromReader(ctx context.Context, reader io.Reader, size int64, opts
 	}
 
 	if size != -1 && size != fh.Size {
-		return nil, SizeError(fmt.Errorf("Expected %d bytes but got only %d", size, fh.Size))
+		return nil, SizeError(fmt.Errorf("expected %d bytes but got only %d", size, fh.Size))
 	}
 
 	fh.hashes = hashes.finish()
diff --git a/internal/objectstore/multipart.go b/internal/objectstore/multipart.go
index 03ac965a6196c98e21fbbb5082b17e8dc546b98d..195cdb95567d5e392febe1f49d82038ea1857500 100644
--- a/internal/objectstore/multipart.go
+++ b/internal/objectstore/multipart.go
@@ -18,7 +18,7 @@ import (
 )
 
 // ErrNotEnoughParts will be used when writing more than size * len(partURLs)
-var ErrNotEnoughParts = errors.New("Not enough Parts")
+var ErrNotEnoughParts = errors.New("not enough Parts")
 
 // Multipart represents a MultipartUpload on a S3 compatible Object Store service.
 // It can be used as io.WriteCloser for uploading an object
@@ -76,7 +76,7 @@ func NewMultipart(ctx context.Context, partURLs []string, completeURL, abortURL,
 
 		n, err := io.Copy(ioutil.Discard, pr)
 		if err != nil {
-			m.uploadError = fmt.Errorf("Cannot drain pipe: %v", err)
+			m.uploadError = fmt.Errorf("cannot drain pipe: %v", err)
 			return
 		}
 		if n > 0 {
@@ -120,12 +120,12 @@ func (m *Multipart) cleanup(ctx context.Context) {
 func (m *Multipart) complete(cmu *CompleteMultipartUpload) error {
 	body, err := xml.Marshal(cmu)
 	if err != nil {
-		return fmt.Errorf("Cannot marshal CompleteMultipartUpload request: %v", err)
+		return fmt.Errorf("cannot marshal CompleteMultipartUpload request: %v", err)
 	}
 
 	req, err := http.NewRequest("POST", m.CompleteURL, bytes.NewReader(body))
 	if err != nil {
-		return fmt.Errorf("Cannot create CompleteMultipartUpload request: %v", err)
+		return fmt.Errorf("cannot create CompleteMultipartUpload request: %v", err)
 	}
 	req.ContentLength = int64(len(body))
 	req.Header.Set("Content-Type", "application/xml")
@@ -144,7 +144,7 @@ func (m *Multipart) complete(cmu *CompleteMultipartUpload) error {
 	result := &compoundCompleteMultipartUploadResult{}
 	decoder := xml.NewDecoder(resp.Body)
 	if err := decoder.Decode(&result); err != nil {
-		return fmt.Errorf("Cannot decode CompleteMultipartUpload answer: %v", err)
+		return fmt.Errorf("cannot decode CompleteMultipartUpload answer: %v", err)
 	}
 
 	if result.isError() {
@@ -152,7 +152,7 @@ func (m *Multipart) complete(cmu *CompleteMultipartUpload) error {
 	}
 
 	if result.CompleteMultipartUploadResult == nil {
-		return fmt.Errorf("Cannot read CompleteMultipartUpload answer")
+		return fmt.Errorf("cannot read CompleteMultipartUpload answer")
 	}
 
 	m.extractETag(result.ETag)
@@ -178,7 +178,7 @@ func (m *Multipart) verifyETag(cmu *CompleteMultipartUpload) error {
 func (m *Multipart) readAndUploadOnePart(partURL string, putHeaders map[string]string, src io.Reader, partNumber int) (*completeMultipartUploadPart, error) {
 	file, err := ioutil.TempFile("", "part-buffer")
 	if err != nil {
-		return nil, fmt.Errorf("Unable to create a temporary file for buffering: %v", err)
+		return nil, fmt.Errorf("unable to create a temporary file for buffering: %v", err)
 	}
 	defer func(path string) {
 		if err := os.Remove(path); err != nil {
@@ -188,19 +188,19 @@ func (m *Multipart) readAndUploadOnePart(partURL string, putHeaders map[string]s
 
 	n, err := io.Copy(file, src)
 	if err != nil {
-		return nil, fmt.Errorf("Cannot write part %d to disk: %v", partNumber, err)
+		return nil, fmt.Errorf("cannot write part %d to disk: %v", partNumber, err)
 	}
 	if n == 0 {
 		return nil, nil
 	}
 
 	if _, err = file.Seek(0, io.SeekStart); err != nil {
-		return nil, fmt.Errorf("Cannot rewind part %d temporary dump : %v", partNumber, err)
+		return nil, fmt.Errorf("cannot rewind part %d temporary dump : %v", partNumber, err)
 	}
 
 	etag, err := m.uploadPart(partURL, putHeaders, file, n)
 	if err != nil {
-		return nil, fmt.Errorf("Cannot upload part %d: %v", partNumber, err)
+		return nil, fmt.Errorf("cannot upload part %d: %v", partNumber, err)
 	}
 	return &completeMultipartUploadPart{PartNumber: partNumber, ETag: etag}, nil
 }
@@ -208,7 +208,7 @@ func (m *Multipart) readAndUploadOnePart(partURL string, putHeaders map[string]s
 func (m *Multipart) uploadPart(url string, headers map[string]string, body io.Reader, size int64) (string, error) {
 	deadline, ok := m.ctx.Deadline()
 	if !ok {
-		return "", fmt.Errorf("Missing deadline")
+		return "", fmt.Errorf("missing deadline")
 	}
 
 	part, err := newObject(m.ctx, url, "", headers, deadline, size, false)
diff --git a/internal/redis/redis.go b/internal/redis/redis.go
index 803052dc2f07b70f2f6eb230bb64425cac8f6340..ef7d03e1bc898c86149586d4c2448687a3953aa6 100644
--- a/internal/redis/redis.go
+++ b/internal/redis/redis.go
@@ -274,7 +274,7 @@ func Configure(cfg *config.RedisConfig, dialFunc func(*config.RedisConfig, bool)
 	if sntnl != nil {
 		pool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
 			if !sentinel.TestRole(c, "master") {
-				return errors.New("Role check failed")
+				return errors.New("role check failed")
 			}
 			return nil
 		}
diff --git a/internal/sendfile/sendfile.go b/internal/sendfile/sendfile.go
index 3da8a6420f12c7874ae6c03f15419c3e3a380161..b42b2321cb24fd239242a99d4f2899a27ec1956c 100644
--- a/internal/sendfile/sendfile.go
+++ b/internal/sendfile/sendfile.go
@@ -132,7 +132,7 @@ func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
 	if contentTypeHeaderPresent {
 		data, err := ioutil.ReadAll(io.LimitReader(content, headers.MaxDetectSize))
 		if err != nil {
-			helper.Fail500(w, r, fmt.Errorf("Error reading the file"))
+			helper.Fail500(w, r, fmt.Errorf("error reading the file"))
 			return
 		}
 
diff --git a/internal/terminal/wrappers_test.go b/internal/terminal/wrappers_test.go
index 7abe08fd83d5401125a7e18ab879c0f7fb918402..a2f23e8e7214f8d029a0ed2ce0701286fad88df8 100644
--- a/internal/terminal/wrappers_test.go
+++ b/internal/terminal/wrappers_test.go
@@ -52,7 +52,7 @@ var (
 	kubeMsg       = append([]byte{0}, msg...)
 	kubeMsgBase64 = append([]byte{'0'}, msgBase64...)
 
-	fakeErr = errors.New("fake error")
+	errFake = errors.New("fake error")
 
 	text   = websocket.TextMessage
 	binary = websocket.BinaryMessage
@@ -81,25 +81,25 @@ func assertEqual(t *testing.T, expected, actual *fakeConn, msg string, args ...i
 func TestReadMessage(t *testing.T) {
 	testCases := map[string][]testcase{
 		"channel.k8s.io": {
-			{fake(binary, kubeMsg, fakeErr), fake(binary, kubeMsg, fakeErr)},
+			{fake(binary, kubeMsg, errFake), fake(binary, kubeMsg, errFake)},
 			{fake(binary, kubeMsg, nil), fake(binary, msg, nil)},
 			{fake(text, kubeMsg, nil), fake(binary, msg, nil)},
 			{fakeOther, fakeOther},
 		},
 		"base64.channel.k8s.io": {
-			{fake(text, kubeMsgBase64, fakeErr), fake(text, kubeMsgBase64, fakeErr)},
+			{fake(text, kubeMsgBase64, errFake), fake(text, kubeMsgBase64, errFake)},
 			{fake(text, kubeMsgBase64, nil), fake(binary, msg, nil)},
 			{fake(binary, kubeMsgBase64, nil), fake(binary, msg, nil)},
 			{fakeOther, fakeOther},
 		},
 		"terminal.gitlab.com": {
-			{fake(binary, msg, fakeErr), fake(binary, msg, fakeErr)},
+			{fake(binary, msg, errFake), fake(binary, msg, errFake)},
 			{fake(binary, msg, nil), fake(binary, msg, nil)},
 			{fake(text, msg, nil), fake(binary, msg, nil)},
 			{fakeOther, fakeOther},
 		},
 		"base64.terminal.gitlab.com": {
-			{fake(text, msgBase64, fakeErr), fake(text, msgBase64, fakeErr)},
+			{fake(text, msgBase64, errFake), fake(text, msgBase64, errFake)},
 			{fake(text, msgBase64, nil), fake(binary, msg, nil)},
 			{fake(binary, msgBase64, nil), fake(binary, msg, nil)},
 			{fakeOther, fakeOther},
@@ -119,25 +119,25 @@ func TestReadMessage(t *testing.T) {
 func TestWriteMessage(t *testing.T) {
 	testCases := map[string][]testcase{
 		"channel.k8s.io": {
-			{fake(binary, msg, fakeErr), fake(binary, kubeMsg, fakeErr)},
+			{fake(binary, msg, errFake), fake(binary, kubeMsg, errFake)},
 			{fake(binary, msg, nil), fake(binary, kubeMsg, nil)},
 			{fake(text, msg, nil), fake(binary, kubeMsg, nil)},
 			{fakeOther, fakeOther},
 		},
 		"base64.channel.k8s.io": {
-			{fake(binary, msg, fakeErr), fake(text, kubeMsgBase64, fakeErr)},
+			{fake(binary, msg, errFake), fake(text, kubeMsgBase64, errFake)},
 			{fake(binary, msg, nil), fake(text, kubeMsgBase64, nil)},
 			{fake(text, msg, nil), fake(text, kubeMsgBase64, nil)},
 			{fakeOther, fakeOther},
 		},
 		"terminal.gitlab.com": {
-			{fake(binary, msg, fakeErr), fake(binary, msg, fakeErr)},
+			{fake(binary, msg, errFake), fake(binary, msg, errFake)},
 			{fake(binary, msg, nil), fake(binary, msg, nil)},
 			{fake(text, msg, nil), fake(binary, msg, nil)},
 			{fakeOther, fakeOther},
 		},
 		"base64.terminal.gitlab.com": {
-			{fake(binary, msg, fakeErr), fake(text, msgBase64, fakeErr)},
+			{fake(binary, msg, errFake), fake(text, msgBase64, errFake)},
 			{fake(binary, msg, nil), fake(text, msgBase64, nil)},
 			{fake(text, msg, nil), fake(text, msgBase64, nil)},
 			{fakeOther, fakeOther},
diff --git a/internal/upload/rewrite.go b/internal/upload/rewrite.go
index ae36a1d1c8c3bb4cd7cc82ed95a888206317b614..2086c8e95e7ef130c53064765483d7476b0fcbf1 100644
--- a/internal/upload/rewrite.go
+++ b/internal/upload/rewrite.go
@@ -118,7 +118,7 @@ func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipa
 		if err == filestore.ErrEntityTooLarge {
 			return err
 		}
-		return fmt.Errorf("Persisting multipart file: %v", err)
+		return fmt.Errorf("persisting multipart file: %v", err)
 	}
 
 	for key, value := range fh.GitLabFinalizeFields(name) {
diff --git a/internal/zipartifacts/open_archive.go b/internal/zipartifacts/open_archive.go
index 91abc4bf8f28646c78399b8a6a0ddfe11f2d972e..d285fdd0105b32d9921fe9a4bdd8683963685f0f 100644
--- a/internal/zipartifacts/open_archive.go
+++ b/internal/zipartifacts/open_archive.go
@@ -59,7 +59,7 @@ func openHTTPArchive(ctx context.Context, archivePath string) (*zip.Reader, erro
 	scrubbedArchivePath := helper.ScrubURLParams(archivePath)
 	req, err := http.NewRequest(http.MethodGet, archivePath, nil)
 	if err != nil {
-		return nil, fmt.Errorf("Can't create HTTP GET %q: %v", scrubbedArchivePath, err)
+		return nil, fmt.Errorf("can't create HTTP GET %q: %v", scrubbedArchivePath, err)
 	}
 	req = req.WithContext(ctx)