diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..6cdaa44
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,46 @@
+run:
+  concurrency: 4
+  deadline: 5m
+  timeout: 5m
+
+output:
+  sort-results: true
+
+linters:
+  enable:
+    - asciicheck
+    - depguard
+    - dupword
+    - durationcheck
+    - errorlint
+    - gosec
+    - gocritic
+    - gofmt
+    - gofumpt
+    - goimports
+    - misspell
+    - nolintlint
+    - predeclared
+    - revive
+    - testifylint
+    - unconvert
+
+linters-settings:
+  depguard:
+    rules:
+      main:
+        deny:
+          - pkg: "io/ioutil"
+            desc: "Use corresponding 'os' or 'io' functions instead."
+          - pkg: "github.com/pkg/errors"
+            desc: "Use 'errors' or 'fmt' instead of github.com/pkg/errors"
+          - pkg: "golang.org/x/exp/slices"
+            desc: "Use 'slices' instead."
+  revive:
+    # https://github.com/mgechev/revive/blob/master/defaults.toml
+    # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
+    rules:
+      # A lot of false positives: incorrectly identifies channel draining as "empty code block".
+      # See https://github.com/mgechev/revive/issues/386
+      - name: empty-block
+        disabled: true
diff --git a/internal/tray/tray.go b/internal/tray/tray.go
index 1041d41..2c01466 100644
--- a/internal/tray/tray.go
+++ b/internal/tray/tray.go
@@ -6,16 +6,13 @@ import (
 	"io"
 
 	"github.com/getlantern/systray"
-
 	"github.com/sonjek/mouse-stay-up/internal/config"
 	"github.com/sonjek/mouse-stay-up/internal/mouse"
 	"github.com/sonjek/mouse-stay-up/internal/utils"
 )
 
-var (
-	//go:embed icon.png
-	iconFile embed.FS
-)
+//go:embed icon.png
+var iconFile embed.FS
 
 func loadIcon() ([]byte, error) {
 	file, err := iconFile.Open("icon.png")