A production-ready Helm chart for deploying Apache Tika Server on Kubernetes. Tika detects and extracts metadata and text from over 1,000 different file types (PDF, DOCX, images via OCR, etc.) via a simple HTTP REST API.
This chart improves upon the upstream apache/tika-helm chart with:
| Feature | Upstream | This Chart |
|---|---|---|
| Startup probe | ❌ | ✅ (critical for JVM warmup) |
| PodDisruptionBudget | ❌ | ✅ |
| ServiceMonitor / PodMonitor | ❌ | ✅ |
| Init containers / Sidecars | ❌ | ✅ |
allowPrivilegeEscalation default |
true |
false |
seccompProfile |
❌ | ✅ RuntimeDefault |
| Autoscaling behavior policies | ❌ | ✅ |
| Network policy egress + DNS | ❌ | ✅ |
values.schema.json validation |
❌ | ✅ |
| Published via GitHub Pages | ❌ | ✅ |
Client → Tika Service (ClusterIP:9998) → Tika Pod (JVM)
├── /tmp (emptyDir, writable)
├── /tika-config (ConfigMap, optional)
└── /tika-extras (extra JARs, optional)
helm repo add kenchrcum-tika https://kenchrcum.github.io/helm-chart-tika
helm repo update
helm install tika kenchrcum-tika/tika
Then verify the deployment:
kubectl get pods -l app.kubernetes.io/name=tika
kubectl port-forward svc/tika 9998:9998
curl http://localhost:9998/version
# → Apache Tika 3.2.3.0
helm repo add kenchrcum-tika https://kenchrcum.github.io/helm-chart-tika
helm install tika kenchrcum-tika/tika --namespace tika --create-namespace
git clone https://github.com/kenchrcum/helm-chart-tika.git
helm install tika ./helm-chart-tika/tika
helm install tika kenchrcum-tika/tika \
--namespace tika \
--create-namespace \
--set tika.fullImage=true \
--set resources.limits.memory=4Gi
Apache Tika ships two image variants:
| Variant | Tag Pattern | Size | Includes |
|---|---|---|---|
| Full (default) | 3.2.3.0-full |
~1.5 GB | Tesseract OCR, GDAL, ImageMagick, fonts |
| Minimal | 3.2.3.0 |
~400 MB | Tika core only |
Control this with tika.fullImage (default: true). To use the minimal image:
tika:
fullImage: false
Mount a tika-config.xml via a ConfigMap:
tika:
config: |
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<parsers>
<parser class="org.apache.tika.parser.DefaultParser">
<parser-exclude class="org.apache.tika.parser.ocr.TesseractOCRParser"/>
</parser>
</parsers>
</properties>
When tika.config is set, the chart automatically:
/tika-config/tika-config.xml-c /tika-config/tika-config.xml to the Tika processtika:
javaOpts: "-Xmx4g -Xms1g -XX:+UseG1GC"
resources:
requests:
memory: 2Gi
limits:
memory: 6Gi
| Parameter | Description | Default |
|---|---|---|
replicaCount |
Number of Tika pod replicas | 1 |
image.repository |
Docker image repository | apache/tika |
image.tag |
Image tag override (empty = auto from appVersion) | "" |
image.pullPolicy |
Image pull policy | IfNotPresent |
imagePullSecrets |
Registry pull secrets | [] |
nameOverride |
Partial name override | "" |
fullnameOverride |
Full name override | "" |
| Parameter | Description | Default |
|---|---|---|
tika.fullImage |
Use full image variant (OCR, GDAL, fonts) | true |
tika.config |
Custom tika-config.xml content |
"" |
tika.additionalConfigs |
Extra config files in the ConfigMap | {} |
tika.javaOpts |
JVM options (e.g., -Xmx2g -XX:+UseG1GC) |
"" |
tika.cors |
CORS origins (* or comma-separated list) |
"" |
tika.logLevel |
Tika Server log level | "info" |
| Parameter | Description | Default |
|---|---|---|
serviceAccount.create |
Create a dedicated service account | true |
serviceAccount.annotations |
Annotations for the service account | {} |
serviceAccount.name |
Name override (auto-generated if empty) | "" |
| Parameter | Description | Default |
|---|---|---|
podAnnotations |
Annotations added to every pod | {} |
podLabels |
Labels added to every pod | {} |
podSecurityContext.runAsNonRoot |
Enforce non-root execution | true |
podSecurityContext.runAsUser |
UID (matches Tika image: 35002) |
35002 |
podSecurityContext.runAsGroup |
GID | 35002 |
podSecurityContext.fsGroup |
Filesystem group | 35002 |
| Parameter | Description | Default |
|---|---|---|
securityContext.readOnlyRootFilesystem |
Read-only root (writable /tmp via emptyDir) |
true |
securityContext.allowPrivilegeEscalation |
Prevent privilege escalation | false |
securityContext.capabilities.drop |
Drop all Linux capabilities | ["ALL"] |
securityContext.seccompProfile.type |
Seccomp profile | RuntimeDefault |
| Parameter | Description | Default |
|---|---|---|
service.enabled |
Create a Service resource | true |
service.type |
Service type (ClusterIP, NodePort, LoadBalancer) |
ClusterIP |
service.port |
Service port | 9998 |
service.annotations |
Annotations for the service | {} |
| Parameter | Description | Default |
|---|---|---|
ingress.enabled |
Create an Ingress resource | false |
ingress.className |
Ingress class name | "" |
ingress.annotations |
Ingress annotations | {} |
ingress.hosts |
Host/path rules | [{host: tika.local, paths: [{/,Prefix}]}] |
ingress.tls |
TLS configuration | [] |
| Parameter | Description | Default |
|---|---|---|
resources.requests.cpu |
CPU request | 500m |
resources.requests.memory |
Memory request | 1Gi |
resources.limits.cpu |
CPU limit | "2" |
resources.limits.memory |
Memory limit | 2Gi |
| Parameter | Description | Default |
|---|---|---|
startupProbe.initialDelaySeconds |
Delay before startup probe starts | 10 |
startupProbe.periodSeconds |
Startup probe interval | 5 |
startupProbe.failureThreshold |
Max failures before restart (120s total) | 24 |
livenessProbe.periodSeconds |
Liveness check interval | 30 |
readinessProbe.periodSeconds |
Readiness check interval | 10 |
All probes check GET /version on the http named port (9998).
| Parameter | Description | Default |
|---|---|---|
autoscaling.enabled |
Enable HorizontalPodAutoscaler | false |
autoscaling.minReplicas |
Minimum replicas | 1 |
autoscaling.maxReplicas |
Maximum replicas | 10 |
autoscaling.targetCPUUtilizationPercentage |
CPU target | 80 |
autoscaling.targetMemoryUtilizationPercentage |
Memory target | 80 |
autoscaling.behavior |
Scale-up/down behavior policies | {} |
| Parameter | Description | Default |
|---|---|---|
podDisruptionBudget.enabled |
Create a PodDisruptionBudget | false |
podDisruptionBudget.minAvailable |
Min available pods or percentage | null |
podDisruptionBudget.maxUnavailable |
Max unavailable pods or percentage | null |
Note: Tika does not natively expose Prometheus metrics. These resources are useful when adding a JMX exporter sidecar. See Monitoring.
| Parameter | Description | Default |
|---|---|---|
serviceMonitor.enabled |
Create a ServiceMonitor | false |
serviceMonitor.interval |
Scrape interval | 30s |
serviceMonitor.scrapeTimeout |
Scrape timeout | 10s |
serviceMonitor.path |
Metrics path | /metrics |
serviceMonitor.labels |
Extra labels (e.g., release: prometheus) |
{} |
podMonitor.enabled |
Create a PodMonitor | false |
podMonitor.interval |
Scrape interval | 30s |
podMonitor.scrapeTimeout |
Scrape timeout | 10s |
podMonitor.path |
Metrics path | /metrics |
podMonitor.labels |
Extra labels | {} |
| Parameter | Description | Default |
|---|---|---|
networkPolicy.enabled |
Create a NetworkPolicy | false |
networkPolicy.policyTypes |
Policy types to enforce | [Ingress, Egress] |
networkPolicy.namespaceIsolation |
Restrict to same namespace only | true |
networkPolicy.namespaceLabel.key |
Namespace label key for isolation | kubernetes.io/metadata.name |
networkPolicy.ingress.ingressControllers |
Allow from ingress-controller namespace | false |
networkPolicy.ingress.ingressNamespace |
Ingress controller namespace name | ingress-nginx |
networkPolicy.ingress.monitoring |
Allow from monitoring namespace | false |
networkPolicy.ingress.monitoringNamespace |
Monitoring namespace name | monitoring |
tikaExtras)Mount additional JARs at /tika-extras/ to extend Tika’s classpath:
| Parameter | Description | Default |
|---|---|---|
tikaExtras.enabled |
Enable the extra JARs volume | false |
tikaExtras.type |
Volume type: emptyDir, pvc |
emptyDir |
tikaExtras.emptyDir.sizeLimit |
emptyDir size limit | 1Gi |
tikaExtras.pvc.storageClass |
PVC storage class | "" |
tikaExtras.pvc.size |
PVC size | 2Gi |
tikaExtras.pvc.existingClaim |
Use an existing PVC | "" |
| Parameter | Description | Default |
|---|---|---|
extraEnv |
Extra env vars for the Tika container | [] |
extraVolumeMounts |
Extra volume mounts for the Tika container | [] |
extraVolumes |
Extra volumes for the pod | [] |
initContainers |
Init containers | [] |
sidecars |
Sidecar containers | [] |
| Parameter | Description | Default |
|---|---|---|
nodeSelector |
Node selector | {} |
tolerations |
Tolerations | [] |
affinity |
Affinity rules | {} |
topologySpreadConstraints |
Topology spread constraints | [] |
# minimal.yaml
tika:
fullImage: false # ~400 MB instead of ~1.5 GB
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
# ha-production.yaml
replicaCount: 3
tika:
fullImage: true
javaOpts: "-Xmx3g -Xms1g -XX:+UseG1GC"
resources:
requests:
cpu: 1
memory: 2Gi
limits:
cpu: "4"
memory: 4Gi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
podDisruptionBudget:
enabled: true
minAvailable: 2
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: tika
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
hosts:
- host: tika.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: tika-tls
hosts:
- tika.example.com
tika:
fullImage: true # Required for OCR
config: |
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<parsers>
<parser class="org.apache.tika.parser.pdf.PDFParser">
<params>
<param name="ocrStrategy" type="string">auto</param>
<param name="ocrDPI" type="int">300</param>
</params>
</parser>
</parsers>
</properties>
networkPolicy:
enabled: true
policyTypes:
- Ingress
- Egress
namespaceIsolation: true # Only same-namespace pods can reach Tika
ingress:
ingressControllers: true # Allow external traffic via nginx-ingress
ingressNamespace: ingress-nginx
monitoring: true # Allow Prometheus scraping
monitoringNamespace: monitoring
sidecars:
- name: jmx-exporter
image: bitnami/jmx-exporter:0.20.0
ports:
- name: metrics
containerPort: 5556
args:
- "5556"
- /config/jmx-config.yaml
volumeMounts:
- name: jmx-config
mountPath: /config
extraVolumes:
- name: jmx-config
configMap:
name: tika-jmx-config
podMonitor:
enabled: true
interval: 30s
labels:
release: prometheus
Tika Server does not natively expose Prometheus metrics. The serviceMonitor and
podMonitor resources are included for teams using a JMX exporter sidecar.
To monitor JVM health without metrics, consider scraping the /version endpoint
as a black-box probe via Prometheus’ blackbox_exporter.
The chart enforces secure defaults out of the box:
| Control | Setting |
|---|---|
| User/Group | 35002:35002 (non-root, matches Tika image) |
| Privilege escalation | Disabled (allowPrivilegeEscalation: false) |
| Linux capabilities | All dropped |
| Seccomp profile | RuntimeDefault |
| Root filesystem | Read-only (writable /tmp via emptyDir) |
| Service account | Dedicated, auto-created, no extra RBAC |
This chart uses chart-releaser to publish releases to GitHub Pages.
| Workflow | Trigger | Purpose |
|---|---|---|
helm-lint-test.yml |
PR / push to master touching tika/** |
Lint + template all CI values files |
helm-release.yml |
Push to master touching tika/** |
Package + publish to GitHub Pages |
setup-pages.yml |
Manual (workflow_dispatch) |
One-time: create gh-pages branch |
bash tika/scripts/test-chart.sh
This mirrors the CI steps: lint, template with all CI values files, schema validation.
Check resource availability and node tolerations:
kubectl describe pod -l app.kubernetes.io/name=tika
JVM startup can take 10–30 seconds. The startup probe allows up to 120 seconds
(24 × 5s). To increase the window:
startupProbe:
failureThreshold: 36 # 3 minutes
Check logs:
kubectl logs -l app.kubernetes.io/name=tika --previous
Increase resources and set JVM heap proportionally (leave ~512 MB headroom):
tika:
javaOpts: "-Xmx6g"
resources:
limits:
memory: 7Gi
kubectl get svc -l app.kubernetes.io/name=tika
kubectl port-forward svc/tika 9998:9998
curl http://localhost:9998/version
If migrating from apache/tika-helm:
| Upstream Key | This Chart Key | Notes |
|---|---|---|
image.tag |
image.tag |
Same |
replicaCount |
replicaCount |
Same |
networkPolicy.ingressSelectorLabel |
networkPolicy.namespaceLabel.key |
Restructured |
env |
extraEnv |
Renamed |
| (not present) | tika.fullImage |
New — auto-selects -full tag |
| (not present) | startupProbe |
New — critical for JVM warmup |
| (not present) | podDisruptionBudget |
New |
serviceMonitor/podMonitor: Prometheus Operator installednetworkPolicy: A CNI that supports NetworkPolicy (e.g., Calico, Cilium)Apache License 2.0 — see the LICENSE file for details.