diff --git a/.github/workflows/build-docker-pr.yml b/.github/workflows/build-docker-pr.yml
new file mode 100644
index 00000000..09c5cd34
--- /dev/null
+++ b/.github/workflows/build-docker-pr.yml
@@ -0,0 +1,71 @@
+name: Build
+
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+    branches:
+      - main
+
+concurrency:
+  group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    permissions: write-all
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+      - name: Get changed files
+        id: changed-files
+        uses: dorny/paths-filter@v3
+        with:
+          filters: |
+            files:
+              - '*.nix'
+              - 'go.*'
+              - '**/*.go'
+              - 'integration_test/'
+              - 'config-example.yaml'
+      - uses: DeterminateSystems/nix-installer-action@main
+        if: steps.changed-files.outputs.files == 'true'
+      - uses: DeterminateSystems/magic-nix-cache-action@main
+        if: steps.changed-files.outputs.files == 'true'
+
+      - name: Run build
+        id: build
+        if: steps.changed-files.outputs.files == 'true'
+        run: |
+          nix build |& tee build-result
+          BUILD_STATUS="${PIPESTATUS[0]}"
+
+          OLD_HASH=$(cat build-result | grep specified: | awk -F ':' '{print $2}' | sed 's/ //g')
+          NEW_HASH=$(cat build-result | grep got: | awk -F ':' '{print $2}' | sed 's/ //g')
+
+          echo "OLD_HASH=$OLD_HASH" >> $GITHUB_OUTPUT
+          echo "NEW_HASH=$NEW_HASH" >> $GITHUB_OUTPUT
+
+          exit $BUILD_STATUS
+
+      - name: Nix gosum diverging
+        uses: actions/github-script@v6
+        if: failure() && steps.build.outcome == 'failure'
+        with:
+          github-token: ${{secrets.GITHUB_TOKEN}}
+          script: |
+            github.rest.pulls.createReviewComment({
+              pull_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              body: 'Nix build failed with wrong gosum, please update "vendorSha256" (${{ steps.build.outputs.OLD_HASH }}) for the "headscale" package in flake.nix with the new SHA: ${{ steps.build.outputs.NEW_HASH }}'
+            })
+
+      - uses: actions/upload-artifact@v4
+        if: steps.changed-files.outputs.files == 'true'
+        with:
+          name: headscale-linux
+          path: result/bin/headscale
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 09c5cd34..1e7c4723 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,9 +1,6 @@
-name: Build
+name: Build Docker images for PRs
 
 on:
-  push:
-    branches:
-      - main
   pull_request:
     branches:
       - main
@@ -36,36 +33,22 @@ jobs:
       - uses: DeterminateSystems/magic-nix-cache-action@main
         if: steps.changed-files.outputs.files == 'true'
 
-      - name: Run build
+      - uses: actions/github-script@v6
+        id: get_pr_data
+        with:
+          script: |
+            return (
+              await github.rest.repos.listPullRequestsAssociatedWithCommit({
+                commit_sha: context.sha,
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+              })
+            ).data[0];
+
+      - name: Run ko build
         id: build
         if: steps.changed-files.outputs.files == 'true'
+        env:
+          KO_DOCKER_REPO: ghcr.io/${{ github.repository_owner }}/headscale
         run: |
-          nix build |& tee build-result
-          BUILD_STATUS="${PIPESTATUS[0]}"
-
-          OLD_HASH=$(cat build-result | grep specified: | awk -F ':' '{print $2}' | sed 's/ //g')
-          NEW_HASH=$(cat build-result | grep got: | awk -F ':' '{print $2}' | sed 's/ //g')
-
-          echo "OLD_HASH=$OLD_HASH" >> $GITHUB_OUTPUT
-          echo "NEW_HASH=$NEW_HASH" >> $GITHUB_OUTPUT
-
-          exit $BUILD_STATUS
-
-      - name: Nix gosum diverging
-        uses: actions/github-script@v6
-        if: failure() && steps.build.outcome == 'failure'
-        with:
-          github-token: ${{secrets.GITHUB_TOKEN}}
-          script: |
-            github.rest.pulls.createReviewComment({
-              pull_number: context.issue.number,
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              body: 'Nix build failed with wrong gosum, please update "vendorSha256" (${{ steps.build.outputs.OLD_HASH }}) for the "headscale" package in flake.nix with the new SHA: ${{ steps.build.outputs.NEW_HASH }}'
-            })
-
-      - uses: actions/upload-artifact@v4
-        if: steps.changed-files.outputs.files == 'true'
-        with:
-          name: headscale-linux
-          path: result/bin/headscale
+          ko build --tags=pr-${{ fromJson(steps.get_pr_data.outputs.result).number }},${{ github.sha }} ./cmd/headscale
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 4e91c74d..262d85d4 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -27,8 +27,6 @@ builds:
       - -mod=readonly
     ldflags:
       - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
-    tags:
-      - ts2019
 
 archives:
   - id: golang-cross
diff --git a/.ko.yaml b/.ko.yaml
new file mode 100644
index 00000000..38a2164b
--- /dev/null
+++ b/.ko.yaml
@@ -0,0 +1,16 @@
+defaultBaseImage: gcr.io/distroless/base-debian12:debug
+defaultPlatforms:
+  - linux/arm64
+  - linux/arm/v7
+  - linux/amd64
+  - linux/386
+
+builds:
+  - id: headscale
+    main: ./cmd/headscale
+    env:
+      - CGO_ENABLED=0
+    flags:
+      - -mod=readonly
+    ldflags:
+      - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Git.ShortCommit}}