diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index f84d905..276ce6e 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -10,6 +10,13 @@ on:
     # At 01:37 every Thursday. Details in https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
     - cron: '37 1 * * 4'
 
+permissions:
+  # Optional: allow write access to checks to allow the action to annotate code in the PR.
+  checks: write
+
+env:
+  GO_VERSION: stable
+
 jobs:
   create-matrix:
     runs-on: ubuntu-latest
@@ -23,15 +30,11 @@ jobs:
     outputs:
       matrix: ${{ steps.set-matrix.outputs.languages }}
 
-  analyze:
-    name: Analyze
+  code-ql:
+    name: CodeQL
     needs: create-matrix
     if: ${{ needs.create-matrix.outputs.matrix != '[]' && github.repository == 'photoview/photoview' }}
     runs-on: ubuntu-latest
-    container:
-      image: ${{ ( matrix.language == 'go' && 'golang:1.23-bookworm' ) || ( matrix.language == 'javascript' && 'node:18' ) || 'null' }}
-      volumes:
-        - /opt/hostedtoolcache:/opt/hostedtoolcache
     strategy:
       fail-fast: false
       matrix:
@@ -40,41 +43,6 @@ jobs:
       - name: Checkout repository
         uses: actions/checkout@v4
 
-      - name: Install GO build dependencies
-        if: ${{ matrix.language == 'go' }}
-        working-directory: ./api
-        shell: bash
-        run: |
-          rm -rf /opt/hostedtoolcache/* # free up some space
-          git config --global --add safe.directory /__w/photoview/photoview
-          OS=$(uname -s | tr '[:upper:]' '[:lower:]')
-          ARCH=$(uname -m)
-          case "$ARCH" in
-            x86_64) ARCH="amd64" ;;
-            aarch64) ARCH="arm64" ;;
-            armv6l) ARCH="arm/v6" ;;
-            armv7l) ARCH="arm/v7" ;;
-            *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
-          esac
-          export TARGETPLATFORM="$OS/$ARCH"
-          echo "Platform is ${TARGETPLATFORM}"
-          export GOPATH="/go"
-          export PATH="${GOPATH}/bin:${PATH}"
-          export CGO_ENABLED=1
-          . ../scripts/set_compiler_env.sh
-          # Comment out the last line with 'apt-get install -y reflex sqlite3'
-          sed -i '$ s/^/#/' ../scripts/install_build_dependencies.sh
-          chmod +x ../scripts/*.sh
-          ../scripts/install_build_dependencies.sh
-          apt-get install -y file
-          echo "<<< Dependencies installed >>>"
-          go env
-          go mod download
-          # Patch go-face
-          sed -i 's/-march=native//g' ${GOPATH}/pkg/mod/github.com/!kagami/go-face*/face.go
-          # Build dependencies that use CGO
-          go install github.com/mattn/go-sqlite3 github.com/Kagami/go-face
-
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
         uses: github/codeql-action/init@v3
@@ -95,6 +63,28 @@ jobs:
         with:
           category: "/language:${{ matrix.language }}"
 
+  golang-lint:
+    name: GOlangCI Lint
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Get GO version from `go.mod`
+        run: |
+          echo "GO_VERSION=$(grep '^go ' ./api/go.mod | awk '{print $2}')" >> "$GITHUB_ENV"
+
+      - name: Setup GO
+        uses: actions/setup-go@v5
+        with:
+          go-version: ${{ env.GO_VERSION }}
+
+      - name: golangci-lint
+        uses: golangci/golangci-lint-action@v6
+        with:
+          version: latest
+          working-directory: ./api
+
   anchore:
     name: Anchore scan code dependencies
     runs-on: ubuntu-latest
diff --git a/api/.golangci.yml b/api/.golangci.yml
new file mode 100644
index 0000000..7d06571
--- /dev/null
+++ b/api/.golangci.yml
@@ -0,0 +1,263 @@
+# Options for analysis running.
+run:
+  # Number of operating system threads (`GOMAXPROCS`) that can execute golangci-lint simultaneously.
+  # If it is explicitly set to 0 (i.e. not the default) then golangci-lint will automatically set the value to match Linux container CPU quota.
+  # Default: the number of logical CPUs in the machine
+  #concurrency: 4
+  # Timeout for analysis, e.g. 30s, 5m.
+  # Default: 1m
+  #timeout: 5m
+  # Exit code when at least one issue was found.
+  # Default: 1
+  #issues-exit-code: 2
+  # Include test files or not.
+  # Default: true
+  tests: false
+  # List of build tags, all linters use it.
+  # Default: []
+  #build-tags:
+  #  - mytag
+  # If set, we pass it to "go list -mod={option}". From "go help modules":
+  # If invoked with -mod=readonly, the go command is disallowed from the implicit
+  # automatic updating of go.mod described above. Instead, it fails when any changes
+  # to go.mod are needed. This setting is most useful to check that go.mod does
+  # not need updates, such as in a continuous integration and testing system.
+  # If invoked with -mod=vendor, the go command assumes that the vendor
+  # directory holds the correct copies of dependencies and ignores
+  # the dependency descriptions in go.mod.
+  #
+  # Allowed values: readonly|vendor|mod
+  # Default: ""
+  #modules-download-mode: readonly
+  # Allow multiple parallel golangci-lint instances running.
+  # If false, golangci-lint acquires file lock on start.
+  # Default: false
+  allow-parallel-runners: true
+  # Allow multiple golangci-lint instances running, but serialize them around a lock.
+  # If false, golangci-lint exits with an error if it fails to acquire file lock on start.
+  # Default: false
+  allow-serial-runners: true
+  # Define the Go version limit.
+  # Mainly related to generics support since go1.18.
+  # Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17
+  #go: '1.19'
+
+# output configuration options
+output:
+  # The formats used to render issues.
+  # Formats:
+  # - `colored-line-number`
+  # - `line-number`
+  # - `json`
+  # - `colored-tab`
+  # - `tab`
+  # - `html`
+  # - `checkstyle`
+  # - `code-climate`
+  # - `junit-xml`
+  # - `github-actions`
+  # - `teamcity`
+  # - `sarif`
+  # Output path can be either `stdout`, `stderr` or path to the file to write to.
+  #
+  # For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
+  # The output can be specified for each of them by separating format name and path by colon symbol.
+  # Example: "--out-format=checkstyle:report.xml,json:stdout,colored-line-number"
+  # The CLI flag (`--out-format`) override the configuration file.
+  #
+  # Default:
+  #   formats:
+  #     - format: colored-line-number
+  #       path: stdout
+  formats:
+    - format: junit-xml
+      path: golang-lint.xml
+    - format: sarif
+      path: golang-lint.sarif
+    - format: github-actions
+  # Print lines of code with issue.
+  # Default: true
+  #print-issued-lines: false
+  # Print linter name in the end of issue text.
+  # Default: true
+  #print-linter-name: false
+  # Make issues output unique by line.
+  # Default: true
+  #uniq-by-line: false
+  # Add a prefix to the output file references.
+  # Default: ""
+  #path-prefix: ""
+  # Sort results by the order defined in `sort-order`.
+  # Default: false
+  sort-results: true
+  # Order to use when sorting results.
+  # Require `sort-results` to `true`.
+  # Possible values: `file`, `linter`, and `severity`.
+  #
+  # If the severity values are inside the following list, they are ordered in this order:
+  #   1. error
+  #   2. warning
+  #   3. high
+  #   4. medium
+  #   5. low
+  # Either they are sorted alphabetically.
+  #
+  # Default: ["file"]
+  sort-order:
+    - linter
+    - severity
+    - file # filepath, line, and column.
+  # Show statistics per linter.
+  # Default: false
+  show-stats: true
+
+linters:
+  # Disable all linters.
+  # Default: false
+  #disable-all: true
+  # Enable specific linter
+  # https://golangci-lint.run/usage/linters/#enabled-by-default
+  enable:
+    - asasalint
+    - asciicheck
+    - bidichk
+    - bodyclose
+    - canonicalheader
+    - containedctx
+    - contextcheck
+    - copyloopvar
+    - cyclop
+    - decorder
+    - depguard
+    - dogsled
+    - dupl
+    - dupword
+    - durationcheck
+    - err113
+    - errcheck
+    - errchkjson
+    - errname
+    - errorlint
+    - execinquery
+    - exhaustive
+    - exhaustruct
+    - exportloopref
+    - fatcontext
+    - forbidigo
+    - forcetypeassert
+    - funlen
+    - gci
+    - ginkgolinter
+    - gocheckcompilerdirectives
+    - gochecknoglobals
+    - gochecknoinits
+    - gochecksumtype
+    - gocognit
+    - goconst
+    - gocritic
+    - gocyclo
+    - godot
+    - godox
+    - gofmt
+    - gofumpt
+    - goheader
+    - goimports
+    - gomoddirectives
+    - gomodguard
+    - goprintffuncname
+    - gosec
+    - gosimple
+    - gosmopolitan
+    - govet
+    - grouper
+    - importas
+    - inamedparam
+    - ineffassign
+    - interfacebloat
+    - intrange
+    - ireturn
+    - lll
+    - loggercheck
+    - maintidx
+    - makezero
+    - mirror
+    - misspell
+    - mnd
+    - musttag
+    - nakedret
+    - nestif
+    - nilerr
+    - nilnil
+    - nlreturn
+    - noctx
+    - nolintlint
+    - nonamedreturns
+    - nosprintfhostport
+    - paralleltest
+    - perfsprint
+    - prealloc
+    - predeclared
+    - promlinter
+    - protogetter
+    - reassign
+    - revive
+    - rowserrcheck
+    - sloglint
+    - spancheck
+    - sqlclosecheck
+    - staticcheck
+    - stylecheck
+    - tagalign
+    - tagliatelle
+    - tenv
+    - testableexamples
+    - testifylint
+    - testpackage
+    - thelper
+    - tparallel
+    - typecheck
+    - unconvert
+    - unparam
+    - unused
+    - usestdlibvars
+    - varnamelen
+    - wastedassign
+    - whitespace
+    - wrapcheck
+    - wsl
+    - zerologlint
+  # Enable all available linters.
+  # Default: false
+  #enable-all: true
+  # Disable specific linter
+  # https://golangci-lint.run/usage/linters/#disabled-by-default
+  disable:
+    - deadcode # Deprecated
+    - exhaustivestruct # Deprecated
+    - golint # Deprecated
+    - ifshort # Deprecated
+    - interfacer # Deprecated
+    - maligned # Deprecated
+    - gomnd # Deprecated
+    - nosnakecase # Deprecated
+    - scopelint # Deprecated
+    - structcheck # Deprecated
+    - varcheck # Deprecated
+  # Enable presets.
+  # https://golangci-lint.run/usage/linters
+  # Default: []
+  presets:
+    - bugs
+    - comment
+    - complexity
+    - error
+    - format
+    - import
+    - metalinter
+    - module
+    - performance
+    - sql
+    - style
+  # Enable only fast linters from enabled linters set (first run won't be fast)
+  # Default: false
+  fast: true
