diff --git a/.github/workflows/helm_chart.yaml b/.github/workflows/helm_chart.yaml
new file mode 100644
index 0000000..013a486
--- /dev/null
+++ b/.github/workflows/helm_chart.yaml
@@ -0,0 +1,48 @@
+name: Publish Helm Repository
+on:
+  workflow_dispatch:
+  push:
+    branches: [main]
+  pull_request:
+
+env:
+  MIX_ENV: prod
+  KUBECONFIG: /home/runner/.kube/config
+
+permissions:
+  contents: read
+  packages: write
+
+jobs:
+  publish_chart:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4
+
+      - name: helm lint --set livebook.auth.livebookPassword=test
+        run: |
+          helm lint .
+      - name: helm login
+        run: |
+          echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ghcr.io -u $ --password-stdin
+      - name: helm dependency update
+        run: helm dependency update
+      - name: helm package
+        if: ${{ github.event_name != 'push' }}
+        run: helm package .
+      - name: helm package
+        if: ${{ github.event_name == 'push' }}
+        run: |
+          echo "${{ secrets.CHART_SIGN_PRIVATE_KEY }}" | gpg --dearmor --output keyring.gpg
+          helm package --sign --key 'Michael Ruoss (Livebook Helm Package Signing)' --keyring keyring.gpg .
+      - name: helm push
+        if: ${{ github.event_name == 'push' }}
+        run: |
+          helm push livebook-*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts
+      # - name: Upload artifacthub-repo.yml
+      #   run: |
+      #     echo ${{ secrets.GITHUB_TOKEN }} | oras login ghcr.io -u mruoss --password-stdin
+      #     oras push \
+      #       ghcr.io/${{ github.repository_owner }}/charts/livebook:artifacthub.io \
+      #       --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \
+      #       priv/charts/livebook/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml