Skip to content

Deployment Guide

This guide covers deploying Enprivacy 3.0 into your own environment, including container configuration, recommended encryption settings, and signature verification.

Enprivacy 3.0 is distributed as Open Container Initiative (OCI) / Docker container images and is designed to run on most cloud and on-premises environments. Before deploying, ensure you have:

  • Container runtime — Docker Engine 24+ with Compose v2, or a Kubernetes cluster / cloud ‘container application’ service (e.g. AWS ECS, Azure Container Apps, Google Cloud Run).
  • Operating system (for self-managed hosts) — a 64-bit Linux distribution such as Ubuntu 22.04+ or Debian 12.
  • PostgreSQL 16+ (18+ recommended) with the hstore and pgvector extensions. This may be a managed service or a self-hosted container — see Database Setup below.
  • Blob / object storage — an S3-compatible service (managed, or self-hosted such as MinIO or Garage) for document uploads and redacted output.
  • GPU (LLM service only) — a T4-equivalent or better GPU, unless you use an external/hosted LLM provider (e.g. AWS Bedrock, Azure OpenAI, Google Vertex).

For full per-service resource requirements and sizing, see the Quick Start Guide.

All Enprivacy 3.0 release images are published to the GitHub Container Registry:

ghcr.io/enprivacyhq/enprivacy:v<version>

For production, pin the image by digest (ghcr.io/enprivacyhq/enprivacy@sha256:…) rather than by tag — the digest is the provenance handle that signature verification and SBOM attestation attach to.

The registry is private. Enprivacy issues a read-only pull token — a GitHub personal access token (classic) scoped to read:packages, owned by an Enprivacy bot account — together with that account’s username. Log in with both, and note the token’s expiry date so it is replaced before pulls start failing:

Terminal window
echo "$GHCR_TOKEN" | docker login ghcr.io -u <username> --password-stdin

All release images are signed with Cosign using keyless (GitHub OIDC) signing — there is no long-lived public key. Verification checks that the image was signed by Enprivacy’s release workflow:

Terminal window
cosign verify \
--certificate-identity-regexp \
"https://github.com/enprivacyhq/enprivacy-platform/.github/workflows/release.yml" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/enprivacyhq/enprivacy:v<version>

Each GitHub Release’s notes embed the exact command for that release. Every release also ships source and image SBOMs (SPDX JSON), attested in the registry via Cosign — see Supply Chain Security.

The container runs as the non-root user rails, UID 1000 / GID 1000. Any volumes you mount (e.g. /rails/storage for local file storage) must be writable by that UID.

A standard deployment comprises five services, a PostgreSQL database, and blob storage:

Service Purpose GPU
Web End-user and administrative interfaces; public Job interfaces No
Job Background job and task processing No
Inference adapter Embedding, reranking and entity detection — mandatory; see Inference Adapter No
LLM System LLM — a managed endpoint (e.g. Azure AI Foundry, AWS Bedrock) or self-hosted vLLM Only if self-hosted (T4-equivalent)
OCR Text extraction via Docling No

The Web and Job services support horizontal scaling and are stateless — all state is held in the database or blob storage. See the Quick Start Guide for minimum and recommended CPU/RAM/disk per service.

The Web and Job services load no machine-learning models. The Inference Adapter runs them, and its image carries the weights, so there is no model cache to mount or stage.

The one data set the platform image holds itself is the address data used by the address refiners (~1.9 GB), baked in at /rails/models. A volume mounted there replaces it, which is only needed for an image built without it; the published image needs nothing mounted.

A self-hosted LLM downloads and caches its own models; see its documentation.

Enprivacy 3.0 requires an always-on PostgreSQL database (16+, 18+ recommended) that holds state for the Web and Job services.

  1. Create a database schema.

  2. Create a database user with a password and all privileges over that schema. The service uses this user to prepare the schema on first run, to apply schema updates during upgrades, and for day-to-day operations.

  3. Enable the required extensions: hstore and pgvector.

    PostGIS and pgRouting were required by earlier releases and are no longer used. If an existing database has them, they can be dropped — on a managed service that also means they can come off the extension allow-list.

The Web service prepares the schema automatically on boot (creating the database if absent and applying pending migrations). If a dedicated migration job owns schema changes — for example when the runtime database user has no DDL rights — set SKIP_DB_PREPARE=1 on the Web service and run bin/rails db:prepare from the migration job instead.

For improved security, rotate the database password using either a single-user or dual-user configuration — see the Operations Guide.

Enprivacy 3.0 supports several deployment models; see the Quick Start Guide for full detail.

  • Cloud managed services (recommended) — deploy each service via the cloud provider’s container application service or Kubernetes, using managed PostgreSQL and managed object storage. For Azure, a complete Terraform reference architecture is available — see Deploy on Azure with Terraform.
  • Self-hosted containers — run the database (with the required extensions) and an S3-compatible store (e.g. MinIO or Garage) as containers.
  • All-In-One (demonstration only) — run the Web, Job, OCR, and Database services in a single environment with local-disk durable storage and an external LLM service. Not recommended for production.

