From 3a6dbcc2c6141508630f02b270cdd99e7f554b7b Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Mon, 20 Apr 2026 16:57:49 +0530 Subject: [PATCH 01/12] Base for self hosted documentation --- pnpm-lock.yaml | 54 +- src/configs/secondary-nav.config.ts | 7 + src/configs/sidebar.config.ts | 30 + .../docs/self-hosted/configuration.mdx | 313 +++++++++ src/content/docs/self-hosted/installation.mdx | 85 +++ src/content/docs/self-hosted/overview.mdx | 66 ++ src/content/docs/self-hosted/setup-script.mdx | 664 ++++++++++++++++++ .../docs/self-hosted/system-requirements.mdx | 108 +++ .../docs/self-hosted/troubleshooting.mdx | 150 ++++ src/content/docs/self-hosted/upgrades.mdx | 123 ++++ 10 files changed, 1573 insertions(+), 27 deletions(-) create mode 100644 src/content/docs/self-hosted/configuration.mdx create mode 100644 src/content/docs/self-hosted/installation.mdx create mode 100644 src/content/docs/self-hosted/overview.mdx create mode 100644 src/content/docs/self-hosted/setup-script.mdx create mode 100644 src/content/docs/self-hosted/system-requirements.mdx create mode 100644 src/content/docs/self-hosted/troubleshooting.mdx create mode 100644 src/content/docs/self-hosted/upgrades.mdx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6dc2d423..3eeab9f3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,7 +170,7 @@ importers: version: 0.2.5(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)) '@tailwindcss/vite': specifier: ^4.2.2 - version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + version: 4.2.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -233,10 +233,10 @@ importers: version: 0.12.0(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))) starlight-page-actions: specifier: ^0.5.0 - version: 0.5.0(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + version: 0.5.0(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) starlight-plugin-icons: specifier: ^1.1.6 - version: 1.1.6(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(typescript@5.9.3)(unocss@66.4.2(postcss@8.5.8)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)))(zod@3.25.76) + version: 1.1.6(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(typescript@5.9.3)(unocss@66.4.2(postcss@8.5.8)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)))(zod@4.3.6) starlight-showcases: specifier: ^0.3.2 version: 0.3.2(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))) @@ -267,7 +267,7 @@ importers: version: 0.4.15(typescript@5.9.3) '@vitejs/plugin-vue': specifier: ^6.0.5 - version: 6.0.5(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3)) + version: 6.0.5(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3)) cross-env: specifier: ^10.1.0 version: 10.1.0 @@ -11545,12 +11545,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': + '@tailwindcss/vite@4.2.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) '@tanstack/virtual-core@3.13.23': {} @@ -11726,13 +11726,13 @@ snapshots: unhead: 2.1.12 vue: 3.5.31(typescript@5.9.3) - '@unocss/astro@66.4.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': + '@unocss/astro@66.4.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': dependencies: '@unocss/core': 66.4.2 '@unocss/reset': 66.4.2 - '@unocss/vite': 66.4.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + '@unocss/vite': 66.4.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) optionalDependencies: - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) '@unocss/cli@66.4.2': dependencies: @@ -11861,7 +11861,7 @@ snapshots: dependencies: '@unocss/core': 66.4.2 - '@unocss/vite@66.4.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': + '@unocss/vite@66.4.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))': dependencies: '@ampproject/remapping': 2.3.0 '@unocss/config': 66.4.2 @@ -11872,7 +11872,7 @@ snapshots: pathe: 2.0.3 tinyglobby: 0.2.15 unplugin-utils: 0.2.5 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) '@usesapient/agent-tracker@0.1.1': {} @@ -11944,10 +11944,10 @@ snapshots: vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) vue: 3.5.31(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.5(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) vue: 3.5.31(typescript@5.9.3) '@vue/babel-helper-vue-transform-on@1.5.0': {} @@ -17148,16 +17148,16 @@ snapshots: dependencies: '@astrojs/starlight': 0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)) - starlight-page-actions@0.5.0(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): + starlight-page-actions@0.5.0(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): dependencies: '@astrojs/starlight': 0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)) astro: 5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3) - vite-plugin-static-copy: 3.4.0(patch_hash=af70d2811d381cdea319ad374155ad8bf276cf04bf80834dd64bc0800a7cd4a3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) - vite-plugin-virtual: 0.5.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + vite-plugin-static-copy: 3.4.0(patch_hash=af70d2811d381cdea319ad374155ad8bf276cf04bf80834dd64bc0800a7cd4a3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + vite-plugin-virtual: 0.5.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) transitivePeerDependencies: - vite - starlight-plugin-icons@1.1.6(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(typescript@5.9.3)(unocss@66.4.2(postcss@8.5.8)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)))(zod@3.25.76): + starlight-plugin-icons@1.1.6(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)))(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))(typescript@5.9.3)(unocss@66.4.2(postcss@8.5.8)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)))(zod@4.3.6): dependencies: '@astrojs/starlight': 0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3)) astro: 5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3) @@ -17168,8 +17168,8 @@ snapshots: rehype: 13.0.2 typescript: 5.9.3 unist-util-visit: 5.1.0 - unocss: 66.4.2(postcss@8.5.8)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) - zod: 3.25.76 + unocss: 66.4.2(postcss@8.5.8)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + zod: 4.3.6 starlight-showcases@0.3.2(@astrojs/starlight@0.37.7(astro@5.18.1(@netlify/blobs@10.7.0)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(yaml@2.8.3))): dependencies: @@ -17731,9 +17731,9 @@ snapshots: dependencies: normalize-path: 2.1.1 - unocss@66.4.2(postcss@8.5.8)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): + unocss@66.4.2(postcss@8.5.8)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): dependencies: - '@unocss/astro': 66.4.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + '@unocss/astro': 66.4.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) '@unocss/cli': 66.4.2 '@unocss/core': 66.4.2 '@unocss/postcss': 66.4.2(postcss@8.5.8) @@ -17751,9 +17751,9 @@ snapshots: '@unocss/transformer-compile-class': 66.4.2 '@unocss/transformer-directives': 66.4.2 '@unocss/transformer-variant-group': 66.4.2 - '@unocss/vite': 66.4.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) + '@unocss/vite': 66.4.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) optionalDependencies: - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - postcss - supports-color @@ -17897,17 +17897,17 @@ snapshots: dependencies: monaco-editor: 0.54.0 - vite-plugin-static-copy@3.4.0(patch_hash=af70d2811d381cdea319ad374155ad8bf276cf04bf80834dd64bc0800a7cd4a3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): + vite-plugin-static-copy@3.4.0(patch_hash=af70d2811d381cdea319ad374155ad8bf276cf04bf80834dd64bc0800a7cd4a3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): dependencies: chokidar: 3.6.0 p-map: 7.0.4 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) - vite-plugin-virtual@0.5.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): + vite-plugin-virtual@0.5.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)): dependencies: - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3) vite-plugin-vue-devtools@7.7.9(rollup@4.60.0)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3)): dependencies: diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 5454ac40f..23a136a81 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -25,6 +25,7 @@ import IconLucidePalette from '~icons/lucide/palette' import IconLucideCheck from '~icons/lucide/check' import IconLucideHome from '~icons/lucide/home' import IconLucideBookOpenText from '~icons/lucide/book-open-text' +import IconLucideServer from '~icons/lucide/server' export interface NavItem { id: string // Unique identifier for the nav item @@ -236,6 +237,12 @@ export const secondaryNavItems: NavItem[] = [ }, ], }, + { + id: 'self-hosted', + href: '/self-hosted/overview/', + label: 'Self-hosted', + iconComponent: IconLucideServer, + }, // Temporarily hidden - Agent Auth // { // id: 'agent-actions', diff --git a/src/configs/sidebar.config.ts b/src/configs/sidebar.config.ts index 593870019..7e1b63064 100644 --- a/src/configs/sidebar.config.ts +++ b/src/configs/sidebar.config.ts @@ -466,6 +466,30 @@ export const sidebar = [ }, ], }, + { + label: 'Self-hosted', + id: 'self-hosted', + link: '/self-hosted/overview/', + icon: 'server', + items: [ + { + label: 'Getting started', + items: ['self-hosted/overview', 'self-hosted/system-requirements'], + }, + { + label: 'Deploy', + items: [ + 'self-hosted/configuration', + 'self-hosted/setup-script', + 'self-hosted/installation', + ], + }, + { + label: 'Maintain', + items: ['self-hosted/upgrades', 'self-hosted/troubleshooting'], + }, + ], + }, { label: 'SDKs & APIs', id: 'sdks', @@ -573,6 +597,9 @@ export const topics = { '/**/*', // Catch-all: anything not matched above defaults here ], + // Self-hosted deployment + 'self-hosted': ['/self-hosted/**/*'], + // === Developer Kit (tools, code, SDKs, AI helpers) === 'dev-kit': [ '/dev-kit/tools/**/*', @@ -678,6 +705,9 @@ export const sidebarToSecondaryNav: Record = { // SDKs sidebar → 'SDKs' tab sdks: 'sdks', + // Self-hosted sidebar → 'Self-hosted' top-level nav item + 'self-hosted': 'self-hosted', + // Events reference sidebar → 'Webhooks' tab under API Reference 'events-reference': 'webhooks-events', } diff --git a/src/content/docs/self-hosted/configuration.mdx b/src/content/docs/self-hosted/configuration.mdx new file mode 100644 index 000000000..388c6a61f --- /dev/null +++ b/src/content/docs/self-hosted/configuration.mdx @@ -0,0 +1,313 @@ +--- +title: Configure Scalekit +description: Complete values.yaml examples and field reference for a self-hosted Scalekit deployment. +sidebar: + label: Configuration + order: 3 +tags: [self-hosted, configuration, helm, values, kubernetes, gateway] +tableOfContents: true +prev: + label: System requirements + link: /self-hosted/system-requirements/ +next: + label: Setup script + link: /self-hosted/setup-script/ +--- + +import { Tabs, TabItem, Aside } from '@astrojs/starlight/components' + +Scalekit is configured through a `values.yaml` file passed to Helm at install and upgrade time. This page shows two complete examples followed by a field-by-field reference. + +The full schema is in `scalekit/values.schema.json` inside the chart package. + +## Complete examples + +Pick the example that matches your setup. Replace placeholder values before running `helm install`. + + + + +Use this configuration to evaluate Scalekit without provisioning external PostgreSQL or Redis. The chart spins up bundled instances automatically. + +**Do not use this in production.** The bundled databases have no backups, no replication, and no persistent storage guarantees. + +```yaml title="values.yaml (evaluation)" +scalekit: + config: + app: + domain: "auth.example.com" # your domain, without scheme or trailing slash + protocol: "https" + region: "us" # us or eu — set once, do not change after first install + + seedData: + adminUser: + firstName: "Admin" + lastName: "User" + email: "admin@example.com" + emailServer: + serverType: "SMTP" + provider: "POSTMARK" # POSTMARK, SENDGRID, or OTHER + enabled: true + settings: + fromEmail: "noreply@example.com" + fromName: "Your Company" + host: "smtp.postmarkapp.com" + port: 587 + username: "your-smtp-api-key" + +# Bundled PostgreSQL — evaluation only +postgresql: + enabled: true + +# Bundled Redis — evaluation only +redis: + enabled: true + +gateway: + enabled: true + className: "gke-l7-global-external-managed" # your GatewayClass + provider: "gcp" # gcp, or "" for generic Gateway API + redirectToHttps: true + healthCheckPolicy: + enabled: true # GKE only +``` + + + + +Use this configuration for production deployments with external PostgreSQL and Redis that you manage. + +```yaml title="values.yaml (production)" +scalekit: + config: + app: + domain: "auth.example.com" # your domain, without scheme or trailing slash + protocol: "https" + region: "us" # us or eu — set once, do not change after first install + + database: + host: "pg.internal.example.com" + name: "scalekit" + user: "scalekit" + port: 5432 + # Password is stored in the authentication-secret Kubernetes secret + + redis: + host: "redis.internal.example.com" + port: 6379 + db: 0 + # Password is stored in the authentication-secret Kubernetes secret + + seedData: + adminUser: + firstName: "Admin" + lastName: "User" + email: "admin@example.com" + emailServer: + serverType: "SMTP" + provider: "POSTMARK" # POSTMARK, SENDGRID, or OTHER + enabled: true + settings: + fromEmail: "noreply@example.com" + fromName: "Your Company" + host: "smtp.postmarkapp.com" + port: 587 + username: "your-smtp-api-key" + +# External services — disable subcharts +postgresql: + enabled: false + +redis: + enabled: false + +gateway: + enabled: true + className: "gke-l7-global-external-managed" # your GatewayClass + provider: "gcp" # gcp, or "" for generic Gateway API + redirectToHttps: true + healthCheckPolicy: + enabled: true # GKE only +``` + + + + +--- + +## Field reference + +### App + +```yaml +scalekit: + config: + app: + domain: "auth.example.com" + protocol: "https" + region: "us" +``` + +| Field | Description | +|-------|-------------| +| `domain` | Base domain for your Scalekit instance. Must match your gateway hostname. | +| `protocol` | Use `https` in production. For local HTTP dev, set to `http` and add `oidc.allow_insecure: true`. | +| `region` | Data residency context. Set once — do not change after the initial install. | + +### Database + +```yaml +scalekit: + config: + database: + host: "your-db-host" + name: "scalekit" + user: "scalekit" + port: 5432 +``` + +The database password is stored in the `authentication-secret` Kubernetes secret (`database_password` key), not in `values.yaml`. + +Omit this section entirely when using the bundled PostgreSQL subchart (`postgresql.enabled: true`). + +### Redis + +```yaml +scalekit: + config: + redis: + host: "your-redis-host" + port: 6379 + db: 0 +``` + +The Redis password is stored in the `authentication-secret` Kubernetes secret (`redis_password` key). + +Omit this section entirely when using the bundled Redis subchart (`redis.enabled: true`). + +### Seed data + +Seed data is applied once on first install. It creates the initial admin user and configures the email server. + +```yaml +scalekit: + config: + seedData: + adminUser: + firstName: "Admin" + lastName: "User" + email: "admin@example.com" + emailServer: + serverType: "SMTP" + provider: "POSTMARK" # POSTMARK, SENDGRID, or OTHER + enabled: true + settings: + fromEmail: "noreply@example.com" + fromName: "Your Company" + host: "smtp.postmarkapp.com" + port: 587 + username: "your-smtp-api-key-or-username" +``` + + + +### Gateway + +Scalekit uses the Kubernetes Gateway API for ingress. + +```yaml +gateway: + enabled: true + className: "gke-l7-global-external-managed" + provider: "gcp" + redirectToHttps: true + healthCheckPolicy: + enabled: true # GKE only +``` + +Set `gateway.className` to the GatewayClass for your cluster: + +| Provider | GatewayClass | +|----------|-------------| +| GKE (external) | `gke-l7-global-external-managed` | +| GKE (internal) | `gke-l7-regional-internal-managed` | +| Istio | `istio` | +| Envoy Gateway | `eg` | + +Set `provider: "gcp"` only for GKE — it enables GKE-specific resources like `HealthCheckPolicy`. Leave it empty (`""`) for all other providers. + +## Gateway annotations + +Annotations on the Gateway resource are how you attach TLS certificates and configure provider-specific behavior. Add them under `gateway.annotations` in your `values.yaml`: + +```yaml +gateway: + annotations: + : "" +``` + +Common annotations by provider: + +| Provider | Annotation | Purpose | +|----------|-----------|---------| +| GKE | `networking.gke.io/certmap` | Attach a GCP Certificate Manager cert map for TLS | +| cert-manager (any cluster) | `cert-manager.io/cluster-issuer` | Provision TLS via cert-manager | +| AWS (ALB) | `kubernetes.io/ingress.class` | Route through an ALB | + +### Example: GCP Certificate Manager + +```yaml +gateway: + enabled: true + className: "gke-l7-global-external-managed" + provider: "gcp" + annotations: + networking.gke.io/certmap: "scalekit-cert-map" + redirectToHttps: true + healthCheckPolicy: + enabled: true +``` + +## Optional components + +### OpenFGA (fine-grained authorization) + +OpenFGA is disabled by default. Enable it when you need fine-grained authorization at scale: + +```yaml +sidecars: + openfga: + enabled: true +``` + +OpenFGA requires its own PostgreSQL database (`openfga`). Credentials are stored in the `openfga-secrets` Kubernetes secret. + +### Directory server (SCIM) + +SCIM provisioning is disabled by default: + +```yaml +scalekit: + config: + directoryServer: + enabled: true +``` + +## Secrets reference + +All sensitive values are stored in Kubernetes secrets, not in `values.yaml`. The setup script (`setup-secrets.sh`) creates these for you. + +| Secret name | Key fields | +|-------------|------------| +| `authentication-service-token` | `TOKEN` — dashboard auth token | +| `db-migrations` | `DATABASE_URL`, `DB_ADAPTER` | +| `authentication-secret` | DB password, Redis password, OIDC keys, cookie keys, email keys, Svix API key | +| `svix-secrets` | `db-dsn`, `jwt-secret`, `main-secret`, `redis-dsn`, `api-token` | +| `openfga-secrets` | `keys`, `password`, `uri`, `username` | +| `artifact-registry-secret` | Docker registry credentials for `ar.scalekit.cloud` | + + diff --git a/src/content/docs/self-hosted/installation.mdx b/src/content/docs/self-hosted/installation.mdx new file mode 100644 index 000000000..732e0f8eb --- /dev/null +++ b/src/content/docs/self-hosted/installation.mdx @@ -0,0 +1,85 @@ +--- +title: Install Scalekit +description: Deploy Scalekit on a Kubernetes cluster using Helm and the Gateway API. +sidebar: + label: Installation + order: 6 +tags: [self-hosted, installation, kubernetes, helm, gateway] +tableOfContents: true +prev: + label: Setup script + link: /self-hosted/setup-script/ +next: + label: Upgrades & maintenance + link: /self-hosted/upgrades/ +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +Deploy Scalekit on any Kubernetes cluster using the Helm chart distributed through [distr.sh](https://distr.sh). The chart uses the Kubernetes **Gateway API** for ingress. + + +1. ## Get the Helm chart and registry token + + Log in to your [distr.sh](https://distr.sh) account. From the Scalekit application page, copy: + + - Your **Helm chart** archive or repository URL + - Your **container registry token** for `ar.scalekit.cloud` + +2. ## Run the setup script + + The [setup script](/self-hosted/setup-script/) collects your configuration interactively and generates two files: a secrets script and a `values.yaml`. Copy the script from that page, then run it: + + ```bash + chmod +x setup-secrets.sh + bash setup-secrets.sh + ``` + + When prompted for environment, enter `2` for a Kubernetes cluster. + + The script walks you through namespace, PostgreSQL, Redis, SMTP, registry token, Gateway, domain, and admin user settings. At the end it prints the exact commands to run next. + +3. ## Apply the Kubernetes secrets + + Run the secrets script the setup script generated: + + ```bash + bash scalekit-secrets-gke-.sh + ``` + + Verify all secrets were created: + + ```bash + kubectl get secrets -n + ``` + + Expected secrets: `authentication-service-token`, `db-migrations`, `authentication-secret`, `svix-secrets`, `openfga-secrets`, `artifact-registry-secret`. + +4. ## Install the Helm chart + + Run the `helm install` command printed by the setup script: + + ```bash + helm install scalekit oci://ar.scalekit.cloud/scalekit/charts/scalekit \ + --version \ + -n \ + --values values-gke-.yaml \ + --wait + ``` + + The pre-install hook runs database migrations before the pods start. `--wait` blocks until all pods are ready. + + + +5. ## Verify the deployment + + ```bash + kubectl get pods -n + kubectl get gateway -n + ``` + + All pods should show `Running` status. Open the admin dashboard at `https://` and sign in with the admin credentials you provided during setup. + + diff --git a/src/content/docs/self-hosted/overview.mdx b/src/content/docs/self-hosted/overview.mdx new file mode 100644 index 000000000..34b50fd25 --- /dev/null +++ b/src/content/docs/self-hosted/overview.mdx @@ -0,0 +1,66 @@ +--- +title: Self-hosted deployment overview +description: Deploy Scalekit on your own Kubernetes cluster to meet data residency, compliance, and network isolation requirements. +sidebar: + label: Overview + order: 1 + next: + label: System requirements + link: /self-hosted/system-requirements/ +tags: [self-hosted, on-premises, kubernetes, helm, deployment] +tableOfContents: true +--- + +import { Aside } from '@astrojs/starlight/components' + +Scalekit's self-hosted deployment lets you run the full Scalekit platform on your own Kubernetes cluster. All authentication data stays within your infrastructure — nothing leaves your network. + +Use self-hosted deployment when your organization requires: + +- **Data residency**: Auth data must remain in a specific region or on-premises location +- **Network isolation**: The auth service must not be reachable from the public internet +- **Compliance**: Regulations such as FedRAMP, HIPAA, or internal security policies prohibit use of SaaS auth services +- **Air-gapped environments**: Your deployment environment has no outbound internet access + +## How it works + +Scalekit is distributed as a **Helm chart** and deployed on Kubernetes. Scalekit uses [distr.sh](https://distr.sh) to distribute the chart and container images to your cluster. + +A pre-install Helm hook runs database migrations automatically before each install or upgrade — you do not run migrations manually. + +## What Scalekit runs + +A self-hosted deployment runs as a single Kubernetes `Deployment` with multiple containers: + +| Component | Description | +|-----------|-------------| +| **Auth service** | Core service handling login, token issuance, sessions, SSO, and SCIM | +| **Dashboard** | Admin web UI for managing your Scalekit instance | +| **Flagd** | Feature flag sidecar for runtime configuration | +| **Svix** | Webhook delivery service | +| **OpenFGA** | Fine-grained authorization engine (optional) | + +## What you provide + +Scalekit does not bundle a database or cache in production. You provision and manage these separately: + +| Dependency | Requirement | Notes | +|------------|-------------|-------| +| **Kubernetes** | 1.27 or later | Any managed or self-managed cluster | +| **PostgreSQL** | 15 or later | Three databases required: `scalekit`, `svix`, `openfga` | +| **Redis** | 6.2 or later | Used for sessions, caching, and job queues | +| **SMTP** | Any provider | Postmark and SendGrid have first-class support | + + + + + +## Next steps + +1. [System requirements](/self-hosted/system-requirements/) — verify your infrastructure before starting +2. [Install Scalekit](/self-hosted/installation/) — deploy the Helm chart on your cluster +3. [Configure Scalekit](/self-hosted/configuration/) — set your domain, database, and email settings diff --git a/src/content/docs/self-hosted/setup-script.mdx b/src/content/docs/self-hosted/setup-script.mdx new file mode 100644 index 000000000..f6b2dff58 --- /dev/null +++ b/src/content/docs/self-hosted/setup-script.mdx @@ -0,0 +1,664 @@ +--- +title: Secrets setup script +description: Interactive script that generates all Kubernetes secrets and a values.yaml for your Scalekit deployment. +sidebar: + label: Setup script + order: 5 + prev: + label: Configure Scalekit + link: /self-hosted/configuration/ + next: + label: Install Scalekit + link: /self-hosted/installation/ +tags: [self-hosted, secrets, kubernetes, helm, setup] +tableOfContents: true +--- + +import { Aside } from '@astrojs/starlight/components' + +The setup script is a **one-time tool for initial deployment**. Run it once to bootstrap your cluster — do not run it again on an existing installation. + +It collects your configuration interactively and produces two output files: + +- **A secrets script** (`scalekit-secrets-.sh`) — runs `kubectl` commands to create all six required Kubernetes secrets +- **A values file** (`values-.yaml`) — ready to pass directly to `helm install` + +At the end it prints the exact `helm install` command to run. + +## Prerequisites + +| Tool | Version | Purpose | +|------|---------|---------| +| `bash` | 4.0 or later | Run the script | +| `openssl` | Any modern version | Generate cryptographic keys and tokens | +| `python3` | 3.6 or later | Generate Svix JWT and OIDC client secret | +| `kubectl` | 1.27 or later | Create Kubernetes secrets in your cluster | + +`kubectl` must be configured and pointed at the cluster you are deploying to before you run the script. + +On macOS, `bash` ships as version 3. Install a newer version with Homebrew: + +```bash +brew install bash +``` + +## Run the script + + + +Copy the script below, save it as `setup-secrets.sh`, make it executable, then run it: + +```bash +chmod +x setup-secrets.sh +bash setup-secrets.sh +``` + +When prompted to choose an environment, enter `2` for a Kubernetes cluster deployment. + +### Optional flags + +| Flag | Effect | +|------|--------| +| `--enable-openfga` | Includes OpenFGA secrets and database configuration | +| `--change-defaults` | Prompts you to confirm or override default values instead of accepting them silently | + +## What the script collects + +The script walks through these sections: + +| Section | What it asks | +|---------|-------------| +| **Namespace** | Kubernetes namespace to deploy into | +| **PostgreSQL** | Host, port, credentials, and database names | +| **Redis** | Host, port, password, and db indexes for app, background jobs, and Svix | +| **Email (SMTP)** | From address, host, port, username, and password | +| **Container registry** | Registry token from distr.sh | +| **Gateway** | GatewayClass name and GCP certificate map | +| **App settings** | Domain, region | +| **Admin user** | First name, last name, email for the initial dashboard login | + +All cryptographic values (OIDC keys, cookie keys, Svix JWT, etc.) are auto-generated — you do not supply these. + +## After the script completes + +The script prints the exact commands to run: + +```bash +# 1. Apply all Kubernetes secrets +bash scalekit-secrets-.sh + +# 2. Install the Helm chart +helm install scalekit oci://ar.scalekit.cloud/scalekit/charts/scalekit \ + --version \ + -n \ + --values values-.yaml +``` + + + +## Script + +```bash title="setup-secrets.sh" +#!/usr/bin/env bash +set -euo pipefail + +# ── Arguments ───────────────────────────────────────────────────────────────── +# Usage: bash setup-secrets.sh [--enable-openfga] [--change-defaults] +OPENFGA_ENABLED="false" +CHANGE_DEFAULTS="false" +for arg in "$@"; do + case "$arg" in + --enable-openfga) OPENFGA_ENABLED="true" ;; + --change-defaults) CHANGE_DEFAULTS="true" ;; + *) echo "Unknown argument: $arg"; exit 1 ;; + esac +done + +# ── Colours ────────────────────────────────────────────────────────────────── +BOLD=$'\033[1m' +DIM=$'\033[2m' +RED=$'\033[31m' +GREEN=$'\033[32m' +YELLOW=$'\033[33m' +CYAN=$'\033[36m' +RESET=$'\033[0m' + +header() { echo "\n${BOLD}${CYAN}▶ $*${RESET}"; } +prompt() { echo "${YELLOW}$*${RESET}"; } +success() { echo "${GREEN}✓ $*${RESET}"; } +dim() { echo "${DIM}$*${RESET}"; } + +ask() { + local var="$1" msg="$2" default="${3:-}" + if [[ -n "$default" && "$CHANGE_DEFAULTS" == "false" ]]; then + eval "$var=\"$default\"" + dim " $msg = $default (default)" + return + fi + while true; do + if [[ -n "$default" ]]; then + read -rp "${YELLOW}$msg [${default}]: ${RESET}" input + else + read -rp "${YELLOW}$msg: ${RESET}" input + fi + if [[ -z "$input" && -n "$default" ]]; then + eval "$var=\"$default\"" + break + elif [[ -n "$input" ]]; then + eval "$var=\"$input\"" + break + else + echo "${RED} ✗ This field is required. Please enter a value.${RESET}" + fi + done +} + +ask_secret() { + local var="$1" msg="$2" default="${3:-}" + local allow_empty="${3+yes}" + while true; do + if [[ -n "$default" ]]; then + read -rp "${YELLOW}$msg [${default}]: ${RESET}" input + else + read -rp "${YELLOW}$msg: ${RESET}" input + fi + if [[ -z "$input" && -n "$default" ]]; then + eval "$var=\"$default\"" + break + elif [[ -z "$input" && "$allow_empty" == "yes" ]]; then + eval "$var=\"\"" + break + elif [[ -n "$input" ]]; then + eval "$var=\"$input\"" + break + else + echo "${RED} ✗ This field is required. Please enter a value.${RESET}" + fi + done +} + +# ── Step 1: Namespace & environment ────────────────────────────────────────── +header "Step 1 — Namespace & environment" +ask NAMESPACE "Kubernetes namespace to deploy Scalekit into" +echo +echo -e "${YELLOW}Which environment are you deploying to?${RESET}" +echo " 1) Minikube (local)" +echo " 2) Kubernetes cluster (GKE / cloud)" +read -rp "${YELLOW}Enter 1 or 2: ${RESET}" ENV_CHOICE +ENV_LABEL=$([[ "$ENV_CHOICE" == "1" ]] && echo "minikube" || echo "gke") + +# ── Step 2: Auto-generate values ───────────────────────────────────────────── +header "Step 2 — Generating secure values" + +OIDC_MASTER_KEY=$(openssl rand -hex 16) +SECURECOOKIE_ENCRYPTKEY=$(openssl rand -hex 16) +SECURECOOKIE_HASHKEY=$(openssl rand -hex 16) +SVIX_JWT_SECRET=$(openssl rand -base64 32) +SVIX_MAIN_SECRET=$(openssl rand -base64 32) +TRAEFIK_TOKEN=$(openssl rand -hex 16) +FGA_API_TOKEN=$(openssl rand -hex 24) +OPENFGA_EXTRA_KEY=$(openssl rand -hex 24) +APP_OIDC_CLIENT_ID="skc_8573429015935040" + +APP_OIDC_CLIENT_SECRET=$(python3 -c " +import secrets +print(f'sk_{secrets.token_urlsafe(48)}') +") + +success "Generated: oidc_master_key, securecookie keys, svix secrets, traefik token, fga tokens, oidc client id/secret" + +# ── Step 3: Svix JWT ────────────────────────────────────────────────────────── +header "Step 3 — Svix JWT token" +dim "The Svix API token is a JWT signed with the generated svix_jwt_secret." + +SVIX_SUB=$(python3 -c " +import secrets, string +chars = string.ascii_letters + string.digits +print('org_' + ''.join(secrets.choice(chars) for _ in range(22))) +") + +SVIX_API_KEY=$(python3 -c " +import base64, hashlib, hmac as _hmac, json, time + +secret = '''${SVIX_JWT_SECRET}''' +sub = '${SVIX_SUB}' +now = int(time.time()) +exp = now + 315360000 # 10 years + +header = base64.urlsafe_b64encode(json.dumps({'alg':'HS256','typ':'JWT'}, separators=(',',':')).encode()).rstrip(b'=').decode() +payload = base64.urlsafe_b64encode(json.dumps({'iat':now,'exp':exp,'nbf':now,'iss':'svix-server','sub':sub}, separators=(',',':')).encode()).rstrip(b'=').decode() +msg = f'{header}.{payload}' +sig = base64.urlsafe_b64encode(_hmac.new(secret.encode(), msg.encode(), hashlib.sha256).digest()).rstrip(b'=').decode() +print(f'{msg}.{sig}') +") + +success "Svix JWT token generated (used as svix_api_key and svix-secrets api-token)" + +# ── Step 4: Collect user-provided values ───────────────────────────────────── +header "Step 4 — Required configuration" +dim " The following sections collect everything needed to configure Scalekit's" +dim " services and generate all Kubernetes secrets and the values.yaml file." +echo + +# ── Database ────────────────────────────────────────────────────────────────── +header " [Database] PostgreSQL" +dim " Scalekit uses a shared PostgreSQL server with one set of credentials" +dim " across all components. Each component gets its own database." +dim "" +dim " These values will be used to:" +dim " - create the 'db-migrations' secret (DATABASE_URL, DB_ADAPTER)" +dim " - create the 'authentication-secret' (database_password)" +dim " - create the 'svix-secrets' (db-dsn)" +if [[ "$OPENFGA_ENABLED" == "true" ]]; then +dim " - create the 'openfga-secrets' (uri, password, username)" +fi +dim "" +dim " All databases must already exist on your server before running helm install." +echo + +ask DB_HOST " PostgreSQL host (IP or hostname)" +read -rp "${YELLOW} PostgreSQL port [5432]: ${RESET}" input; DB_PORT="${input:-5432}" +ask DB_USER " PostgreSQL username" +ask_secret DATABASE_PASSWORD " PostgreSQL password" +echo +dim " Database names — each component gets its own isolated database:" +ask DB_NAME_SCALEKIT " Scalekit main application database name" "scalekit" +ask DB_NAME_SVIX " Svix webhooks database name" "webhooks" +if [[ "$OPENFGA_ENABLED" == "true" ]]; then + ask DB_NAME_OPENFGA " OpenFGA authorization database name" "openfga" +fi +echo + +# Construct all DB URLs from shared credentials +DATABASE_URL="postgresql://${DB_USER}:${DATABASE_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME_SCALEKIT}" +SVIX_DB_DSN="postgresql://${DB_USER}:${DATABASE_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME_SVIX}" +if [[ "$OPENFGA_ENABLED" == "true" ]]; then + OPENFGA_DB_URI="postgresql://${DB_USER}:${DATABASE_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME_OPENFGA}" +fi + +# ── Redis ───────────────────────────────────────────────────────────────────── +header " [Redis]" +dim " Redis powers three isolated workloads — main app cache, the Asynq" +dim " background job queue, and the Svix webhook message bus. All three share" +dim " the same server but use different database indexes to stay isolated." +dim "" +dim " These values will be used to:" +dim " - set redis/asynq config in values.yaml" +dim " - create the 'authentication-secret' (redis_password, asynq_redis_password)" +dim " - create the 'svix-secrets' (redis-dsn)" +dim "" +dim " Leave password empty if your Redis instance has no auth configured." +echo + +ask REDIS_HOST " Redis host (IP or hostname)" +read -rp "${YELLOW} Redis port [6379]: ${RESET}" input; REDIS_PORT="${input:-6379}" +ask_secret REDIS_PASSWORD " Redis password (leave empty if none)" "" +ASYNQ_REDIS_PASSWORD="$REDIS_PASSWORD" +echo +dim " Database indexes — each component gets its own slot to avoid key collisions:" +ask REDIS_DB " Redis db index for main application" "10" +ask ASYNQ_REDIS_DB " Redis db index for Asynq background jobs" "12" +ask SVIX_REDIS_DB " Redis db index for Svix webhooks" "11" + +SVIX_REDIS_DSN="redis://${REDIS_HOST}:${REDIS_PORT}/${SVIX_REDIS_DB}#insecure" +echo + +# ── Email ───────────────────────────────────────────────────────────────────── +EMAIL_KEY="na" +SENDGRID_KEY="na" +POSTMARK_KEY="na" + +header " [Email] Outbound SMTP" +dim " Scalekit sends transactional emails (invites, magic links, verification" +dim " codes) via SMTP." +dim "" +dim " These values will be used to:" +dim " - set seedData.emailServer in values.yaml (from, host, port, username)" +echo + +ask EMAIL_FROM " Sender email address (e.g. hi@yourdomain.com)" +ask EMAIL_FROM_NAME " Sender display name (e.g. Team Scalekit)" +ask_secret SMTP_HOST " SMTP server host" "smtp.postmarkapp.com" +ask_secret SMTP_PORT " SMTP server port" "587" +ask SMTP_USERNAME " SMTP login username" +ask_secret SMTP_PASSWORD " SMTP login password" +echo + +# ── Image Registry ──────────────────────────────────────────────────────────── +header " [Registry] Container Image Registry" +dim " Scalekit images are hosted on a private registry at ar.scalekit.cloud." +dim " Kubernetes needs a pull secret with your access token to download images." +dim "" +dim " These values will be used to:" +dim " - create the 'artifact-registry-secret' (docker-registry pull secret)" +echo + +ask REGISTRY_SERVER " Container registry server URL" "ar.scalekit.cloud" +dim " Registry username is always: oauth2accesstoken" +REGISTRY_USERNAME="oauth2accesstoken" +ask_secret REGISTRY_PASSWORD " Registry access token (your personal or service account token)" +echo + +# ── GKE Gateway (GKE only) ──────────────────────────────────────────────────── +if [[ "$ENV_CHOICE" == "2" ]]; then + header " [GKE Gateway]" + dim " On GKE, all external traffic enters through a Google-managed L7 gateway." + dim "" + dim " These values will be used to:" + dim " - set gateway.className and networking.gke.io/certmap in values.yaml" + echo + ask GATEWAY_CLASS " GKE gateway class name" "gke-l7-global-external-managed" + ask CERT_MAP_NAME " GCP certificate map name covering your domain (networking.gke.io/certmap)" + echo +fi + +# ── App ─────────────────────────────────────────────────────────────────────── +header " [App] Application settings" +dim " The base domain is used to derive all subdomains (app.*, auth.*). Region" +dim " controls data residency labelling. Replica count sets pods per service." +dim "" +dim " These values will be used to:" +dim " - set scalekit.config.app.* in values.yaml (domain, protocol, region)" +echo + +if [[ "$ENV_CHOICE" == "1" ]]; then + ask APP_DOMAIN " Application base domain" "scalekit.local" + APP_PROTOCOL="http" + OIDC_ALLOW_INSECURE="true" +else + ask APP_DOMAIN " Application base domain (e.g. onprem.scalekit.cloud)" + APP_PROTOCOL="https" + OIDC_ALLOW_INSECURE="false" +fi +ask APP_REGION " Deployment region for data residency labelling (e.g. us, eu)" "us" +ask REPLICA_COUNT " Number of pod replicas per service" "2" +echo + +# ── Admin seed user ─────────────────────────────────────────────────────────── +header " [Admin] Seed admin user" +dim " This account is created automatically on first boot. Use it to log in" +dim " to the Scalekit dashboard and set up your workspace." +dim "" +dim " These values will be used to:" +dim " - set seedData.adminUser.* in values.yaml (firstName, lastName, email)" +echo + +ask ADMIN_FIRST_NAME " Admin user first name" +ask ADMIN_LAST_NAME " Admin user last name" +ask ADMIN_EMAIL " Admin user email address (used to log in to the dashboard)" +echo + + +# ── Step 5: Summary ─────────────────────────────────────────────────────────── +header "Step 5 — Review all values" +echo +echo -e "${DIM}Auto-generated:${RESET}" +echo " traefik_token = $TRAEFIK_TOKEN" +echo " oidc_master_key = $OIDC_MASTER_KEY" +echo " securecookie_encryptkey = $SECURECOOKIE_ENCRYPTKEY" +echo " securecookie_hashkey = $SECURECOOKIE_HASHKEY" +echo " svix_jwt_secret = $SVIX_JWT_SECRET" +echo " svix_main_secret = $SVIX_MAIN_SECRET" +echo " svix_api_key = $SVIX_API_KEY" +echo " fga_api_token = $FGA_API_TOKEN" +echo " openfga_extra_key = $OPENFGA_EXTRA_KEY" +echo " app_oidc_client_id = $APP_OIDC_CLIENT_ID" +echo " app_oidc_client_secret = $APP_OIDC_CLIENT_SECRET" +echo +echo -e "${DIM}Database (shared credentials):${RESET}" +echo " host = $DB_HOST:$DB_PORT" +echo " username = $DB_USER" +echo " password = $DATABASE_PASSWORD" +echo " scalekit db = $DB_NAME_SCALEKIT → $DATABASE_URL" +echo " svix db = $DB_NAME_SVIX → $SVIX_DB_DSN" +if [[ "$OPENFGA_ENABLED" == "true" ]]; then + echo " openfga db = $DB_NAME_OPENFGA → $OPENFGA_DB_URI" +fi +echo +echo -e "${DIM}Provided by you:${RESET}" +echo " namespace = $NAMESPACE" +echo " environment = $ENV_LABEL" +echo " redis host:port = $REDIS_HOST:$REDIS_PORT" +echo " redis password = ${REDIS_PASSWORD:-}" +echo " redis.db (main) = $REDIS_DB" +echo " redis.db (asynq) = $ASYNQ_REDIS_DB" +echo " redis.db (svix) = $SVIX_REDIS_DB → $SVIX_REDIS_DSN" +echo " smtp password = $SMTP_PASSWORD" +echo " smtp from = $EMAIL_FROM_NAME <$EMAIL_FROM>" +echo " smtp host:port = $SMTP_HOST:$SMTP_PORT" +echo " smtp username = $SMTP_USERNAME" +echo " app.domain = $APP_DOMAIN" +echo " app.region = $APP_REGION" +echo " app.protocol = $APP_PROTOCOL" +echo " replicaCount = $REPLICA_COUNT" +echo " adminUser = $ADMIN_FIRST_NAME $ADMIN_LAST_NAME <$ADMIN_EMAIL>" +echo " registry_server = $REGISTRY_SERVER" +echo " registry_password = $REGISTRY_PASSWORD" +echo + +# ── Step 6: Write secrets script ───────────────────────────────────────────── +OUTPUT_FILE="$(pwd)/scalekit-secrets-${ENV_LABEL}-$(date +%Y%m%d%H%M%S).sh" + +cat > "$OUTPUT_FILE" <> "$OUTPUT_FILE" < "$VALUES_FILE" < + The Helm chart includes an optional PostgreSQL subchart. Set `postgresql.enabled: true` in your `values.yaml` to spin up a bundled database for evaluation. Do not use this in production. + + + + +## Redis + +| Requirement | Value | +|-------------|-------| +| Version | 6.2 or later | + +Scalekit uses Redis for session storage, token caching, and background job queues. Redis does not need persistence enabled, but enabling RDB snapshots is recommended for production. + + + +## Email (SMTP) + +Scalekit sends transactional email for password resets, magic links, and user invitations. You need an SMTP provider configured before first run. + +Supported providers with first-class integration: **Postmark**, **SendGrid**. Any SMTP server works with the `OTHER` provider type. + +## Node sizing + +| Resource | Production (per node) | +|----------|-----------------------| +| CPU | 4 vCPUs | +| RAM | 8 GB | + +The Scalekit Deployment defaults to 2 replicas with autoscaling up to 20. Each replica includes all sidecar containers. Tune `replicaCount` and HPA settings in your `values.yaml`. + +## Network and DNS + +### DNS + +Create a wildcard DNS record pointing to the external IP assigned to your Gateway: + +``` +*.your-domain.com → +``` + +### TLS + +HTTPS is required. Manage certificates through your cloud provider's certificate manager (for example, GCP Certificate Manager) and reference it via a Gateway annotation. cert-manager is also supported — add the appropriate annotation to `gateway.annotations` in your `values.yaml`. + +## Container registry access + +Scalekit container images are hosted on `ar.scalekit.cloud`. You receive registry credentials through [distr.sh](https://distr.sh) as part of onboarding. The registry token is stored as a Kubernetes image pull secret (`artifact-registry-secret`) during setup. + +## Next step + +Once your infrastructure is ready, proceed to [install Scalekit](/self-hosted/installation/). diff --git a/src/content/docs/self-hosted/troubleshooting.mdx b/src/content/docs/self-hosted/troubleshooting.mdx new file mode 100644 index 000000000..a065e3d8c --- /dev/null +++ b/src/content/docs/self-hosted/troubleshooting.mdx @@ -0,0 +1,150 @@ +--- +title: Troubleshooting +description: Diagnose and fix common issues with a self-hosted Scalekit deployment on Kubernetes. +sidebar: + label: Troubleshooting + order: 6 +tags: [self-hosted, troubleshooting, kubernetes, helm, debugging] +tableOfContents: true +prev: + label: Upgrades & maintenance + link: /self-hosted/upgrades/ +--- + +## Check pod status first + +Before investigating specific errors, confirm which pods are unhealthy: + +```bash +kubectl get pods -n scalekit +kubectl describe pod -n scalekit +kubectl logs -n scalekit --tail=100 +``` + +For the Scalekit pod (which runs multiple containers), specify the container: + +```bash +# Main auth service +kubectl logs -n scalekit -c scalekit --tail=100 + +# Dashboard +kubectl logs -n scalekit -c dashboard --tail=100 + +# Svix +kubectl logs -n scalekit -c svix --tail=100 +``` + +--- + +## Helm install failures + +### `ImagePullBackOff` or `ErrImagePull` + +**Cause**: The cluster cannot pull images from `ar.scalekit.cloud` — usually a missing or incorrect registry secret. + +**Fix**: + +1. Confirm the `artifact-registry-secret` exists: + ```bash + kubectl get secret artifact-registry-secret -n scalekit + ``` +2. If missing, re-run the setup script or recreate it manually: + ```bash + kubectl create secret docker-registry artifact-registry-secret \ + --docker-server=ar.scalekit.cloud \ + --docker-username=oauth2accesstoken \ + --docker-password= \ + -n scalekit + ``` +3. Verify the token is current — registry tokens from distr.sh may expire. + +--- + +### Migration hook fails or times out + +**Cause**: The `db-migrations` pre-install hook cannot connect to PostgreSQL. + +**Fix**: + +1. Check the migration job logs: + ```bash + kubectl get jobs -n scalekit + kubectl logs job/scalekit-db-migrations -n scalekit + ``` +2. Confirm the `DATABASE_URL` in the `db-migrations` secret is correct and the database host is reachable from the cluster. +3. Confirm the database and user exist and the user has full privileges. + +--- + +### Pod stuck in `CrashLoopBackOff` + +**Cause**: A required secret key is missing, or a configuration value is incorrect. + +**Fix**: + +1. Check the pod logs for the startup error: + ```bash + kubectl logs -n scalekit -c scalekit --previous + ``` +2. Common causes: + - Missing key in `authentication-secret` — re-run `setup-secrets.sh` + - `database.host` or `redis.host` in `values.yaml` is unreachable from the cluster + - `domain` in `values.yaml` does not match the gateway hostname + +--- + +## Authentication errors + +### `redirect_uri_mismatch` during login + +**Cause**: The callback URL in the authorization request does not match any registered redirect URL. + +**Fix**: In the admin dashboard, go to **Applications > Your App > Redirect URLs** and add the exact callback URL your application uses. + +--- + +### `invalid_client` on token exchange + +**Cause**: Wrong `client_id` or `client_secret`, or the credentials point to a different environment. + +**Fix**: Check **Dashboard > Developers > API credentials** and confirm `SCALEKIT_ENVIRONMENT_URL` in your application matches your self-hosted domain. + +--- + +## Gateway issues + +### Gateway has no external IP + +**Cause**: The GatewayClass is not installed, or the Gateway controller is not running. + +**Fix**: + +```bash +kubectl get gateway -n scalekit +kubectl describe gateway scalekit -n scalekit +``` + +Check that `gateway.className` in your `values.yaml` matches an installed GatewayClass: + +```bash +kubectl get gatewayclass +``` + +On GKE, confirm the GKE Gateway controller is enabled in your cluster settings. + +--- + +### TLS certificate not provisioning + +**Cause**: The certificate annotation on the Gateway is incorrect, or the certificate manager has not issued the certificate yet. + +**Fix**: Check your cloud provider's certificate manager console for issuance errors. Confirm the `gateway.annotations` value in `values.yaml` uses the correct annotation key and certificate resource name for your provider. + +--- + +## Get support + +If the issue is not covered here: + +- **Enterprise support**: File a ticket via your Scalekit customer portal with pod logs and the output of `kubectl describe pod -n scalekit` attached. +- **Community**: Join the [Scalekit Slack community](https://join.slack.com/t/scalekit-community/shared_invite/zt-3gsxwr4hc-0tvhwT2b_qgVSIZQBQCWRw) in the `#self-hosted` channel. diff --git a/src/content/docs/self-hosted/upgrades.mdx b/src/content/docs/self-hosted/upgrades.mdx new file mode 100644 index 000000000..ac0aac512 --- /dev/null +++ b/src/content/docs/self-hosted/upgrades.mdx @@ -0,0 +1,123 @@ +--- +title: Upgrades & maintenance +description: How to upgrade your self-hosted Scalekit deployment and perform routine maintenance tasks. +sidebar: + label: Upgrades & maintenance + order: 5 +tags: [self-hosted, upgrades, maintenance, helm, kubernetes] +tableOfContents: true +prev: + label: Installation + link: /self-hosted/installation/ +next: + label: Troubleshooting + link: /self-hosted/troubleshooting/ +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +## Upgrade Scalekit + +New Scalekit versions are distributed through [distr.sh](https://distr.sh). When an upgrade is available, download the new chart version from your distr.sh account. + +The chart includes a pre-upgrade Helm hook that runs database migrations before the new pods start. You do not need to run migrations manually. + + + + +1. ## Back up your databases + + ```bash + # Scalekit core database + pg_dump -h -U scalekit -d scalekit \ + -F c -f scalekit-backup-$(date +%Y%m%d).dump + + # Svix database + pg_dump -h -U scalekit -d svix \ + -F c -f svix-backup-$(date +%Y%m%d).dump + ``` + +2. ## Download the new chart version + + Log in to [distr.sh](https://distr.sh) and download the updated chart archive. + +3. ## Run the upgrade + + ```bash + helm upgrade scalekit ./scalekit \ + --namespace scalekit \ + --values values.yaml \ + --wait + ``` + + The pre-upgrade hook runs migrations, then the new pods roll out. `--wait` blocks until the rollout is complete. + +4. ## Verify the upgrade + + ```bash + kubectl get pods -n scalekit + kubectl rollout status deployment/scalekit -n scalekit + ``` + + + +## Roll back an upgrade + +If an upgrade causes issues, roll back to the previous Helm release revision: + +```bash +# List revisions +helm history scalekit -n scalekit + +# Roll back to the previous revision +helm rollback scalekit -n scalekit +``` + +If the upgrade ran schema migrations, you also need to restore your database backup: + +```bash +pg_restore -h -U scalekit -d scalekit \ + -F c scalekit-backup-.dump +``` + +## Back up your databases + +| Database | Frequency (production) | Retention | +|----------|----------------------|-----------| +| `scalekit` | Every 6 hours | 30 days | +| `svix` | Daily | 7 days | +| `openfga` | Daily | 7 days | + +```bash +# Full backup of all three databases +for db in scalekit svix openfga; do + pg_dump -h -U scalekit -d $db \ + -F c -f ${db}-$(date +%Y%m%d-%H%M).dump +done +``` + +## Renew TLS certificates + +When your TLS certificate is due for renewal, update the Kubernetes secret and Scalekit will pick up the new certificate on the next pod restart: + +```bash +kubectl create secret tls your-tls-secret \ + --cert=new-cert.pem \ + --key=new-key.pem \ + -n scalekit \ + --dry-run=client -o yaml | kubectl apply -f - + +kubectl rollout restart deployment/scalekit -n scalekit +``` + +## Routine maintenance tasks + +| Task | Frequency | Action | +|------|-----------|--------| +| Review auth logs | Daily | Admin dashboard → **Audit logs** | +| Check pod health | Daily | `kubectl get pods -n scalekit` | +| Rotate Redis cache | As needed | Automatic — expired keys are evicted by Redis TTL | +| Vacuum PostgreSQL | Weekly | Run `VACUUM ANALYZE` on each Scalekit database | +| Rotate secrets | Per policy | Re-run `setup-secrets.sh` with new values and restart pods | From 71d7eb5e241d273385c359145d5ffdb76fc65c03 Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Mon, 20 Apr 2026 17:56:56 +0530 Subject: [PATCH 02/12] On prem documentation base --- src/configs/self-hosted.ts | 10 ++ .../docs/self-hosted/configuration.mdx | 9 +- src/content/docs/self-hosted/installation.mdx | 105 ++++++++++++++---- src/content/docs/self-hosted/overview.mdx | 11 +- src/content/docs/self-hosted/setup-script.mdx | 3 +- .../docs/self-hosted/system-requirements.mdx | 15 +-- .../docs/self-hosted/troubleshooting.mdx | 13 +-- src/content/docs/self-hosted/upgrades.mdx | 63 ++++++----- 8 files changed, 150 insertions(+), 79 deletions(-) create mode 100644 src/configs/self-hosted.ts diff --git a/src/configs/self-hosted.ts b/src/configs/self-hosted.ts new file mode 100644 index 000000000..888acd83a --- /dev/null +++ b/src/configs/self-hosted.ts @@ -0,0 +1,10 @@ +/** + * Self-hosted deployment URL constants. + * Update these values here when hostnames change — they propagate to all self-hosted docs. + */ + +/** Container image registry hostname */ +export const REGISTRY_HOST = 'ar.scalekit.cloud' + +/** Scalekit distribution portal URL (Helm chart and registry token source) */ +export const DISTR_URL = 'https://repack.scalekit.cloud' diff --git a/src/content/docs/self-hosted/configuration.mdx b/src/content/docs/self-hosted/configuration.mdx index 388c6a61f..25a13a7a3 100644 --- a/src/content/docs/self-hosted/configuration.mdx +++ b/src/content/docs/self-hosted/configuration.mdx @@ -15,14 +15,13 @@ next: --- import { Tabs, TabItem, Aside } from '@astrojs/starlight/components' +import { REGISTRY_HOST } from '../../../configs/self-hosted' -Scalekit is configured through a `values.yaml` file passed to Helm at install and upgrade time. This page shows two complete examples followed by a field-by-field reference. - -The full schema is in `scalekit/values.schema.json` inside the chart package. +Scalekit is configured through a `values.yaml` file. The [setup script](/self-hosted/setup-script/) generates this file for you — use the examples and field reference below to understand the structure or to make changes after initial setup. ## Complete examples -Pick the example that matches your setup. Replace placeholder values before running `helm install`. +These examples show the full structure of a generated `values.yaml`. The setup script produces this file automatically — refer here when reviewing or modifying values after initial setup. @@ -306,7 +305,7 @@ All sensitive values are stored in Kubernetes secrets, not in `values.yaml`. The | `authentication-secret` | DB password, Redis password, OIDC keys, cookie keys, email keys, Svix API key | | `svix-secrets` | `db-dsn`, `jwt-secret`, `main-secret`, `redis-dsn`, `api-token` | | `openfga-secrets` | `keys`, `password`, `uri`, `username` | -| `artifact-registry-secret` | Docker registry credentials for `ar.scalekit.cloud` | +| `artifact-registry-secret` | Docker registry credentials for {REGISTRY_HOST} | ## Next steps 1. [System requirements](/self-hosted/system-requirements/) — verify your infrastructure before starting -2. [Install Scalekit](/self-hosted/installation/) — deploy the Helm chart on your cluster -3. [Configure Scalekit](/self-hosted/configuration/) — set your domain, database, and email settings +2. [Configure Scalekit](/self-hosted/configuration/) — understand the values.yaml structure +3. [Install Scalekit](/self-hosted/installation/) — deploy through the Scalekit distribution portal diff --git a/src/content/docs/self-hosted/setup-script.mdx b/src/content/docs/self-hosted/setup-script.mdx index f6b2dff58..1b6f700f6 100644 --- a/src/content/docs/self-hosted/setup-script.mdx +++ b/src/content/docs/self-hosted/setup-script.mdx @@ -15,6 +15,7 @@ tableOfContents: true --- import { Aside } from '@astrojs/starlight/components' +import { REGISTRY_HOST, DISTR_URL } from '../../../configs/self-hosted' The setup script is a **one-time tool for initial deployment**. Run it once to bootstrap your cluster — do not run it again on an existing installation. @@ -76,7 +77,7 @@ The script walks through these sections: | **PostgreSQL** | Host, port, credentials, and database names | | **Redis** | Host, port, password, and db indexes for app, background jobs, and Svix | | **Email (SMTP)** | From address, host, port, username, and password | -| **Container registry** | Registry token from distr.sh | +| **Container registry** | Registry token from the Scalekit distribution portal; server defaults to {REGISTRY_HOST} | | **Gateway** | GatewayClass name and GCP certificate map | | **App settings** | Domain, region | | **Admin user** | First name, last name, email for the initial dashboard login | diff --git a/src/content/docs/self-hosted/system-requirements.mdx b/src/content/docs/self-hosted/system-requirements.mdx index e18fc4b6c..c172ec83d 100644 --- a/src/content/docs/self-hosted/system-requirements.mdx +++ b/src/content/docs/self-hosted/system-requirements.mdx @@ -10,11 +10,12 @@ prev: label: Overview link: /self-hosted/overview/ next: - label: Install Scalekit - link: /self-hosted/installation/ + label: Configure Scalekit + link: /self-hosted/configuration/ --- import { Aside } from '@astrojs/starlight/components' +import { REGISTRY_HOST, DISTR_URL } from '../../../configs/self-hosted' Before installing Scalekit, confirm your infrastructure meets the requirements below. @@ -89,11 +90,7 @@ The Scalekit Deployment defaults to 2 replicas with autoscaling up to 20. Each r ### DNS -Create a wildcard DNS record pointing to the external IP assigned to your Gateway: - -``` -*.your-domain.com → -``` +A wildcard DNS record pointing to your Gateway's external IP is required. You set this up after the deployment is running — see [Install Scalekit](/self-hosted/installation/#update-dns). ### TLS @@ -101,8 +98,8 @@ HTTPS is required. Manage certificates through your cloud provider's certificate ## Container registry access -Scalekit container images are hosted on `ar.scalekit.cloud`. You receive registry credentials through [distr.sh](https://distr.sh) as part of onboarding. The registry token is stored as a Kubernetes image pull secret (`artifact-registry-secret`) during setup. +Scalekit container images are hosted on {REGISTRY_HOST}. You receive registry credentials through the Scalekit distribution portal as part of onboarding. The registry token is stored as a Kubernetes image pull secret (`artifact-registry-secret`) during setup. ## Next step -Once your infrastructure is ready, proceed to [install Scalekit](/self-hosted/installation/). +Once your infrastructure is ready, proceed to [configure Scalekit](/self-hosted/configuration/). diff --git a/src/content/docs/self-hosted/troubleshooting.mdx b/src/content/docs/self-hosted/troubleshooting.mdx index a065e3d8c..42d312ee5 100644 --- a/src/content/docs/self-hosted/troubleshooting.mdx +++ b/src/content/docs/self-hosted/troubleshooting.mdx @@ -11,6 +11,11 @@ prev: link: /self-hosted/upgrades/ --- +import { Code } from '@astrojs/starlight/components' +import { REGISTRY_HOST } from '../../../configs/self-hosted' + +export const registrySecretCmd = `kubectl create secret docker-registry artifact-registry-secret \\\n --docker-server=${REGISTRY_HOST} \\\n --docker-username=oauth2accesstoken \\\n --docker-password= \\\n -n ` + ## Check pod status first Before investigating specific errors, confirm which pods are unhealthy: @@ -49,13 +54,7 @@ kubectl logs -n scalekit -c svix --tail=100 kubectl get secret artifact-registry-secret -n scalekit ``` 2. If missing, re-run the setup script or recreate it manually: - ```bash - kubectl create secret docker-registry artifact-registry-secret \ - --docker-server=ar.scalekit.cloud \ - --docker-username=oauth2accesstoken \ - --docker-password= \ - -n scalekit - ``` + 3. Verify the token is current — registry tokens from distr.sh may expire. --- diff --git a/src/content/docs/self-hosted/upgrades.mdx b/src/content/docs/self-hosted/upgrades.mdx index ac0aac512..d8b0611f2 100644 --- a/src/content/docs/self-hosted/upgrades.mdx +++ b/src/content/docs/self-hosted/upgrades.mdx @@ -3,7 +3,7 @@ title: Upgrades & maintenance description: How to upgrade your self-hosted Scalekit deployment and perform routine maintenance tasks. sidebar: label: Upgrades & maintenance - order: 5 + order: 7 tags: [self-hosted, upgrades, maintenance, helm, kubernetes] tableOfContents: true prev: @@ -15,15 +15,14 @@ next: --- import { Steps, Aside } from '@astrojs/starlight/components' +import { DISTR_URL } from '../../../configs/self-hosted' ## Upgrade Scalekit -New Scalekit versions are distributed through [distr.sh](https://distr.sh). When an upgrade is available, download the new chart version from your distr.sh account. +New versions are available through the Scalekit distribution portal. Upgrades are performed through the portal — it handles chart delivery and runs database migrations automatically before the new pods start. -The chart includes a pre-upgrade Helm hook that runs database migrations before the new pods start. You do not need to run migrations manually. - - -## Next steps +## Choose your path -1. [System requirements](/self-hosted/system-requirements/) — verify your infrastructure before starting -2. [Configure Scalekit](/self-hosted/configuration/) — understand the values.yaml structure -3. [Install Scalekit](/self-hosted/installation/) — deploy through the Scalekit distribution portal +| Path | When to use | +|------|-------------| +| **[Quick start](/self-hosted/quickstart/)** | Get Scalekit running fast using bundled PostgreSQL and Redis. Ideal for evaluation and proof-of-concept. No external databases or `kubectl secret` commands needed. | +| **[Full installation](/self-hosted/system-requirements/)** | Production deployment with external PostgreSQL and Redis, full secret management, and your choice of ingress controller. | diff --git a/src/content/docs/self-hosted/quickstart.mdx b/src/content/docs/self-hosted/quickstart.mdx new file mode 100644 index 000000000..833559106 --- /dev/null +++ b/src/content/docs/self-hosted/quickstart.mdx @@ -0,0 +1,206 @@ +--- +title: Quick start +description: Deploy Scalekit on Kubernetes in minutes using bundled databases and a single values.yaml file. +sidebar: + label: Quick start + order: 2 + prev: + label: Overview + link: /self-hosted/overview/ + next: + label: System requirements + link: /self-hosted/system-requirements/ +tags: [self-hosted, quickstart, kubernetes, helm] +tableOfContents: true +--- + +import { Steps, Aside } from '@astrojs/starlight/components' +import { REGISTRY_HOST, DISTR_URL } from '../../../configs/self-hosted' + +Get Scalekit running on Kubernetes with a single `helm install`. This guide uses bundled PostgreSQL and Redis and auto-creates Kubernetes secrets from your `values.yaml` — no external databases or `kubectl secret` commands needed. + + + +## Before you start + +| Requirement | Notes | +|-------------|-------| +| `kubectl` 1.27+ | Configured against your target cluster | +| Helm 3.12+ | Run `helm version` to verify | +| Domain | Subdomains `app.` and `auth.` must resolve to your cluster | +| Gateway class | A GatewayClass must be installed on your cluster | +| TLS certificate | Attached via a gateway annotation (GCP cert map, cert-manager, etc.) | +| SMTP credentials | Any provider; Postmark and SendGrid have first-class support | +| Registry token | From the Scalekit distribution portal — see step 1 | +| `openssl` and `python3` | To generate webhook credentials in step 2 | + +--- + + + +1. ## Get a registry token + + Log in to the Scalekit distribution portal and create a Personal Access Token. This token authenticates your cluster to pull Scalekit images from {REGISTRY_HOST}. + + + + Copy the token — it is shown only once. + +2. ## Generate webhook credentials + + Run these commands to produce a JWT secret and a signed API token. Copy both output lines — you will paste them into `values.yaml` in the next step. + + ```bash frame="terminal" + export JWT_SECRET=$(openssl rand -base64 32) + echo "jwtSecret: $JWT_SECRET" + + python3 << 'EOF' + import base64, hashlib, hmac as _hmac, json, os, secrets, string, time + + secret = os.environ['JWT_SECRET'] + chars = string.ascii_letters + string.digits + sub = 'org_' + ''.join(secrets.choice(chars) for _ in range(22)) + now = int(time.time()) + exp = now + 315360000 + + h = base64.urlsafe_b64encode(json.dumps({'alg':'HS256','typ':'JWT'}, separators=(',',':')).encode()).rstrip(b'=').decode() + p = base64.urlsafe_b64encode(json.dumps({'iat':now,'exp':exp,'nbf':now,'iss':'svix-server','sub':sub}, separators=(',',':')).encode()).rstrip(b'=').decode() + msg = f'{h}.{p}' + sig = base64.urlsafe_b64encode(_hmac.new(secret.encode(), msg.encode(), hashlib.sha256).digest()).rstrip(b'=').decode() + print(f'apiToken: {msg}.{sig}') + EOF + ``` + + The output looks like: + + ``` + jwtSecret: aB3cD4eF5gH6... + apiToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + ``` + +3. ## Create values.yaml + + Create a `values.yaml` file using the template below. Replace each placeholder with your actual value. + + ```yaml title="values.yaml" + scalekit: + config: + app: + domain: "" # e.g. scalekit.example.com — no scheme or trailing slash + seedData: + adminUser: + firstName: "" + lastName: "" + email: "" + emailServer: + settings: + fromEmail: "hi@" + fromName: "Team " + host: "" + port: + username: "" + + postgresql: + enabled: true + + redis: + enabled: true + + secrets: + create: true + svix: + jwtSecret: "" + apiToken: "" + registry: + password: "" + + gateway: + enabled: true + provider: "" # gcp for GKE; other for all other clusters + className: "" # e.g. gke-l7-global-external-managed + annotations: + : "" # e.g. networking.gke.io/certmap: your-cert-map + redirectToHttps: true + healthCheckPolicy: + enabled: true # GKE only — set false for other providers + ``` + + | Field | Description | + |-------|-------------| + | `domain` | Base domain. The chart derives `app.` and `auth.` from this. | + | `adminUser` | First admin account created on first boot. Use this to log in to the dashboard. | + | `emailServer` | SMTP settings for transactional email (invites, magic links, verification codes). The password is provided separately via `secrets.registry.password` — see [full configuration](/self-hosted/configuration/) for details. | + | `secrets.create` | When `true`, the chart creates all required Kubernetes secrets from the values below. | + | `secrets.svix` | Webhook service credentials generated in step 2. | + | `secrets.registry.password` | Registry access token from step 1. | + | `gateway.provider` | Set to `gcp` for GKE to enable GKE-specific resources. Leave empty for all other providers. | + | `gateway.className` | The GatewayClass installed on your cluster. | + | `gateway.annotations` | Attach a TLS certificate via your provider's annotation. | + +4. ## Create a deployment in the portal + + Deployments are managed through the Scalekit distribution portal. + + Create the namespace on your cluster first: + + ```bash + kubectl create namespace + ``` + + Then, in the portal: + + - In the left sidebar, click **Deployments**, then **+ New Deployment** + - Select **Scalekit Onprem** and click **Continue** + - Set a **Deployment Name** (`scalekit` is recommended) and enter the **Kubernetes Namespace** you just created + - Leave **Enable cluster-scoped permissions** checked and click **Continue** + - Select the **Version** you want to install + - Under **Helm values**, paste the full contents of your `values.yaml` + - Click **Create Deployment** + + The portal shows a **Deployment Created Successfully** screen with a `kubectl apply` command. Click **Copy Command** and run it on your cluster: + + ```bash + kubectl apply -n -f "" + ``` + + This connects your cluster to the portal, which then installs the Helm chart and runs database migrations. + +5. ## Update DNS + + Once the gateway is provisioned, get its external IP: + + ```bash + kubectl get gateway -n scalekit + ``` + + Copy the address from the `ADDRESS` column. In your DNS provider, create a wildcard `A` record: + + ``` + *. + ``` + + DNS propagation can take a few minutes. Verify with: + + ```bash + dig app. + ``` + +6. ## Verify + + ```bash + kubectl get pods -n scalekit + ``` + + All pods should show `Running`. Open `https://app.` in a browser and sign in with the admin email you set in `values.yaml`. + + + +## Next steps + +- Ready for production? Switch to external databases and full secret management — follow the [installation guide](/self-hosted/installation/). +- Review every configuration field in the [configuration reference](/self-hosted/configuration/). +- Learn how to upgrade and maintain your deployment in [upgrades & maintenance](/self-hosted/upgrades/). diff --git a/src/content/docs/self-hosted/setup-script.mdx b/src/content/docs/self-hosted/setup-script.mdx index 1b6f700f6..d34092b5a 100644 --- a/src/content/docs/self-hosted/setup-script.mdx +++ b/src/content/docs/self-hosted/setup-script.mdx @@ -21,8 +21,8 @@ The setup script is a **one-time tool for initial deployment**. Run it once to b It collects your configuration interactively and produces two output files: -- **A secrets script** (`scalekit-secrets-.sh`) — runs `kubectl` commands to create all six required Kubernetes secrets -- **A values file** (`values-.yaml`) — ready to pass directly to `helm install` +- **A secrets script** (`scalekit-secrets--.sh`) — runs `kubectl` commands to create all five required Kubernetes secrets +- **A values file** (`values--.yaml`) — ready to pass directly to `helm install` At the end it prints the exact `helm install` command to run. @@ -32,7 +32,7 @@ At the end it prints the exact `helm install` command to run. |------|---------|---------| | `bash` | 4.0 or later | Run the script | | `openssl` | Any modern version | Generate cryptographic keys and tokens | -| `python3` | 3.6 or later | Generate Svix JWT and OIDC client secret | +| `python3` | 3.6 or later | Generate webhook JWT and OIDC client secret | | `kubectl` | 1.27 or later | Create Kubernetes secrets in your cluster | `kubectl` must be configured and pointed at the cluster you are deploying to before you run the script. @@ -46,7 +46,7 @@ brew install bash ## Run the script @@ -58,7 +58,20 @@ chmod +x setup-secrets.sh bash setup-secrets.sh ``` -When prompted to choose an environment, enter `2` for a Kubernetes cluster deployment. +When prompted to choose an environment, enter the number that matches your target: + +| Option | Environment | Notes | +|--------|-------------|-------| +| `1` | Minikube (local) | Uses nginx ingress; sets `http` protocol and `allow_insecure: true` | +| `2` | GCP / GKE | Configures GKE Gateway API and NEG annotations | +| `3` | Other Kubernetes cluster | Generic config — you add your own ingress or gateway | +| `4` | Evaluation | Fast path: Helm spins up bundled PostgreSQL and Redis; only asks for webhook and registry credentials | + +### Evaluation mode + +Option `4` is a shortcut for a local or throwaway environment. The script asks only for a webhook JWT secret, a webhook API token, and a registry access token, then exits. It generates a minimal `values-eval-.yaml` and prints a single `helm install` command. No databases or Redis instances are needed — the chart provides bundled ones. + +**Do not use evaluation mode in production.** The bundled databases have no backups, no replication, and no persistent storage guarantees. ### Optional flags @@ -69,20 +82,33 @@ When prompted to choose an environment, enter `2` for a Kubernetes cluster deplo ## What the script collects +### Full setup (options 1, 2, 3) + The script walks through these sections: | Section | What it asks | |---------|-------------| | **Namespace** | Kubernetes namespace to deploy into | -| **PostgreSQL** | Host, port, credentials, and database names | -| **Redis** | Host, port, password, and db indexes for app, background jobs, and Svix | +| **Environment** | Deployment target: Minikube, GCP/GKE, other K8s, or Evaluation | +| **PostgreSQL** | Host, port, credentials, and database names (scalekit, webhooks, openfga if enabled) | +| **Redis** | Host, port, password, and db indexes for app, background jobs, and webhooks | | **Email (SMTP)** | From address, host, port, username, and password | | **Container registry** | Registry token from the Scalekit distribution portal; server defaults to {REGISTRY_HOST} | -| **Gateway** | GatewayClass name and GCP certificate map | -| **App settings** | Domain, region | +| **GKE Gateway** | GatewayClass name and GCP certificate map (GCP/GKE only) | +| **App settings** | Domain, region, replica count | | **Admin user** | First name, last name, email for the initial dashboard login | -All cryptographic values (OIDC keys, cookie keys, Svix JWT, etc.) are auto-generated — you do not supply these. +All cryptographic values (OIDC keys, cookie keys, webhook JWT, etc.) are auto-generated — you do not supply these. + +### Evaluation mode (option 4) + +The script only asks for: + +| Section | What it asks | +|---------|-------------| +| **Namespace** | Kubernetes namespace to deploy into | +| **Webhook credentials** | JWT secret and API token | +| **Container registry** | Registry access token | ## After the script completes @@ -137,6 +163,7 @@ dim() { echo "${DIM}$*${RESET}"; } ask() { local var="$1" msg="$2" default="${3:-}" + # If a default exists and --change-defaults is not set, use it silently if [[ -n "$default" && "$CHANGE_DEFAULTS" == "false" ]]; then eval "$var=\"$default\"" dim " $msg = $default (default)" @@ -162,6 +189,7 @@ ask() { ask_secret() { local var="$1" msg="$2" default="${3:-}" + # If the 3rd argument was explicitly passed (even as ""), empty input is allowed local allow_empty="${3+yes}" while true; do if [[ -n "$default" ]]; then @@ -190,9 +218,72 @@ ask NAMESPACE "Kubernetes namespace to deploy Scalekit into" echo echo -e "${YELLOW}Which environment are you deploying to?${RESET}" echo " 1) Minikube (local)" -echo " 2) Kubernetes cluster (GKE / cloud)" -read -rp "${YELLOW}Enter 1 or 2: ${RESET}" ENV_CHOICE -ENV_LABEL=$([[ "$ENV_CHOICE" == "1" ]] && echo "minikube" || echo "gke") +echo " 2) GCP / GKE" +echo " 3) Other Kubernetes cluster" +echo " 4) Evaluation (quickstart — Helm brings up PostgreSQL & Redis)" +read -rp "${YELLOW}Enter 1, 2, 3 or 4: ${RESET}" ENV_CHOICE +if [[ "$ENV_CHOICE" == "1" ]]; then + ENV_LABEL="minikube" +elif [[ "$ENV_CHOICE" == "2" ]]; then + ENV_LABEL="gke" +elif [[ "$ENV_CHOICE" == "4" ]]; then + ENV_LABEL="eval" +else + ENV_LABEL="k8s" +fi + +# ── Evaluation flow (early exit) ────────────────────────────────────────────── +if [[ "$ENV_CHOICE" == "4" ]]; then + header "Step 2 — Evaluation setup" + dim " Helm will spin up PostgreSQL and Redis automatically." + dim " You only need a Svix API token and registry credentials." + echo + + ask_secret SVIX_JWT_SECRET " Svix JWT secret (must be the secret used to sign the API token)" + ask_secret SVIX_API_KEY " Svix API token (JWT signed with the above secret)" + ask_secret REGISTRY_PASSWORD " Registry access token" + echo + + VALUES_FILE="$(pwd)/values-eval-$(date +%Y%m%d%H%M%S).yaml" + cat > "$VALUES_FILE" <}" echo " redis.db (main) = $REDIS_DB" echo " redis.db (asynq) = $ASYNQ_REDIS_DB" echo " redis.db (svix) = $SVIX_REDIS_DB → $SVIX_REDIS_DSN" +echo " email_key = na (fixed)" echo " smtp password = $SMTP_PASSWORD" +echo " sendgrid_key = na (fixed)" echo " smtp from = $EMAIL_FROM_NAME <$EMAIL_FROM>" echo " smtp host:port = $SMTP_HOST:$SMTP_PORT" echo " smtp username = $SMTP_USERNAME" @@ -455,49 +552,49 @@ cat > "$OUTPUT_FILE" <> "$OUTPUT_FILE" < "$VALUES_FILE" < "$VALUES_FILE" < "$VALUES_FILE" < "$VALUES_FILE" <> /etc/hosts'" + echo + dim " Or add manually — open /etc/hosts and append this line:" + echo " 127.0.0.1 app.${APP_DOMAIN} auth.${APP_DOMAIN}" + echo + dim " 5. Update CoreDNS so pods inside the cluster can resolve app.${APP_DOMAIN} and auth.${APP_DOMAIN}:" + echo + dim " Open the CoreDNS ConfigMap and find the IP already assigned to host.minikube.internal." + dim " Add two more entries pointing to that same IP:" + echo + echo " kubectl edit configmap coredns -n kube-system" + echo + echo " # Inside the hosts { } block, add:" + echo " app.${APP_DOMAIN}" + echo " auth.${APP_DOMAIN}" + echo + dim " Then restart CoreDNS to apply:" + echo " kubectl rollout restart deployment coredns -n kube-system" + echo + echo "${BOLD}${CYAN}└─────────────────────────────────────────────────────────────┘${RESET}" +fi ``` diff --git a/src/content/docs/self-hosted/system-requirements.mdx b/src/content/docs/self-hosted/system-requirements.mdx index c172ec83d..855ec10a8 100644 --- a/src/content/docs/self-hosted/system-requirements.mdx +++ b/src/content/docs/self-hosted/system-requirements.mdx @@ -3,12 +3,12 @@ title: System requirements description: Kubernetes, database, and network requirements for a self-hosted Scalekit deployment. sidebar: label: System requirements - order: 2 + order: 3 tags: [self-hosted, requirements, kubernetes, prerequisites] tableOfContents: true prev: - label: Overview - link: /self-hosted/overview/ + label: Quick start + link: /self-hosted/quickstart/ next: label: Configure Scalekit link: /self-hosted/configuration/ @@ -46,7 +46,7 @@ Scalekit requires **three separate databases** on PostgreSQL 15 or later. These | Database | Used by | |----------|---------| | `scalekit` | Core auth service | -| `svix` | Webhook delivery (Svix) | +| `svix` | Webhook delivery | | `openfga` | Authorization engine (if enabled) | Create a dedicated user with full privileges on each database. Scalekit runs migrations automatically on install and upgrade. From e46098048db5673da867a64cf8f7b3ac55299ae9 Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Tue, 21 Apr 2026 19:00:46 +0530 Subject: [PATCH 05/12] Update to the documentation --- .../docs/self-hosted/configuration.mdx | 35 +++++++++++-------- src/content/docs/self-hosted/installation.mdx | 2 +- src/content/docs/self-hosted/overview.mdx | 2 +- .../docs/self-hosted/system-requirements.mdx | 9 +++-- src/content/docs/self-hosted/upgrades.mdx | 10 +++--- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/content/docs/self-hosted/configuration.mdx b/src/content/docs/self-hosted/configuration.mdx index 2e823441e..f9714fb1e 100644 --- a/src/content/docs/self-hosted/configuration.mdx +++ b/src/content/docs/self-hosted/configuration.mdx @@ -167,7 +167,10 @@ scalekit: port: 5432 ``` -The database password is stored in the `authentication-secret` Kubernetes secret (`database_password` key), not in `values.yaml`. +When using external PostgreSQL (`postgresql.enabled: false`), the database password is injected via Kubernetes secret: + +- **`secrets.create: true`** — provide it under `secrets.database.password` in `values.yaml`; the chart creates the secret automatically +- **`secrets.create: false`** — pre-create the `authentication-secret` with a `database_password` key using the setup script Omit this section entirely when using the bundled PostgreSQL subchart (`postgresql.enabled: true`). @@ -182,7 +185,10 @@ scalekit: db: 0 ``` -The Redis password is stored in the `authentication-secret` Kubernetes secret (`redis_password` key). +When using external Redis (`redis.enabled: false`), the Redis password and DSN are injected via Kubernetes secret: + +- **`secrets.create: true`** — provide the DSN under `secrets.svix.redisDsn` in `values.yaml`; the chart creates the secret automatically +- **`secrets.create: false`** — pre-create the `svix-secrets` with a `redis-dsn` key using the setup script Omit this section entirely when using the bundled Redis subchart (`redis.enabled: true`). @@ -211,7 +217,7 @@ scalekit: ``` ### Gateway @@ -298,17 +304,16 @@ scalekit: ## Secrets reference -All sensitive values are stored in Kubernetes secrets, not in `values.yaml`. The setup script (`setup-secrets.sh`) creates these for you. +Scalekit uses Kubernetes secrets to inject all sensitive values into pods. There are two ways these secrets are created: -| Secret name | Key fields | -|-------------|------------| -| `authentication-service-token` | `TOKEN` — dashboard auth token | -| `db-migrations` | `DATABASE_URL`, `DB_ADAPTER` | -| `authentication-secret` | DB password, Redis password, OIDC keys, cookie keys, email keys, webhook API key | -| `svix-secrets` | `db-dsn`, `jwt-secret`, `main-secret`, `redis-dsn`, `api-token` | -| `openfga-secrets` | `keys`, `password`, `uri`, `username` | -| `artifact-registry-secret` | Docker registry credentials for {REGISTRY_HOST} | +- **`secrets.create: true`** (quick start) — the chart auto-creates all secrets from values you provide in `values.yaml` under the `secrets.*` block +- **`secrets.create: false`** (full deployment) — you pre-create the secrets using the setup script - +| Secret name | Key fields | Created by | +|-------------|------------|------------| +| `authentication-service-token` | `TOKEN` — dashboard auth token | Chart or setup script | +| `db-migrations` | `DATABASE_URL`, `DB_ADAPTER` | Chart or setup script | +| `authentication-secret` | DB password, Redis password, OIDC keys, cookie keys, email keys, webhook API key | Chart or setup script | +| `svix-secrets` | `db-dsn`, `jwt-secret`, `main-secret`, `redis-dsn`, `api-token` | Chart or setup script | +| `artifact-registry-secret` | Docker registry credentials for {REGISTRY_HOST} | Chart or setup script | +| `openfga-secrets` | `keys`, `uri` | Chart or setup script — only when `sidecars.openfga.enabled: true` | diff --git a/src/content/docs/self-hosted/installation.mdx b/src/content/docs/self-hosted/installation.mdx index 2b5be398e..261d919a0 100644 --- a/src/content/docs/self-hosted/installation.mdx +++ b/src/content/docs/self-hosted/installation.mdx @@ -152,7 +152,7 @@ Deploy Scalekit on any Kubernetes cluster using the Helm chart distributed throu 5. ## Update DNS diff --git a/src/content/docs/self-hosted/overview.mdx b/src/content/docs/self-hosted/overview.mdx index 09e528a5f..fbd2c3519 100644 --- a/src/content/docs/self-hosted/overview.mdx +++ b/src/content/docs/self-hosted/overview.mdx @@ -46,7 +46,7 @@ Scalekit does not bundle a database or cache in production. You provision and ma | Dependency | Requirement | Notes | |------------|-------------|-------| | **Kubernetes** | 1.27 or later | Any managed or self-managed cluster | -| **PostgreSQL** | 15 or later | Three databases required: `scalekit`, `svix`, `openfga` | +| **PostgreSQL** | 15 or later | Three databases required: `scalekit`, `webhooks`, `openfga` | | **Redis** | 6.2 or later | Used for sessions, caching, and job queues | | **SMTP** | Any provider | Postmark and SendGrid have first-class support | diff --git a/src/content/docs/self-hosted/system-requirements.mdx b/src/content/docs/self-hosted/system-requirements.mdx index 855ec10a8..a096f8483 100644 --- a/src/content/docs/self-hosted/system-requirements.mdx +++ b/src/content/docs/self-hosted/system-requirements.mdx @@ -29,7 +29,12 @@ Before installing Scalekit, confirm your infrastructure meets the requirements b Any managed Kubernetes service works — GKE, EKS, AKS, or a self-managed cluster. -Scalekit uses the **Kubernetes Gateway API** for ingress. Your cluster must have a GatewayClass installed: +Scalekit supports two ingress options — use one, not both: + +- **Kubernetes Gateway API** (recommended for cloud clusters): requires a GatewayClass installed on your cluster +- **Nginx Ingress** (for local or Minikube deployments): requires the nginx ingress controller + +For Gateway API, common GatewayClasses: | Provider | GatewayClass | |----------|-------------| @@ -46,7 +51,7 @@ Scalekit requires **three separate databases** on PostgreSQL 15 or later. These | Database | Used by | |----------|---------| | `scalekit` | Core auth service | -| `svix` | Webhook delivery | +| `webhooks` | Webhook delivery | | `openfga` | Authorization engine (if enabled) | Create a dedicated user with full privileges on each database. Scalekit runs migrations automatically on install and upgrade. diff --git a/src/content/docs/self-hosted/upgrades.mdx b/src/content/docs/self-hosted/upgrades.mdx index d8b0611f2..75807a12a 100644 --- a/src/content/docs/self-hosted/upgrades.mdx +++ b/src/content/docs/self-hosted/upgrades.mdx @@ -33,9 +33,9 @@ New versions are available through the Scalekit distribution pg_dump -h -U -d scalekit \ -F c -f scalekit-backup-$(date +%Y%m%d).dump - # Svix database - pg_dump -h -U -d svix \ - -F c -f svix-backup-$(date +%Y%m%d).dump + # Webhooks database + pg_dump -h -U -d webhooks \ + -F c -f webhooks-backup-$(date +%Y%m%d).dump ``` 2. ## Select the new version in the portal @@ -91,12 +91,12 @@ pg_restore -h -U -d scalekit \ | Database | Frequency (production) | Retention | |----------|----------------------|-----------| | `scalekit` | Every 6 hours | 30 days | -| `svix` | Daily | 7 days | +| `webhooks` | Daily | 7 days | | `openfga` | Daily | 7 days | ```bash # Full backup of all three databases -for db in scalekit svix openfga; do +for db in scalekit webhooks openfga; do pg_dump -h -U -d $db \ -F c -f ${db}-$(date +%Y%m%d-%H%M).dump done From 9fa3d7daa506d1e4b8bdadb772110e08c3ad19a1 Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Tue, 21 Apr 2026 19:23:15 +0530 Subject: [PATCH 06/12] Update --- src/content/docs/self-hosted/upgrades.mdx | 69 +++-------------------- 1 file changed, 9 insertions(+), 60 deletions(-) diff --git a/src/content/docs/self-hosted/upgrades.mdx b/src/content/docs/self-hosted/upgrades.mdx index 75807a12a..141800138 100644 --- a/src/content/docs/self-hosted/upgrades.mdx +++ b/src/content/docs/self-hosted/upgrades.mdx @@ -19,26 +19,10 @@ import { DISTR_URL } from '../../../configs/self-hosted' ## Upgrade Scalekit -New versions are available through the Scalekit distribution portal. Upgrades are performed through the portal — it handles chart delivery and runs database migrations automatically before the new pods start. - - +New versions are available through the Scalekit distribution portal. Upgrades are performed through the portal — the Distr agent running in your cluster picks up the change and applies it automatically. Database migrations run before the new pods start. -1. ## Back up your databases - - ```bash - # Scalekit core database - pg_dump -h -U -d scalekit \ - -F c -f scalekit-backup-$(date +%Y%m%d).dump - - # Webhooks database - pg_dump -h -U -d webhooks \ - -F c -f webhooks-backup-$(date +%Y%m%d).dump - ``` - -2. ## Select the new version in the portal +1. ## Select the new version in the portal Log in to the Scalekit distribution portal. In the left sidebar, click **Deployments** and select your deployment. @@ -46,19 +30,17 @@ New versions are available through the Scalekit distribution - Under **Version**, select the new version - Click **Update deployment** - The portal provides a `kubectl apply` command. Copy it. + The portal notifies the Distr agent in your cluster. No manual `kubectl` command is needed. -3. ## Apply the update to your cluster +2. ## Monitor the upgrade - Run the command from the portal on your cluster: + The Distr agent handles chart delivery and migration sequencing. Check its logs to follow progress: ```bash - kubectl apply -n -f "" + kubectl logs -l app=distr-agent -n --tail=100 -f ``` - Migrations run automatically before the new pods start. - -4. ## Verify the upgrade +3. ## Verify the upgrade ```bash kubectl get pods -n @@ -79,42 +61,9 @@ helm history scalekit -n helm rollback scalekit -n ``` -If the upgrade ran schema migrations, restore your database backup as well: - -```bash -pg_restore -h -U -d scalekit \ - -F c scalekit-backup-.dump -``` - -## Back up your databases - -| Database | Frequency (production) | Retention | -|----------|----------------------|-----------| -| `scalekit` | Every 6 hours | 30 days | -| `webhooks` | Daily | 7 days | -| `openfga` | Daily | 7 days | - -```bash -# Full backup of all three databases -for db in scalekit webhooks openfga; do - pg_dump -h -U -d $db \ - -F c -f ${db}-$(date +%Y%m%d-%H%M).dump -done -``` - ## Renew TLS certificates -When your TLS certificate is due for renewal, update the Kubernetes secret and restart Scalekit to pick it up: - -```bash -kubectl create secret tls your-tls-secret \ - --cert=new-cert.pem \ - --key=new-key.pem \ - -n \ - --dry-run=client -o yaml | kubectl apply -f - - -kubectl rollout restart deployment/scalekit -n -``` +TLS certificates are managed through your cloud provider's certificate manager (for example, GCP Certificate Manager) or cert-manager. Renew certificates through your provider — the gateway picks up the updated certificate automatically without restarting Scalekit. ## Routine maintenance tasks @@ -123,6 +72,6 @@ kubectl rollout restart deployment/scalekit -n | Review auth logs | Daily | Admin dashboard → **Audit logs** | | Check pod health | Daily | `kubectl get pods -n ` | | Rotate Redis cache | As needed | Automatic — expired keys are evicted by Redis TTL | -| Vacuum PostgreSQL | Weekly | Run `VACUUM ANALYZE` on each Scalekit database | +| Back up databases | Regular | Take consistent backups of your PostgreSQL databases | | Rotate registry token | Before expiry | Create a new token in the portal, update `artifact-registry-secret`, restart pods | | Rotate individual secrets | Per policy | Edit the specific Kubernetes secret directly and restart pods | From c1a070124fbca1bb5490260319991e0dd90f9165 Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Tue, 21 Apr 2026 19:30:43 +0530 Subject: [PATCH 07/12] troubleshooting update --- .../docs/self-hosted/troubleshooting.mdx | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/content/docs/self-hosted/troubleshooting.mdx b/src/content/docs/self-hosted/troubleshooting.mdx index 42d312ee5..f0c89ba3c 100644 --- a/src/content/docs/self-hosted/troubleshooting.mdx +++ b/src/content/docs/self-hosted/troubleshooting.mdx @@ -92,24 +92,6 @@ kubectl logs -n scalekit -c svix --tail=100 --- -## Authentication errors - -### `redirect_uri_mismatch` during login - -**Cause**: The callback URL in the authorization request does not match any registered redirect URL. - -**Fix**: In the admin dashboard, go to **Applications > Your App > Redirect URLs** and add the exact callback URL your application uses. - ---- - -### `invalid_client` on token exchange - -**Cause**: Wrong `client_id` or `client_secret`, or the credentials point to a different environment. - -**Fix**: Check **Dashboard > Developers > API credentials** and confirm `SCALEKIT_ENVIRONMENT_URL` in your application matches your self-hosted domain. - ---- - ## Gateway issues ### Gateway has no external IP @@ -133,17 +115,8 @@ On GKE, confirm the GKE Gateway controller is enabled in your cluster settings. --- -### TLS certificate not provisioning - -**Cause**: The certificate annotation on the Gateway is incorrect, or the certificate manager has not issued the certificate yet. - -**Fix**: Check your cloud provider's certificate manager console for issuance errors. Confirm the `gateway.annotations` value in `values.yaml` uses the correct annotation key and certificate resource name for your provider. - ---- - ## Get support If the issue is not covered here: -- **Enterprise support**: File a ticket via your Scalekit customer portal with pod logs and the output of `kubectl describe pod -n scalekit` attached. - **Community**: Join the [Scalekit Slack community](https://join.slack.com/t/scalekit-community/shared_invite/zt-3gsxwr4hc-0tvhwT2b_qgVSIZQBQCWRw) in the `#self-hosted` channel. From b9bde2c7cdea88f15ee3a50d32a65e16e4ec2aed Mon Sep 17 00:00:00 2001 From: Anuj Sharma Date: Tue, 21 Apr 2026 19:43:52 +0530 Subject: [PATCH 08/12] Update --- src/content/docs/self-hosted/quickstart.mdx | 2 +- src/content/docs/self-hosted/setup-script.mdx | 25 +++++++------------ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/content/docs/self-hosted/quickstart.mdx b/src/content/docs/self-hosted/quickstart.mdx index 833559106..27966514b 100644 --- a/src/content/docs/self-hosted/quickstart.mdx +++ b/src/content/docs/self-hosted/quickstart.mdx @@ -17,7 +17,7 @@ tableOfContents: true import { Steps, Aside } from '@astrojs/starlight/components' import { REGISTRY_HOST, DISTR_URL } from '../../../configs/self-hosted' -Get Scalekit running on Kubernetes with a single `helm install`. This guide uses bundled PostgreSQL and Redis and auto-creates Kubernetes secrets from your `values.yaml` — no external databases or `kubectl secret` commands needed. +Get Scalekit running on Kubernetes quickly through the Scalekit distribution portal. This guide uses bundled PostgreSQL and Redis and auto-creates Kubernetes secrets from your `values.yaml` — no external databases or `kubectl secret` commands needed.