Deployment guide

Fides

Overview

Fides is an open source privacy engineering platform and the system of record for how personal data moves through your organization. You describe your systems and datasets against a shared privacy taxonomy, and Fides turns that description into working controls. It maps and inventories the personal data you hold, stores and enforces the consent and communication preferences your customers have given you, and automates data subject access and erasure requests by executing them against the systems where the data lives. Everything is available through a documented REST API as well as the web interface, so you can build privacy controls into your software delivery process.

This SecureAnyCloud image installs Fides 2.86.2 into a dedicated Python 3.13 virtual environment with a local PostgreSQL 16 database. Redis 7 serves as the cache and task broker. The web application runs under uvicorn bound to loopback, with a separate celery worker for privacy request execution, and nginx fronts the whole stack.

Fides holds several distinct secrets, and the upstream project publishes a working sample value for nearly all of them. This image therefore generates every one of them uniquely on the first boot of each VM: the encryption key that protects stored connection credentials and access tokens, the identifier and secret of the root API client, the administrator username and password, the token signing secret, the database password, the cache password, and the TLS certificate. None of them is baked into the image, and the running server refuses each published default.

What you get:

  • Fides 2.86.2 installed in a dedicated Python 3.13 virtual environment and run by uvicorn as the fides systemd service
  • The Fides Admin UI and REST API on :80, and on :443 with a per-VM self-signed certificate
  • A celery worker (fides-worker) that executes privacy requests off the web request path
  • PostgreSQL 16 bound to loopback, holding the data map, taxonomy, consent and privacy request tables
  • Redis 7 bound to loopback, serving as both the Fides cache and the celery broker
  • The fideslang privacy taxonomy seeded and ready to use, with 85 data categories, 56 data uses and 15 data subjects
  • A per-VM application encryption key that protects stored connection secrets, generated on first boot and never shared between VMs
  • A per-VM administrator account, root OAuth client ID and secret, token signing secret, database password and cache password
  • Fides' own anonymous readiness endpoints for Azure Load Balancer probes, which report the database and cache state rather than merely that a socket is open
  • fides.service, fides-worker.service, nginx.service, postgresql.service and redis-server.service as enabled and active systemd units
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and runs the full stack comfortably.

In the network security group (NSG), allow inbound 22/tcp from your management network and 443/tcp for the Admin UI and API. Allow 80/tcp only if you also want plain HTTP. Fides holds a map of where your personal data lives, so keep port exposure narrow and restrict access to the networks that need it.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Fides by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTPS (443).

Select Review + create, then Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:fides:default:latest
az vm create \
  --resource-group <your-rg> \
  --name fides \
  --image sac1647283583153:fides:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name fides --port 443 --priority 1001
az vm open-port --resource-group <your-rg> --name fides --port 80 --priority 1002

Step 3 - Confirm the services are running

SSH to the VM as azureuser and confirm that the whole stack is up.

Fides splits readiness across three anonymous endpoints. A bare GET /health reports only the web server and returns "cache": "skipped", so it stays green even when both datastores are unreachable. Ask for the cache explicitly, and check the database on its own route.

systemctl is-active fides fides-worker nginx postgresql redis-server curl -s "http://127.0.0.1/health?include_cache=true"; echo curl -s http://127.0.0.1/health/database; echo

Expected output:

active active active active active {"webserver":"healthy","version":"2.86.2","cache":"healthy"} {"database":"healthy","pools":{"api_sync_primary":{"health":"healthy","prewarming":null},"api_sync_readonly":{"health":"skipped","prewarming":null},"api_async_primary":{"health":"healthy","prewarming":null},"api_async_readonly":{"health":"skipped","prewarming":null}},"async_readonly_pool_prewarmed":null,"database_revision":"b034cd68950d"}

The first boot takes a few extra seconds, because that is when the per-VM secrets are generated and the application database is created. If the endpoints are not answering yet, wait a moment and try again.

Use /health/workers to confirm that the celery worker registered with the broker. Fides ships with task_always_eager enabled by default, which runs privacy requests inside the web process and leaves a worker idle. This image disables it, so the worker does the work.

curl -s http://127.0.0.1/health/workers; echo

