Deployment guide

Apache Tika Server 3.3.3

Overview

This guide shows you how to launch the Apache Tika Server 3.3.3 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one software version of the SecureAnyCloud Documents, Forms and Scheduling listing on AWS Marketplace. You choose it when you subscribe and launch.

Apache Tika is the Apache Software Foundation's content analysis toolkit. Tika Server wraps it in a REST API: you upload a document and get back its plain text, its metadata as JSON, or both, for PDF, Microsoft Office and OpenDocument files, HTML, e-mail, archives, images and more than a thousand other formats. Search engines, document management systems, e-discovery and RAG pipelines use it as their text extraction service.

Upstream Tika Server has no authentication, listens in plain HTTP, and can fetch remote resources when its "unsecure features" are switched on, so it must never be exposed as it ships. The SecureAnyCloud image makes it safe to run on a public address.

What you get:

  • HTTPS from the first boot. nginx serves the API on port 443 with a certificate generated on your instance's first boot, naming the instance's DNS name and IP addresses. There is no port 80, and requests for any other host name are dropped.
  • A password on every endpoint. Every Tika endpoint needs HTTP Basic authentication with the user admin and a password generated for your instance at first boot (root only, in /root/tika-server-credentials.txt). Only /healthz is open, for load balancer checks. No password ships in the image, and nginx refuses to start until first boot has created one.
  • Tika on localhost only. Tika listens on 127.0.0.1:9998. nginx is the only way in from the network, and it never passes your password on to Tika.
  • No fetching. Tika's unsecure features stay off (enableUnsecureFeatures=false), so there are no fetchers, emitters, /pipes or /async, and no fileUrl. Only the extraction endpoints are loaded. XML external entities in uploaded documents are not resolved.
  • No network egress for the parser. Tika runs as its own unprivileged user in a systemd sandbox that may talk to localhost only, so a malicious document cannot make it reach the internet, your VPC or the instance metadata. nginx and the first-boot service are also blocked from the instance metadata service, and the AMI requires IMDSv2.
  • Request limits. Uploads are limited to 100 MiB and streamed to Tika, so nginx does not spool them to disk. Each client address may hold 16 connections. Tika's parsing process is restarted if a document makes it run out of memory or parse for longer than 5 minutes.
  • The real client address. nginx overwrites X-Forwarded-For and X-Real-IP with the address of the connection.
  • The official release, verified against its published SHA-512 checksum, on a fully patched Ubuntu 24.04 LTS with unattended security upgrades switched on.
  • On the image: Apache Tika Server 3.3.3 (the official tika-server-standard JAR in /opt/tika, licence and notice in /usr/share/doc/tika), OpenJDK 17 (headless runtime), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
  • Default user: ubuntu
  • Architecture: x86_64
  • Recommended instance type: m5.large. Tika sizes its parsing heap at half the instance memory, which is 4 GiB on m5.large. Choose more memory for very large documents.

Prerequisites

  • An AWS account subscribed to the listing
  • An EC2 key pair
  • A VPC subnet with a route to the internet
  • A security group that allows TCP 22 (SSH) from your administrators, and TCP 443 (the API) only from the hosts that call Tika

Tika has no reason to be open to the whole internet.

1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Apache Tika Server 3.3.3 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2. Select your instance type, key pair, subnet and the security group described above.

Or use the AWS CLI to create the security group and launch. Replace the values in angle brackets:

aws ec2 create-security-group --region <region> --vpc-id <your-vpc> \
  --group-name tika --description "Apache Tika: SSH from admins, HTTPS from API clients"
aws ec2 authorize-security-group-ingress --region <region> --group-id <sg-id> --ip-permissions \
  'IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=<your-admin-cidr>}]' \
  'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=<your-client-cidr>}]'
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <sg-id> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=apache-tika}]'

To serve your own DNS name from the first boot, add user data that writes it before first boot runs, for example #!/bin/sh followed by mkdir -p /etc/sac && echo tika.example.com > /etc/sac/tika-hostname. Without it, the instance serves its EC2 public DNS name (or its IP address, if it has no public DNS name). You can change the name later (Step 10).

2

Connect to your instance

Connect over SSH as the ubuntu user of the Apache Tika Server 3.3.3 on Ubuntu 24.04 image:

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot generates the certificate and the API password. Then Tika and nginx start:

