Kubernetes
This guide walks through deploying Frona on Kubernetes. The same services from the Docker Compose setup (engine, Browserless, SearXNG) are deployed as separate pods.
Prerequisites
Section titled “Prerequisites”- A running Kubernetes cluster
kubectlconfigured to access your cluster- A container registry with the Frona image (or build from the Dockerfile)
- A StorageClass that supports
ReadWriteOncepersistent volumes
Namespace
Section titled “Namespace”Create a dedicated namespace:
apiVersion: v1kind: Namespacemetadata: name: fronaSecrets
Section titled “Secrets”Store sensitive values in a Kubernetes Secret:
apiVersion: v1kind: Secretmetadata: name: frona-secrets namespace: fronatype: OpaquestringData: FRONA_AUTH_ENCRYPTION_SECRET: "your-secret-key-here" ANTHROPIC_API_KEY: "sk-ant-..." # Add other provider keys as needed # OPENAI_API_KEY: "sk-..." # FRONA_VOICE_TWILIO_ACCOUNT_SID: "..." # FRONA_VOICE_TWILIO_AUTH_TOKEN: "..."ConfigMap
Section titled “ConfigMap”Non-sensitive configuration:
apiVersion: v1kind: ConfigMapmetadata: name: frona-config namespace: fronadata: FRONA_SERVER_PORT: "3001" FRONA_DATABASE_PATH: "/data/db" FRONA_STORAGE_WORKSPACES_PATH: "/data/workspaces" FRONA_STORAGE_FILES_PATH: "/data/files" FRONA_BROWSER_WS_URL: "ws://browserless:3333" FRONA_BROWSER_PROFILES_PATH: "/data/browser_profiles" FRONA_SEARCH_PROVIDER: "searxng" FRONA_SEARCH_SEARXNG_BASE_URL: "http://searxng:8080" FRONA_SERVER_BASE_URL: "https://frona.example.com"Persistent storage
Section titled “Persistent storage”Frona needs persistent storage for its embedded database and file storage. Create a PersistentVolumeClaim:
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: frona-data namespace: fronaspec: accessModes: - ReadWriteOnce resources: requests: storage: 10GiThe /data directory holds the database, workspaces, files, and browser profiles. A single volume mounted at /data covers everything.
Frona deployment
Section titled “Frona deployment”apiVersion: apps/v1kind: Deploymentmetadata: name: frona namespace: fronaspec: replicas: 1 selector: matchLabels: app: frona template: metadata: labels: app: frona spec: securityContext: runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 containers: - name: frona image: your-registry/frona:latest ports: - containerPort: 3001 envFrom: - configMapRef: name: frona-config - secretRef: name: frona-secrets volumeMounts: - name: data mountPath: /data resources: requests: cpu: 500m memory: 512Mi limits: cpu: "2" memory: 2Gi livenessProbe: httpGet: path: /api/health port: 3001 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /api/health port: 3001 initialDelaySeconds: 5 periodSeconds: 10 volumes: - name: data persistentVolumeClaim: claimName: frona-dataFrona embeds its database in the same process, so you only need one replica. Running multiple replicas against the same volume is not supported.
Frona service
Section titled “Frona service”apiVersion: v1kind: Servicemetadata: name: frona namespace: fronaspec: selector: app: frona ports: - port: 3001 targetPort: 3001Browserless
Section titled “Browserless”apiVersion: apps/v1kind: Deploymentmetadata: name: browserless namespace: fronaspec: replicas: 1 selector: matchLabels: app: browserless template: metadata: labels: app: browserless spec: containers: - name: browserless image: ghcr.io/browserless/chromium:latest ports: - containerPort: 3333 env: - name: MAX_CONCURRENT_SESSIONS value: "10" - name: PREBOOT_CHROME value: "true" - name: ENABLE_DEBUGGER value: "true" - name: PORT value: "3333" - name: TIMEOUT value: "1800000" resources: requests: cpu: 500m memory: 1Gi limits: cpu: "2" memory: 4Gi---apiVersion: v1kind: Servicemetadata: name: browserless namespace: fronaspec: selector: app: browserless ports: - port: 3333 targetPort: 3333Browserless needs more memory than the other services because it runs headless Chrome instances. Adjust limits based on MAX_CONCURRENT_SESSIONS.
SearXNG
Section titled “SearXNG”apiVersion: apps/v1kind: Deploymentmetadata: name: searxng namespace: fronaspec: replicas: 1 selector: matchLabels: app: searxng template: metadata: labels: app: searxng spec: containers: - name: searxng image: searxng/searxng:2026.2.16 ports: - containerPort: 8080 env: - name: SEARXNG_BASE_URL value: "http://searxng:8080" volumeMounts: - name: searxng-config mountPath: /etc/searxng/settings.yml subPath: settings.yml resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi volumes: - name: searxng-config configMap: name: searxng-config---apiVersion: v1kind: Servicemetadata: name: searxng namespace: fronaspec: selector: app: searxng ports: - port: 8080 targetPort: 8080---apiVersion: v1kind: ConfigMapmetadata: name: searxng-config namespace: fronadata: settings.yml: | search: formats: - html - jsonThe JSON format is required. Without it, the web_search tool won’t work.
Ingress
Section titled “Ingress”Expose Frona externally with an Ingress. This example uses nginx-ingress with TLS:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: frona namespace: frona annotations: nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" nginx.ingress.kubernetes.io/proxy-buffering: "off"spec: ingressClassName: nginx tls: - hosts: - frona.example.com secretName: frona-tls rules: - host: frona.example.com http: paths: - path: / pathType: Prefix backend: service: name: frona port: number: 3001The timeout and buffering annotations are important. Frona uses Server-Sent Events for real-time streaming, which requires long-lived connections and no response buffering.
- Frona uses an embedded database, so only one replica is supported. Do not scale the Frona deployment beyond 1.
- Browserless and SearXNG are internal services. Don’t expose them outside the cluster.
- The Frona container runs as non-root user
frona(UID 1000). Make sure the persistent volume is writable by this user (thefsGroup: 1000in the security context handles this). - For Twilio voice callbacks, set
FRONA_VOICE_CALLBACK_BASE_URLto your public Ingress URL.