All configuration is supplied through environment variables. Enprivacy 3.0 is single-tenant per install: these values are set once per deployment, not per workspace.

Variable Purpose
SECRET_KEY_BASE Rails session/signing secret (generate: openssl rand -hex 64)
EP_ENCRYPTION_PRIMARY_KEY Application-level encryption of sensitive database columns. Rotatable as a comma-delimited list: append the new key last (it becomes the active key; the older keys keep decrypting existing data)
EP_ENCRYPTION_INDEX_KEY HMAC key for the one-way blind indexes that make encrypted identity fields searchable. Stable for the life of the data — never rotate without re-indexing
EP_ENCRYPTION_KEY_DERIVATION_SALT As above (key derivation). A single value — never rotatable
DATABASE_URL PostgreSQL connection URL (postgres://user:pass@host:5432/dbname?sslmode=require)
EP_HOST The public hostname users reach the app on — drives link generation and the OAuth callback URL
EP_LLM_URL OpenAI-compatible endpoint for the platform’s system LLM
EP_OCR_URL Endpoint of the OCR (Docling) service
EP_INFERENCE_URL / EP_INFERENCE_API_KEY The Inference Adapter’s endpoint and API key. Web and Job refuse to start without both — see Inference Adapter
Variable Default Purpose
EP_AUTH_CLIENT_ID / EP_AUTH_CLIENT_SECRET — OIDC client credentials, provided by Enprivacy. Required for sign-in.
EP_AUTH_URL / EP_AUTH_ISSUER https://auth.enprivacy.com OIDC provider base and expected issuer. Leave at default unless directed otherwise.
EP_ALLOWED_ORGANIZATION_CODES empty (no gating) Comma-separated allowlist of organisation codes permitted to sign in, provided by Enprivacy.
EP_SUPER_ADMIN_EMAILS empty Comma-separated emails bootstrapped as super administrators.
Variable Default Purpose
EP_LLM_URL — (required) OpenAI-compatible endpoint for the platform’s system agents
EP_LLM_PROVIDER enprivacy LLM client to use: enprivacy (self-hosted gateway), azure (Azure AI Foundry / Azure OpenAI), openai, anthropic, bedrock, …
EP_LLM_MODEL openai/gpt-oss-20b Model (or Azure deployment name) the system agents request
EP_LLM_API_KEY — Credential for the system agents’ own LLM deployment — kept isolated from any provider-global keys

For Azure AI Foundry, point EP_LLM_URL at the account’s OpenAI-compatible surface (https://<account>.openai.azure.com/openai/v1), set EP_LLM_PROVIDER=azure, and supply the account key as EP_LLM_API_KEY. The system agents use these settings in an isolated context, so any AZURE_OPENAI_* variables you configure for other Azure models are unaffected — and the Foundry account serving the system agents should be dedicated to this workload (nothing else pointed at it). The Azure Terraform deployment wires all of this automatically.

Set ACTIVE_STORAGE_SERVICE to select the backend, then its credentials:

Backend Variables
amazon (S3 / S3-compatible) AWS_REGION, S3_BUCKET_NAME, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY
azure (Azure Blob) AZURE_STORAGE_ACCOUNT_NAME, AZURE_STORAGE_CONTAINER; keyless by default — with AZURE_STORAGE_ACCESS_KEY unset the app authenticates with the host’s managed identity (grant it Storage Blob Data Contributor; optionally pick an identity with AZURE_STORAGE_PRINCIPAL_ID). Supplying the access key switches to shared-key auth.
local (disk) none — mount durable storage at /rails/storage

Outbound mail is disabled by default (EP_MAIL_DELIVERY_METHOD=none). To enable SMTP delivery, set EP_MAIL_DELIVERY_METHOD=smtp plus EP_MAIL_SMTP_ADDRESS, EP_MAIL_SMTP_USER_NAME, EP_MAIL_SMTP_PASSWORD, EP_MAIL_FROM_ADDRESS, and EP_MAIL_DEFAULT_URL_HOST. Additional knobs (port, STARTTLS, authentication mode, reply-to) are available; ask Enprivacy for the provider-specific examples (AWS SES, SendGrid, Postmark, Resend, Office 365).

Variable Default Purpose
WEB_CONCURRENCY 1 Web server worker processes — raise to match vCPUs on larger containers
RAILS_MAX_THREADS 3 Threads per worker (and per-process DB pool size)
JOB_CONCURRENCY 1 Background job worker processes (Job service)
HTTP_WRITE_TIMEOUT / HTTP_IDLE_TIMEOUT 30 / 60 Set both to 90 — long synchronous LLM turns need it on every proxy hop, kept below Cloudflare’s ~100 s edge timeout
RAILS_LOG_LEVEL info Log verbosity

Web, Job and the Inference Adapter download nothing at runtime: the adapter’s models and the address data ship inside the images. Only a self-hosted LLM needs its models staged in advance. See Air-Gapped Deployments.

Some releases rename names the database stores, and deploy in a maintenance window: pause the queues, drain, stop Web and Job, migrate, start, resume. The release notes say when; the procedure is Maintenance-Window Upgrades.