systemctl is-active tika nginx
sudo test -f /var/lib/sac/data/tika-firstboot.done && echo FIRSTBOOT_DONE
active active FIRSTBOOT_DONE

nginx is the only service on the network (port 443, plus SSH). Tika's own port 9998 is bound to 127.0.0.1. The other local listeners are the operating system's DNS stub, time sync and DHCP client:

sudo ss -lntup | awk 'NR>1 {print $1, $5, $7}' | sed -E 's/pid=[0-9]+,fd=[0-9]+/.../g' | sort
tcp 0.0.0.0:22 users:(("sshd",...),("systemd",...)) tcp 0.0.0.0:443 users:(("nginx",...),("nginx",...),("nginx",...)) tcp 127.0.0.53%lo:53 users:(("systemd-resolve",...)) tcp 127.0.0.54:53 users:(("systemd-resolve",...)) tcp [::]:22 users:(("sshd",...),("systemd",...)) tcp [::]:443 users:(("nginx",...),("nginx",...),("nginx",...)) tcp [::ffff:127.0.0.1]:9998 users:(("java",...)) udp 127.0.0.1:323 users:(("chronyd",...)) udp 127.0.0.53%lo:53 users:(("systemd-resolve",...)) udp 127.0.0.54:53 users:(("systemd-resolve",...)) udp 172.31.88.254%ens5:68 users:(("systemd-network",...)) udp [::1]:323 users:(("chronyd",...))
4

Read your URL and password

The credentials file is readable by root only. This prints it with the password hidden; sudo cat /root/tika-server-credentials.txt shows it:

sudo sed -E 's/^(TIKA_PASSWORD=).*/\1<hidden>/' /root/tika-server-credentials.txt
# Apache Tika Server 3.3.3 - generated on first boot by tika-firstboot.service
# This password is unique to this instance; none was baked into the image. Keep this file secret. TIKA_URL=https://ec2-100-55-11-151.compute-1.amazonaws.com/ TIKA_USERNAME=admin TIKA_PASSWORD=<hidden>
#
# Every Tika endpoint needs HTTP Basic Auth with TIKA_USERNAME and TIKA_PASSWORD; https://ec2-100-55-11-151.compute-1.amazonaws.com/healthz is the
# only open path. The certificate is self signed (minted on this instance's first boot): pass curl --cacert with # /etc/ssl/tika/tika.crt, or install your own certificate. # Extract text:
curl -u admin -T report.pdf -H 'Accept: text/plain' https://ec2-100-55-11-151.compute-1.amazonaws.com/tika
# Extract metadata:
curl -u admin -T report.docx -H 'Accept: application/json' https://ec2-100-55-11-151.compute-1.amazonaws.com/meta
# New password:
sudo tika-set-password
# Change the name it serves as:
sudo tika-set-hostname <your-dns-name>
# User guide: https://secureanycloud.com/stacks/apache-tika-server-3-3-3/

The values above come from one example instance. Yours are different.

5

Trust the instance's certificate

Clients verify the instance's own certificate, /etc/ssl/tika/tika.crt. The certificate is public, and its key is readable by root only. Copy the certificate to every host that calls Tika, as shown here into your home directory, and check that it names this instance. Inside the VPC the public DNS name resolves to the private address, so the commands below also work on the instance itself:

sudo install -m 0644 /etc/ssl/tika/tika.crt ~/tika-ca.crt openssl x509 -in ~/tika-ca.crt -noout -subject -enddate -ext subjectAltName
H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)
curl -s --cacert ~/tika-ca.crt "https://$H/healthz"
subject=CN = ec2-100-55-11-151.compute-1.amazonaws.com, O = sac notAfter=Jan 22 04:28:04 2029 GMT X509v3 Subject Alternative Name: DNS:ec2-100-55-11-151.compute-1.amazonaws.com, IP Address:100.55.11.151, IP Address:172.31.88.254, IP Address:127.0.0.1, DNS:localhost ok

Without the password, every Tika endpoint is refused:

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)
curl -s --cacert ~/tika-ca.crt -o /dev/null -w 'no password: HTTP %{http_code}\n' "https://$H/version"
curl -s --cacert ~/tika-ca.crt -o /dev/null -w 'no password, upload: HTTP %{http_code}\n' -T /opt/tika/samples/sac-sample.pdf "https://$H/tika"
no password: HTTP 401 no password, upload: HTTP 401
6