Expected output (the worker name contains your VM's own hostname):

{"workers_enabled":true,"workers":["celery@<your-vm-hostname>"],"queue_counts":{"fidesops.messaging":0,"fides.privacy_preferences":0,"fides.privacy_request_exports":0,"fides.privacy_request_ingestion":0,"fides.dsr":0,"fidesplus.consent_webhooks":0,"fidesplus.discovery_monitors_detection":0,"fidesplus.discovery_monitors_classification":0,"fidesplus.discovery_monitors_promotion":0,"fidesplus.bulk_consent_import":0,"fides":0}}

The queue names prefixed fidesplus belong to Ethyca's commercial edition. The shared task routing declares them, and they stay empty on this open source image. Only the fides queues carry work here.

Step 4 - Retrieve the per-VM credentials

Every VM generates its own credentials on first boot and writes them to a root-only file. Nothing is shared between deployments, and there is no default password to change.

sudo stat -c '%a %U:%G %n' /root/fides-credentials.txt
sudo grep -E '^FIDES_ROOT_USERNAME|^FIDES_OAUTH_CLIENT_ID' /root/fides-credentials.txt

Expected output (your client id will differ, it is generated per VM):

600 root:root /root/fides-credentials.txt FIDES_ROOT_USERNAME=sacadmin FIDES_OAUTH_CLIENT_ID=f4dccfb4a240558e11bf4509

Read the whole file, including the administrator password and the OAuth client secret, with:

sudo cat /root/fides-credentials.txt

The file records the URL that Fides resolved for itself. Azure's instance metadata service returns an empty value for a Standard SKU public IP, so a VM can discover only its own private address. If you connect from outside the VNet, browse to the VM's public IP address or DNS name, which you can find on the VM's Overview blade in the Azure Portal.

You can re-run the credential check at any time. It shows that the per-VM credentials authenticate end to end and that each published upstream sample credential is refused.

sudo bash /usr/local/sbin/fides-verify-login.sh

Expected output:

FIDES_LOGIN_VERIFIED user=sacadmin health=ok token=ok oauth=ok defaults=rejected

Step 5 - Sign in to the Admin UI

Open https://<your-vm-public-ip>/ in a browser. The image ships a self-signed certificate generated for this VM, so your browser warns on the first visit until you install a certificate of your own (see Step 10). Sign in with the username and password from Step 4.

After you sign in, you land on the home view. It reports how many systems Fides is managing and offers the three tasks you will do most: add systems to your data map, review the systems already there, and process privacy requests.

Step 6 - Browse the privacy taxonomy

Everything in Fides is described against fideslang, a shared privacy taxonomy of data categories (what kind of personal data this is), data uses (why you process it) and data subjects (whose data it is). The image seeds the full default taxonomy on first boot, so it is ready to use immediately. In the Admin UI, open Core configuration -> Taxonomy.

The same taxonomy is available over the API. Authenticate first. The login endpoint expects the password base64 encoded, which is fiddly to do by hand. The image therefore ships fides-token, which reads this VM's own credentials and prints a fresh access token, keeping the password out of your shell history. Every API step below starts by minting a token this way, so you can run each step on its own.

TOKEN=$(sudo fides-token)
echo "access token length: ${#TOKEN}" curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/data_category \
  | python3 -c 'import json,sys; print(len(json.load(sys.stdin)), "data categories")'

Expected output (the token is a JWE, so its length varies):

access token length: 4412 85 data categories

The token is a bearer credential in its own right. Treat it like a password, and do not paste it into shared logs.

Step 7 - Add a system to your data map

A system in Fides is anything that processes personal data: an application you wrote, a database, or a third-party service. Each system carries one or more privacy declarations that state what data it handles, why, and about whom. Together, the systems form your data map.

Register a system:

TOKEN=$(sudo fides-token)
curl -sS -o /tmp/fides-system.json -w 'HTTP %{http_code}\n' \
  -X POST http://127.0.0.1/api/v1/system \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "fides_key": "customer_crm", "name": "Customer CRM", "system_type": "Service", "description": "Customer relationship management platform holding contact details and purchase history.", "privacy_declarations": [ { "name": "Contact customers about their orders", "data_categories": ["user.contact"], "data_use": "essential.service", "data_subjects": ["customer"] } ] }'

Expected output:

HTTP 201

Now read the data map back:

TOKEN=$(sudo fides-token)
curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/system | python3 -c ' import json, sys for s in json.load(sys.stdin): print("system:", s["fides_key"], "-", s["name"]) for d in (s.get("privacy_declarations") or []): print(" declares:", d["name"]) print(" data use: ", d["data_use"]) print(" data categories:", ", ".join(d["data_categories"])) print(" data subjects: ", ", ".join(d["data_subjects"])) '

Expected output:

system: customer_crm - Customer CRM declares: Contact customers about their orders data use: essential.service data categories: user.contact data subjects: customer

The system appears immediately in Data inventory -> System inventory in the Admin UI, with its data uses and description.

Step 8 - Machine to machine access with the root OAuth client

Interactive login is for people. For scripts and CI, use the root OAuth client. Its ID and secret are also generated per VM and recorded in the credentials file. The client exchanges its credentials for a bearer token.

CLIENT_ID=$(sudo sed -n 's/^FIDES_OAUTH_CLIENT_ID=//p' /root/fides-credentials.txt)
CLIENT_SECRET=$(sudo sed -n 's/^FIDES_OAUTH_CLIENT_SECRET=//p' /root/fides-credentials.txt)
curl -sS -X POST http://127.0.0.1/api/v1/oauth/token \
  -d "client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&grant_type=client_credentials" \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); print("token type:", d["token_type"], "| expires in:", d["expires_in"], "seconds")'

Expected output:

token type: Bearer | expires in: 691200 seconds

Step 9 - Review privacy requests

Privacy requests are Fides' term for data subject access and erasure requests. Open Privacy requests -> Request manager in the Admin UI to see the queue. A fresh VM has no requests until you connect a system and receive one.

The image sets require_manual_request_approval. Requests wait for an operator to approve them before the worker executes anything against your connected systems.

The queue is also available over the API:

TOKEN=$(sudo fides-token)
curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/privacy-request \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); print("privacy requests:", d.get("total", 0))'

Expected output on a fresh VM:

privacy requests: 0

Step 10 - Install your own TLS certificate

The image generates a self-signed certificate for the VM on first boot, so HTTPS works immediately. For production, replace it with a certificate for your own domain. Point a DNS record at the VM. Then either copy your certificate and key over the generated pair and reload nginx, or use certbot.

sudo cp /path/to/fullchain.pem /etc/ssl/fides/fides.crt
sudo cp /path/to/privkey.pem /etc/ssl/fides/fides.key
sudo chmod 0600 /etc/ssl/fides/fides.key
sudo nginx -t && sudo systemctl reload nginx

If you change the address Fides is reached on, add it to the allowed browser origins in /etc/fides/fides.toml under cors_origins, then restart the application:

sudo systemctl restart fides fides-worker

Server Components

Component Version Notes

Fides 2.86.2 Installed from the official ethyca-fides distribution into /opt/fides/venv

Python 3.13.15 Required by Fides 2.79.0 and later; Ubuntu 24.04's own interpreter is 3.12

PostgreSQL 16.14 Application database, bound to loopback

Redis 7.0.15 Cache and celery broker, bound to loopback, password protected

nginx 1.24.0 Reverse proxy on :80 and :443

Operating system Ubuntu 24.04 LTS Fully patched at build time, unattended security updates enabled

Filesystem Layout

Mount Size Purpose

/ 29 GB Root filesystem

/boot 881 MB Operating system kernel files

/boot/efi 105 MB UEFI boot partition (Gen2 Hyper V)

/mnt 16 GB Azure temporary resource disk

Key directories:

Path Purpose

/opt/fides/venv The Python 3.13 virtual environment holding Fides and its dependencies

/opt/fides/data The SaaS connector templates Fides loads at startup

/etc/fides/fides.toml The Fides configuration, holding every per VM secret (0600 fides:fides)

/etc/ssl/fides/ The per VM TLS certificate and private key

/root/fides-credentials.txt The per VM credentials, readable only by root (0600 root:root)

/var/lib/postgresql/16/main The PostgreSQL data directory

Managing the services

systemctl status fides --no-pager | head -5

Restart, stop and start the application and its worker together, because they share the same configuration:

