1"use strict";(globalThis.webpackChunkauthorizer_docs=globalThis.webpackChunkauthorizer_docs||[]).push([[1968],{4137(e,n,r){r.r(n),r.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>l});const s=JSON.parse('{"id":"deployment/kubernetes","title":"Kubernetes","description":"This guide shows how to deploy Authorizer v2 on Kubernetes using the CLI-only configuration model.","source":"@site/docs/deployment/kubernetes.md","sourceDirName":"deployment","slug":"/deployment/kubernetes","permalink":"/deployment/kubernetes","draft":false,"unlisted":false,"editUrl":"https://github.com/authorizerdev/authorizer-docs/tree/main/docs/deployment/kubernetes.md","tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"sidebar_position":3,"title":"Kubernetes"},"sidebar":"docsSidebar","previous":{"title":"Binary Deployment","permalink":"/deployment/binary"},"next":{"title":"Helm Chart","permalink":"/deployment/helm-chart"}}');var t=r(4848),i=r(8453);const o={sidebar_position:3,title:"Kubernetes"},a="Kubernetes (v2)",c={},l=[{value:"1. Prerequisites",id:"1-prerequisites",level:2},{value:"2. Deployment manifest (v2-style)",id:"2-deployment-manifest-v2-style",level:2},{value:"Ports: <code>containerPort</code>, Services, and Ingress",id:"k8s-ports-services",level:3},{value:"Metrics (Prometheus)",id:"metrics-prometheus",level:3},{value:"3. Managing configuration changes",id:"3-managing-configuration-changes",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"kubernetes-v2",children:"Kubernetes (v2)"})}),"\n",(0,t.jsxs)(n.p,{children:["This guide shows how to deploy ",(0,t.jsx)(n.strong,{children:"Authorizer v2"})," on Kubernetes using the ",(0,t.jsx)(n.strong,{children:"CLI-only configuration model"}),".\nInstead of configuring env vars that the server reads directly, we pass ",(0,t.jsx)(n.strong,{children:"CLI flags"})," through the container ",(0,t.jsx)(n.code,{children:"args"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["If you are migrating from v1, compare with ",(0,t.jsx)(n.a,{href:"../deployment/kubernetes",children:"Kubernetes"})," and the ",(0,t.jsx)(n.a,{href:"../migration/v1-to-v2",children:"Migration v1 to v2"})," guide."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"1-prerequisites",children:"1. Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"A Kubernetes cluster (GKE, EKS, AKS, k3s, etc.)."}),"\n",(0,t.jsxs)(n.li,{children:["Container image for ",(0,t.jsx)(n.strong,{children:"Authorizer v2"})," built with:"]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-dockerfile",children:'# Official image listens on 8080 (HTTP) and 8081 (metrics); see Dockerfile EXPOSE comments.\nENTRYPOINT ["./authorizer"]\nCMD []\n'})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Optional:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Ingress controller (for example nginx)."}),"\n",(0,t.jsx)(n.li,{children:"cert-manager for automatic TLS."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"2-deployment-manifest-v2-style",children:"2. Deployment manifest (v2-style)"}),"\n",(0,t.jsxs)(n.p,{children:["Below is an example ",(0,t.jsx)(n.code,{children:"Deployment"})," + ",(0,t.jsx)(n.code,{children:"Service"})," + ",(0,t.jsx)(n.code,{children:"Ingress"})," setup.\nThe key difference vs v1: ",(0,t.jsxs)(n.strong,{children:["we pass configuration as CLI flags in ",(0,t.jsx)(n.code,{children:"args"})]}),"."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yaml",children:'apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: authorizer-v2\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: authorizer-v2\n template:\n metadata:\n labels:\n app: authorizer-v2\n spec:\n containers:\n - name: authorizer-v2\n image: your-registry/authorizer:v2\n imagePullPolicy: Always\n ports:\n - containerPort: 8080\n name: http\n - containerPort: 8081\n name: metrics\n env:\n - name: DATABASE_URL\n valueFrom:\n secretKeyRef:\n name: authorizer-secrets\n key: database-url\n - name: CLIENT_ID\n valueFrom:\n secretKeyRef:\n name: authorizer-secrets\n key: client-id\n - name: CLIENT_SECRET\n valueFrom:\n secretKeyRef:\n name: authorizer-secrets\n key: client-secret\n - name: ADMIN_SECRET\n valueFrom:\n secretKeyRef:\n name: authorizer-secrets\n key: admin-secret\n - name: ENCRYPTION_KEY\n valueFrom:\n secretKeyRef:\n name: authorizer-secrets\n key: encryption-key\n args:\n - "--env=production"\n - "--http-port=8080"\n - "--metrics-port=8081"\n - "--metrics-host=0.0.0.0"\n - "--rate-limit-rps=30"\n - "--rate-limit-burst=20"\n - "--rate-limit-fail-closed=false"\n - "--database-type=postgres"\n - "--database-url=$(DATABASE_URL)"\n - "--url=https://YOUR_DOMAIN"\n - "--client-id=$(CLIENT_ID)"\n - "--client-secret=$(CLIENT_SECRET)"\n - "--admin-secret=$(ADMIN_SECRET)"\n - "--jwt-type=RS256"\n - "--encryption-key=$(ENCRYPTION_KEY)"\n # Example: mount keys from files or use env and expand here\n - "--enable-login-page=true"\n - "--enable-playground=false"\n - "--enable-graphql-introspection=false"\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: authorizer-v2\nspec:\n selector:\n app: authorizer-v2\n ports:\n - port: 80\n name: http\n targetPort: 8080\n type: ClusterIP\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: authorizer-v2\n annotations:\n kubernetes.io/ingress.class: nginx\n cert-manager.io/cluster-issuer: letsencrypt-prod\nspec:\n rules:\n - host: YOUR_DOMAIN\n http:\n paths:\n - path: /\n pathType: Prefix\n backend:\n service:\n name: authorizer-v2\n port:\n number: 80\n tls:\n - hosts:\n - YOUR_DOMAIN\n secretName: authorizer-v2-tls\n'})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Note:"})," Use Kubernetes ",(0,t.jsx)(n.code,{children:"Secret"})," resources for sensitive values and reference them via ",(0,t.jsx)(n.code,{children:"env"})," + ",(0,t.jsx)(n.code,{children:"args"})," as shown."]}),"\n"]}),"\n",(0,t.jsxs)(n.h3,{id:"k8s-ports-services",children:["Ports: ",(0,t.jsx)(n.code,{children:"containerPort"}),", Services, and Ingress"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Declare ",(0,t.jsx)(n.strong,{children:"both"})," ",(0,t.jsx)(n.code,{children:"containerPort: 8080"})," and ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"8081"})})," on the pod so the API contract matches the image (",(0,t.jsx)(n.code,{children:"EXPOSE 8080 8081"})," in the Dockerfile). This is documentation for humans and tooling; it does not by itself expose traffic to the internet."]}),"\n",(0,t.jsxs)(n.li,{children:["The ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"Service"})})," that backs your ",(0,t.jsx)(n.strong,{children:"Ingress"})," (or cloud load balancer) should forward ",(0,t.jsx)(n.strong,{children:"only"})," the app port (",(0,t.jsx)(n.strong,{children:"80 \u2192 8080"})," in the example above). ",(0,t.jsx)(n.strong,{children:"Do not"})," add ",(0,t.jsx)(n.code,{children:"8081"})," to that same public-facing ",(0,t.jsx)(n.code,{children:"Service"})," or ",(0,t.jsx)(n.code,{children:"Ingress"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:["For ",(0,t.jsx)(n.a,{href:"https://prometheus.io",children:"Prometheus"}),", scrape ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"8081"})})," via the ",(0,t.jsx)(n.strong,{children:"pod network"})," or a ",(0,t.jsxs)(n.strong,{children:["separate ClusterIP ",(0,t.jsx)(n.code,{children:"Service"})]})," (below). Set ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"--metrics-host=0.0.0.0"})})," in ",(0,t.jsx)(n.code,{children:"args"})," so the metrics listener accepts connections from other pods; without it, metrics stay on container loopback and in-cluster scrapes will fail."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Summary:"})," expose ",(0,t.jsx)(n.strong,{children:"both"})," ports on the ",(0,t.jsx)(n.strong,{children:"Pod"}),"; expose ",(0,t.jsx)(n.strong,{children:"only HTTP (8080)"})," to clients via Ingress/LB; keep ",(0,t.jsx)(n.strong,{children:"metrics (8081)"})," internal to the cluster."]}),"\n",(0,t.jsx)(n.h3,{id:"metrics-prometheus",children:"Metrics (Prometheus)"}),"\n",(0,t.jsxs)(n.p,{children:["The app serves ",(0,t.jsxs)(n.strong,{children:[(0,t.jsx)(n.code,{children:"/metrics"})," on a second HTTP listener"]})," on port ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"8081"})}),". In Kubernetes, ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"--metrics-host=0.0.0.0"})})," is usually required so Prometheus can scrape the pod IP. ",(0,t.jsx)(n.strong,{children:"Do not"})," put port ",(0,t.jsx)(n.code,{children:"8081"})," on an internet-facing ",(0,t.jsx)(n.code,{children:"Ingress"}),". Use a ",(0,t.jsx)(n.code,{children:"ServiceMonitor"}),", PodMonitor, or a dedicated ",(0,t.jsx)(n.strong,{children:"ClusterIP"})," ",(0,t.jsx)(n.code,{children:"Service"})," and scrape config only. If Prometheus runs on the ",(0,t.jsx)(n.strong,{children:"same host"})," as a bare binary (not K8s), you can keep the default ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"127.0.0.1"})})," for metrics instead."]}),"\n",(0,t.jsxs)(n.p,{children:["Optional internal ",(0,t.jsx)(n.code,{children:"Service"})," for metrics (ClusterIP only):"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yaml",children:"apiVersion: v1\nkind: Service\nmetadata:\n name: authorizer-v2-metrics\nspec:\n selector:\n app: authorizer-v2\n ports:\n - port: 8081\n targetPort: metrics\n name: metrics\n type: ClusterIP\n"})}),"\n",(0,t.jsx)(n.p,{children:"Apply the manifest:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f authorizer-v2.yaml\n"})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"3-managing-configuration-changes",children:"3. Managing configuration changes"}),"\n",(0,t.jsxs)(n.p,{children:["Because all configuration is now expressed as ",(0,t.jsx)(n.strong,{children:"CLI flags"}),":"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["To change a setting (for example enable playground), update the ",(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"args"})})," list."]}
1),"\n",(0,t.jsx)(n.li,{children:"Then run:"}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f authorizer-v2.yaml\nkubectl rollout restart deployment authorizer-v2\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You no longer update server config via ",(0,t.jsx)(n.code,{children:"_update_env"})," or dashboard; those are deprecated in v2."]}),"\n",(0,t.jsx)(n.p,{children:"For a full list of available flags, see:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"../core/server-config",children:"Server Configuration (v2)"})}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"./authorizer --help"})," in the container image"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453(e,n,r){r.d(n,{R:()=>o,x:()=>a});var s=r(6540);const t={},i=s.createContext(t);function o(e){const n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.