Store the password for curl

Keep the password out of your shell history and process list. Put it in a netrc file that only you can read, and give curl --netrc-file. Interactively, curl -u admin also works, and curl then asks for the password.

C=/root/tika-server-credentials.txt
H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' $C);
P=$(sudo sed -n 's/^TIKA_PASSWORD=//p' $C) ( umask 077; printf 'machine %s login admin password %s\n' "$H" "$P" > ~/.tika-netrc ) ls -l ~/.tika-netrc | awk '{print $1, $NF}' curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt "https://$H/version"; echo
-rw------- /root/.tika-netrc Apache Tika 3.3.3
7

Extract text from a document

Upload a document with PUT (curl -T) to /tika and ask for text/plain. The image includes two small sample documents in /opt/tika/samples, a PDF and a Word (DOCX) file. Use your own files the same way:

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)
curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T /opt/tika/samples/sac-sample.pdf -H 'Accept: text/plain' "https://$H/tika" | sed '/^[[:space:]]*$/d'
curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T /opt/tika/samples/sac-sample.docx -H 'Accept: text/plain' "https://$H/tika" | sed '/^[[:space:]]*$/d'
SacTikaProbe Apache Tika on sac extracts text and metadata from this document. SacTikaProbe Apache Tika on sac extracts text and metadata from this document.

Ask /tika for text/html instead to get the document as XHTML with its structure (headings, paragraphs, tables).

8

Read metadata, extract both at once, and detect types

/meta returns the document's metadata as JSON: its media type, title, author, page count and so on, and which parser read it:

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)
curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T /opt/tika/samples/sac-sample.docx -H 'Accept: application/json' "https://$H/meta" | jq '{"Content-Type", "dc:title", "dc:creator", "X-TIKA:Parsed-By"}'
{ "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "dc:title": "sac Tika sample", "dc:creator": "sac", "X-TIKA:Parsed-By": [ "org.apache.tika.parser.DefaultParser", "org.apache.tika.parser.microsoft.ooxml.OOXMLParser", "org.apache.tika.parser.microsoft.ooxml.xwpf.XWPFEventBasedWordExtractor" ] }

/rmeta/text returns metadata and text together, one JSON object per document, including every document embedded in it (attachments of an e-mail, files in a ZIP, images in a Word file). /detect/stream names the media type and /language/string guesses the language of a text:

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)
curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T /opt/tika/samples/sac-sample.pdf "https://$H/rmeta/text" | jq '.[0] | {"Content-Type", "xmpTPg:NPages", "pdf:PDFVersion", content: (."X-TIKA:content" | gsub("\\s+"; " ") | ltrimstr(" "))}'
curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T /opt/tika/samples/sac-sample.docx "https://$H/detect/stream"; echo printf 'Apache Tika detects the language of this sentence.' | curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T - "https://$H/language/string"; echo
{ "Content-Type": "application/pdf", "xmpTPg:NPages": "1", "pdf:PDFVersion": "1.4", "content": "SacTikaProbe Apache Tika on sac extracts text and metadata from this document. " } application/vnd.openxmlformats-officedocument.wordprocessingml.document en

The other endpoints are /unpack (the embedded files of a document, as a ZIP), /parsers, /detectors and /mime-types (what this Tika supports) and /version. The Tika Server documentation describes each endpoint and the request headers that tune parsing.

9

Check the security posture

A wrong password is refused like a missing one, a request for any host name other than the instance's own is dropped, and nothing listens on port 80:

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt) printf 'machine %s login admin password %s\n' "$H" "wrong-$(openssl rand -hex 8)" | \
  curl -s --netrc-file /dev/stdin --cacert ~/tika-ca.crt -o /dev/null -w 'wrong password: HTTP %{http_code}\n' "https://$H/version" curl -sk -o /dev/null -w 'unknown host name: HTTP %{http_code} (000 = connection closed)\n' --resolve evil.example:443:127.0.0.1 https://evil.example/version || true curl -s -o /dev/null -w 'port 80: HTTP %{http_code} (000 = nothing listening)\n' http://127.0.0.1/ || true
wrong password: HTTP 401 unknown host name: HTTP 000 (000 = connection closed) port 80: HTTP 000 (000 = nothing listening)

Tika's configuration is /etc/tika/tika-config.xml. It binds Tika to localhost, keeps the unsecure features off and loads only the extraction endpoints. Asked directly on its local port, Tika refuses a fetch request for a local file (422, nothing fetched) and has no /pipes or /async endpoint:

grep -oE '<(host|port|enableUnsecureFeatures|endpoint)>[^<]*' /etc/tika/tika-config.xml | sed 's/^<//; s/>/=/' | paste -sd' ' - curl -s -o /dev/null -w 'fetcher request for /etc/passwd: HTTP %{http_code}\n' -X PUT -H 'fetcherName: fsf' -H 'fetchKey: /etc/passwd' http://127.0.0.1:9998/tika for p in pipes async; do curl -s -o /dev/null -w "POST /$p: HTTP %{http_code}\n" -X POST -H 'Content-Type: application/json' -d '{}' http://127.0.0.1:9998/$p; done
host=127.0.0.1 port=9998 enableUnsecureFeatures=false endpoint=tika endpoint=rmeta endpoint=meta endpoint=detect endpoint=language endpoint=unpack endpoint=mime endpoint=detectors endpoint=parsers endpoint=version fetcher request for /etc/passwd: HTTP 422 POST /pipes: HTTP 404 POST /async: HTTP 404

Port 9998 answers without a password, but only on the instance itself. Anyone who can run commands there is already an administrator. Never open 9998 in a security group or bind Tika to another address. Go through nginx on 443.

An uploaded XML document that tries to include a local file through an external entity gets nothing back:

X=$(mktemp) printf '<?xml version="1.0"?>\n<!DOCTYPE r [<!ENTITY x SYSTEM "file:///etc/passwd">]>\n<r>external entity test: &x;</r>\n' > "$X" curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -T "$X" -H 'Content-Type: application/xml' -H 'Accept: text/plain' \
  "https://$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt)/tika" | sed '/^[[:space:]]*$/d' rm -f "$X"
external entity test:

Tika may talk to localhost only (every other address is denied), and nginx and the first boot service are denied the instance metadata addresses:

systemctl show -p IPAddressAllow -p IPAddressDeny tika.service nginx.service tika-firstboot.service | sed '/^$/d'
IPAddressAllow=::1/128 127.0.0.0/8 IPAddressDeny=::/0 0.0.0.0/0 IPAddressAllow= IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 IPAddressAllow= IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128

Tika runs as the unprivileged tika user. Its watchdog process starts the parsing process with a heap of half the instance memory and restarts it if a document exhausts that memory:

for p in $(pgrep -u tika java); do printf '%s pid %s: %s\n' "$(ps -o user= -p "$p")" "$p" "$(tr '\0' ' ' < /proc/$p/cmdline | grep -oE '(-Xmx[^ ]+|-XX:MaxRAMPercentage=[^ ]+|-XX:\+ExitOnOutOfMemoryError|TikaServerProcess -h [^ ]+ -p [^ ]+|-jar [^ ]+ -c [^ ]+)' | paste -sd' ' -)"; done
tika pid 1035: -Xmx128m -jar /opt/tika/tika-server.jar -c /etc/tika/tika-config.xml tika pid 1353: -XX:MaxRAMPercentage=50 -XX:+ExitOnOutOfMemoryError TikaServerProcess -h 127.0.0.1 -p 9998

Uploads larger than 100 MiB are refused before they reach Tika, and every response carries security headers (Tika's HTML output repeats document content, so browsers are told never to run anything in it):

H=$(sudo sed -n 's#^TIKA_URL=https://\(.*\)/$#\1#p' /root/tika-server-credentials.txt) head -c 110000000 /dev/zero | curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -o /dev/null \
  -w 'a 110 MB upload: HTTP %{http_code}\n' -X PUT -H 'Content-Type: application/octet-stream' --data-binary @- "https://$H/tika" curl -s --netrc-file ~/.tika-netrc --cacert ~/tika-ca.crt -D - -o /dev/null "https://$H/version" | grep -iE '^(strict|x-content|x-frame|content-security|cache-control|server)' | tr -d '\r'
a 110 MB upload: HTTP 413 server: nginx strict-transport-security: max-age=31536000 x-content-type-options: nosniff x-frame-options: DENY content-security-policy: default-src 'none'; frame-ancestors 'none'; sandbox cache-control: no-store
10

Use the API from your own hosts

Copy ~/tika-ca.crt to the host that calls Tika and use the URL from the credentials file, or the instance's public IP address (both are in the certificate). From your workstation, with the security group allowing your address on 443:

curl --cacert tika-ca.crt -u admin -T report.pdf -H 'Accept: text/plain' https://<public-ip>/tika

Client libraries that call a Tika Server, for example the Python tika package (set TIKA_SERVER_ENDPOINT to the HTTPS URL), need the same four settings: the URL, the user admin, the password and the CA certificate.

Your own DNS name and certificate. Point a DNS record at the instance (an Elastic IP is recommended), then make nginx serve that name with a new certificate:

sudo tika-set-hostname tika.example.com

The name is kept in /etc/sac/tika-hostname. To use a certificate from a public or private certificate authority instead, replace /etc/ssl/tika/tika.crt and /etc/ssl/tika/tika.key (key mode 0600, owner root) and run sudo systemctl reload nginx, or put an Application Load Balancer with an AWS Certificate Manager certificate in front of port 443.

11

Change the password

Generate a new random password, which is written to /root/tika-server-credentials.txt. Or set your own from standard input so it never appears on a command line. nginx reads the password hash on every request, so no restart is needed:

sudo tika-set-password read -rs NEWPW && printf '%s\n' "$NEWPW" | sudo tika-set-password --stdin; unset NEWPW
12

Tuning

  • Larger uploads. Raise client_max_body_size 100m; in /etc/nginx/sites-available/sac-tika, then sudo nginx -t && sudo systemctl reload nginx. Make sure the instance has the memory and disk for the documents you allow.
  • Memory. The parsing process uses half the instance memory (-XX:MaxRAMPercentage=50 under forkedJvmArgs in /etc/tika/tika-config.xml). The simplest way to give Tika more memory is a larger instance type. To change the share instead, edit the value and run sudo systemctl restart tika.
  • Time limit. A single parse may take up to 5 minutes (taskTimeoutMillis in the same file) before Tika restarts its parsing process. nginx waits slightly longer.

Keep enableUnsecureFeatures set to false. Turning it on lets anyone who knows the password make Tika read files and fetch URLs through fetchers. If you need Tika's pipes for batch work, run them on a private instance with no internet access.

13

Security recommendations and updates

  • Allow TCP 443 only from the hosts that call Tika, and TCP 22 only from your administrators.
  • Keep the password in a secret store (for example AWS Secrets Manager) and rotate it with tika-set-password.
  • Unattended security upgrades stay on for Ubuntu, OpenJDK and nginx. Tika is the official JAR in /opt/tika. Watch the Apache Tika news page for new releases and security announcements. To upgrade, download the new tika-server-standard JAR from Maven Central and check its checksum. Then point the /opt/tika/tika-server.jar link at it and run sudo systemctl restart tika.

Support and licensing

Apache Tika is developed by the Apache Software Foundation and distributed under the Apache License 2.0 (/usr/share/doc/tika/LICENSE and NOTICE). The image ships the unmodified official JAR, verified against its published checksum; the README also records the versions of the libraries bundled in it that carry security fixes:

head -6 /usr/share/doc/tika/README.sac.txt
Apache Tika Server 3.3.3 (https://tika.apache.org/), Apache License 2.0 (LICENSE, NOTICE in this directory). This image runs the unmodified official tika-server-standard JAR: https://repo1.maven.org/maven2/org/apache/tika/tika-server-standard/3.3.3/tika-server-standard-3.3.3.jar sha512 5242b1c9d7aa455bdb0fe101304b2c4a274882406f4d054af39cc91eef678aecb7bf31127d8cc692cd5f47a9f602f4840139f9e8e44a708e312b2ba59d1ebe89 installed as /opt/tika/tika-server-standard-3.3.3.jar (symlink /opt/tika/tika-server.jar) bundled (advisory-fixed): jackson-core 2.22.3 jackson-databind 2.22.3 jsoup 1.23.2 log4j-api 2.26.1

SecureAnyCloud provides the packaging, the first-boot automation, the HTTPS front end with per-instance authentication, and the hardening, with 24/7 support by email and live chat at sales@secureanycloud.com.

All product names are trademarks of their respective owners. Apache, Apache Tika and Tika are trademarks of the Apache Software Foundation. SecureAnyCloud is not affiliated with or endorsed by the Apache Software Foundation.

Deploy on AWS

Launch Apache Tika Server 3.3.3 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS