docs/self-hosting/kubernetes.md
Kubernetes may be overkill for a static site, but it can be a great fit if you already standardize on Helm + GitOps.
[!IMPORTANT] Required Headers for Office File Conversion
LibreOffice-based tools (Word, Excel, PowerPoint conversion) require these HTTP headers for
SharedArrayBuffersupport:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corpThe official BentoPDF nginx images include these headers. In Kubernetes, Ingress/Gateway controllers are also reverse proxies, so ensure these headers are preserved (or add them at the edge).
ghcr.io/alam00000/bentopdf:<tag>) that serves on port 8080kubectl create namespace bentopdf
helm upgrade --install bentopdf /path/to/bentopdf/chart \
--namespace bentopdf \
--set image.repository=ghcr.io/alam00000/bentopdf \
--set image.tag=latest
If the chart is published to GHCR as an OCI artifact:
export GHCR_USERNAME="<github-org-or-user>"
helm upgrade --install bentopdf oci://ghcr.io/$GHCR_USERNAME/charts/bentopdf \
--namespace bentopdf \
--create-namespace \
--version 0.1.0 \
--set image.repository=ghcr.io/alam00000/bentopdf \
--set image.tag=latest
kubectl -n bentopdf port-forward deploy/bentopdf 8080:8080
Enable Ingress (example for nginx-ingress):
ingress:
enabled: true
className: nginx
hosts:
- host: pdf.example.com
paths:
- path: /
pathType: Prefix
This chart supports Gateway API Gateway + HTTPRoute.
Example (Cloudflare Gateway API operator):
gateway:
enabled: true
name: bento-tunnel
namespace: bentopdf
gatewayClassName: cloudflare
httpRoute:
enabled: true
parentRefs:
- name: bento-tunnel
namespace: bentopdf
sectionName: http
hostnames:
- pdfs.example.com
BentoPDF’s nginx config sets the required response headers. Most Ingress/Gateway controllers pass upstream response headers through unchanged.
Run this against your public endpoint:
curl -I https://pdf.example.com/ | egrep -i 'cross-origin-opener-policy|cross-origin-embedder-policy'
You should see:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corpAdd the headers at the edge (controller-specific). Example for nginx-ingress:
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
Gateway API supports a ResponseHeaderModifier filter. You can attach it in httpRoute.rules[*].filters:
httpRoute:
enabled: true
hostnames: [pdf.example.com]
parentRefs:
- name: bento-tunnel
namespace: misc
sectionName: http
rules:
- matches:
- path: { type: PathPrefix, value: / }
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
set:
- name: Cross-Origin-Opener-Policy
value: same-origin
- name: Cross-Origin-Embedder-Policy
value: require-corp
Support for specific filters depends on your Gateway controller; if a filter is ignored, add headers at the edge/controller layer instead.
Use a ConfigMap to disable tools at runtime without rebuilding the image:
apiVersion: v1
kind: ConfigMap
metadata:
name: bentopdf-config
namespace: bentopdf
data:
config.json: |
{
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
}
Mount it into the served directory:
spec:
containers:
- name: bentopdf
volumeMounts:
- name: config
mountPath: /usr/share/nginx/html/config.json
subPath: config.json
readOnly: true
volumes:
- name: config
configMap:
name: bentopdf-config
Tool IDs are the page URL without .html — open any tool and look at the URL (e.g., edit-pdf, merge-pdf, compress-pdf). Disabled tools are hidden from the homepage, search, shortcuts, workflow builder, and direct URL access. See the Docker guide for the full list of options.