sudo systemctl restart fides fides-worker
sudo systemctl stop fides fides-worker
sudo systemctl start fides fides-worker

Follow the logs:

sudo journalctl -u fides -f sudo journalctl -u fides-worker -f

Run the Fides CLI as the service account with the appliance configuration already applied:

sudo fides-cli --version

Expected output:

fides, version 2.86.2

Scripts and Log Files

Path Purpose

/usr/local/sbin/fides-firstboot.sh Generates every per VM secret and the application database on first boot

/usr/local/sbin/fides-write-config.py Renders /etc/fides/fides.toml; refuses to write any published upstream default

/usr/local/sbin/fides-verify-login.sh Proves the per VM credentials work and the published defaults are refused

/usr/local/sbin/fides-cli Runs the Fides CLI as the fides service account

/usr/local/sbin/fides-token Prints a fresh API access token for this VM's root account

/var/lib/sac/data/fides-firstboot.done First boot sentinel; its presence stops firstboot re-running

/var/lib/sac/data/fides-bootstrap.ready Bootstrap marker the application units are gated on

journalctl -u fides Web application log

journalctl -u fides-worker Privacy request worker log

/var/log/nginx/access.log nginx access log

On Startup

On the very first boot, fides-firstboot.service runs before the application. It resolves the VM's address, generates the application encryption key, the OAuth root client id and secret, the administrator password, the token signing secret, the database password and the cache password, rotates the PostgreSQL role, creates the application database from the schema template shipped in the image, generates the TLS keypair, writes /etc/fides/fides.toml and /root/fides-credentials.txt, and only then creates /var/lib/sac/data/fides-bootstrap.ready.

That last step matters. fides.service and fides-worker.service both carry ConditionPathExists on that marker, so on a first boot they physically cannot start until the secrets exist. The image ships without the marker, which is what guarantees the application never comes up against a half initialised database or an upstream default credential. On every subsequent boot the sentinel is present, firstboot exits immediately, and the services start normally.

Troubleshooting

The Admin UI returns 502. nginx is up but the application is not yet listening. Check systemctl status fides and sudo journalctl -u fides -n 50. On a first boot the application waits for firstboot to finish; give it a minute.

systemctl status fides shows the unit as inactive with a condition failure. The bootstrap marker is missing, which means firstboot has not completed. Check sudo journalctl -u fides-firstboot -n 50. Once firstboot succeeds the marker appears and the units start.

/health says healthy but the UI does not work. A bare GET /health reports only the web server. Ask for the cache with curl -s "http://127.0.0.1/health?include_cache=true" and check the database with curl -s http://127.0.0.1/health/database.

Login is refused. Confirm you are using the username and password from /root/fides-credentials.txt on this VM. Credentials are unique per VM and none of the upstream sample credentials will work by design. sudo bash /usr/local/sbin/fides-verify-login.sh will tell you which part of the chain is failing.

Privacy requests stay queued. Confirm the worker is registered with curl -s http://127.0.0.1/health/workers. If workers_enabled is false, the celery configuration in /etc/fides/fides.toml has been changed. Requests also wait for approval by design, so approve them in Privacy requests -> Request manager.

The browser warns about the certificate. The image ships a self signed certificate generated for the VM. Install your own certificate as described in Step 10.

Security Recommendations

  • Restrict the NSG so that 22/tcp, 80/tcp and 443/tcp are reachable only from the networks that need them. Fides holds a map of where your personal data lives, so treat it as sensitive infrastructure.
  • Replace the self signed certificate with one issued for your own domain and prefer 443/tcp over 80/tcp.
  • Back up /root/fides-credentials.txt somewhere safe, then consider removing it from the VM. The application reads its secrets from /etc/fides/fides.toml, not from that file.
  • Keep PostgreSQL and Redis on loopback. Neither needs to be reachable from the network for this appliance to work.
  • Create named user accounts in the Admin UI for your team rather than sharing the root administrator, and reserve the root OAuth client for automation.
  • Leave unattended security updates enabled so the operating system keeps receiving patches.

Support

SecureAnyCloud provides 24/7 support for this image. Contact sales@secureanycloud.com with your Azure subscription id and the VM name. For questions about Fides itself, see the Ethyca documentation and the Fides GitHub repository.

Deploy on Azure

Launch Fides on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure