Presidio PII Detection and Anonymization on AWS User Guide
Overview
Presidio is an open source toolkit for finding and removing personally identifiable information (PII) from free text. It runs as two cooperating services: the analyzer, which locates entities such as names, email addresses, phone numbers, credit card numbers and national identifiers and returns the entity type, the exact character offsets and a confidence score for each; and the anonymizer, which redacts, replaces, masks, hashes or encrypts those spans. Presidio was originally created at Microsoft and is now community-governed under the Data Privacy Stack organisation, and is released under the MIT licence.
The cloudimg image installs both services into a Python virtual environment at /opt/presidio, runs each under gunicorn as an unprivileged presidio system account bound to loopback, fronts them with a single nginx gateway on TCP 80 that enforces HTTP Basic authentication on every API path, and generates a unique API password on the first boot of every instance. Backed by 24/7 cloudimg support.
What is included:
- presidio-analyzer and presidio-anonymizer, both MIT licensed
- spaCy with the en_core_web_lg language model, MIT licensed and verified against the artifact at build time
- nginx as an authenticating reverse proxy — the only network-reachable surface
- A per-instance API password generated on first boot into a root-only file
- An unauthenticated
/healthzendpoint for load-balancer probes

Key facts
| Item | Value |
|---|---|
| Platform | Ubuntu 24.04 LTS |
| Install root | /opt/presidio |
| Credentials file | /root/presidio-credentials.txt |
| API user | admin |
| Analyzer (loopback) | 127.0.0.1:5001 |
| Anonymizer (loopback) | 127.0.0.1:5002 |
| Public API | TCP 80 via nginx (HTTP Basic auth) |
| Health probe | GET /healthz (unauthenticated) |
Connecting to your instance
Open port 80 (HTTP API) and, if you want shell access, port 22 (SSH) in the instance security group. SSH in as the default login user for the OS variant you launched:
| OS variant | SSH login user | Example |
|---|---|---|
| Ubuntu 24.04 LTS | ubuntu |
ssh ubuntu@<public-ip> |
ssh ubuntu@<public-ip>
Retrieve the per-instance API password
Every instance generates its own 24-character API password on first boot and writes it to a root-only file. Read it once and store it somewhere safe:
sudo cat /root/presidio-credentials.txt
The file records the API URL, the username (admin) and the generated password. Nothing about the password is baked into the image — two instances launched from this AMI get two different passwords.
Confirm the services and the security posture
Both engines bind to loopback only; nginx on port 80 is the single reachable surface and requires authentication on every API path except the static /healthz probe.
systemctl is-active presidio-analyzer presidio-anonymizer nginx
ss -tln | grep -E '127.0.0.1:500[12]|:80 '
curl -s -o /dev/null -w 'healthz (no auth) -> HTTP %{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w 'analyze (no auth) -> HTTP %{http_code}\n' \
-X POST http://127.0.0.1/analyze \
-H 'Content-Type: application/json' -d '{"text":"hello","language":"en"}'
/healthz returns 200 without credentials; /analyze returns 401 without credentials.

Detect PII with the analyzer
POST free text to /analyze with the admin credential. Replace <your-password> with the password from the credentials file. The analyzer returns one object per detected entity with its type, start/end character offsets and a confidence score. All example data below uses the reserved example.com domain and the 555-01xx range reserved for fiction.
curl -s -u 'admin:<your-password>' \
-X POST http://127.0.0.1/analyze \
-H 'Content-Type: application/json' \
-d '{"text":"My name is Jane Doe, email jane.doe@example.com, card 4095-2609-9393-4932, phone 212-555-0143.","language":"en"}'
List every entity type the analyzer can detect:
curl -s -u 'admin:<your-password>' http://127.0.0.1/supportedentities
Redact PII with the anonymizer
Feed the analyzer's results into /anonymize to remove the detected spans. This example replaces each span with its entity type; the anonymizer also supports mask, hash, redact and encrypt operators.
RESULTS=$(curl -s -u 'admin:<your-password>' \
-X POST http://127.0.0.1/analyze \
-H 'Content-Type: application/json' \
-d '{"text":"My name is Jane Doe, email jane.doe@example.com.","language":"en"}')
curl -s -u 'admin:<your-password>' \
-X POST http://127.0.0.1/anonymize \
-H 'Content-Type: application/json' \
-d "{\"text\":\"My name is Jane Doe, email jane.doe@example.com.\",\"analyzer_results\":$RESULTS,\"anonymizers\":{\"DEFAULT\":{\"type\":\"replace\",\"new_value\":\"[REDACTED]\"}}}"
The returned text has every detected span replaced, so the personal data no longer appears in the output.

Calling the API from another machine
From your own workstation, point any Presidio SDK client or HTTP tool at the instance's public IP over port 80 with the same credentials. The analyzer and anonymizer paths are served unchanged through the proxy:
curl -s -u 'admin:<your-password>' \
-X POST http://<public-ip>/analyze \
-H 'Content-Type: application/json' \
-d '{"text":"Contact John Smith at john.smith@example.com","language":"en"}'
Only /healthz is reachable without the credential; every other path returns 401 until you authenticate.
Log hygiene for submitted text
Submitted text is the sensitive artifact for this product, so the image is configured not to persist it. Access logging is disabled on both services and on every nginx API path, and the services run at log level WARNING with Presidio's decision-process tracing left disabled. You can confirm nothing you submit is written to the journal:
curl -s -o /dev/null -u 'admin:<your-password>' \
-X POST http://127.0.0.1/analyze \
-H 'Content-Type: application/json' \
-d '{"text":"audit probe for Alex Roe at alex.roe@example.com","language":"en"}'
sudo journalctl -u presidio-analyzer -u presidio-anonymizer -u nginx --no-pager --since "-5 min" | grep -c 'alex.roe@example.com' || true
The grep returns 0 — the submitted address does not appear in the logs.
The shipped language model and its licence
Presidio's own code is MIT, but it is inert without an NLP model, and models are separately licensed artifacts. This image ships the spaCy en_core_web_lg model, whose MIT licence is verified against the installed artifact at build time and recorded in a provenance file on the image:
cat /opt/presidio/MODEL-PROVENANCE.txt
Stanza and transformer-based recognizers are deliberately not installed, because many of those models carry share-alike or research-only licences that would not permit commercial redistribution.
Change the API password
To rotate the password, write a new nginx htpasswd entry and reload:
sudo sh -c 'printf "admin:%s\n" "$(openssl passwd -apr1 <new-password>)" > /etc/nginx/cloudimg-presidio.htpasswd'
sudo systemctl reload nginx
Enable HTTPS (recommended for production)
The image serves the API over plain HTTP on port 80. For production, terminate TLS in front of Presidio. Point a DNS record at the instance, open port 443, and install a certificate with certbot:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain> --agree-tos -m admin@<your-domain> --redirect
certbot rewrites the nginx server block to serve the authenticated API over HTTPS and redirect HTTP to HTTPS.
Maintenance
- Restart the stack:
sudo systemctl restart presidio-analyzer presidio-anonymizer nginx - View service logs:
sudo journalctl -u presidio-analyzer -n 50 --no-pager - Apply OS updates:
sudo apt-get update && sudo apt-get upgrade -y
The analyzer loads its ~1 GiB language model at start, so allow a minute after a restart before the first request.
Support
This image is backed by 24/7 cloudimg technical support via email and live chat. We help with deployment, instance sizing for CPU NLP workloads, credential management, anonymization operators, model provenance and licensing questions, API integration, and TLS/VPC configuration for production. Critical issues receive a one-hour average response time. Contact support@cloudimg.co.uk.