Skip to content

These pages describe PrivacyFence 6.6.1. Download it · What changed

Organization deployment with Docker

This page runs the organization-mode daemon and its reverse proxy as containers. It replaces section 3 (service account and install), section 7 (reverse proxy and TLS) and section 8 (systemd unit) of the organization guide, and the Upgrade and Backup parts of its section 14. Sections 4, 5, 6, 9, 10, 11, 12, 13 apply unchanged.

The image

Image ghcr.io/privacyfence/privacyfence-org
Tags X.Y.Z for every stable release. X.Y, X and latest move forward only: each follows the newest stable release in its scope, and a patch release on an older line never moves them back. Pin X.Y in production.
Releases Stable releases only. A pre-release has no image.
Platforms linux/amd64, linux/arm64
User privacyfence-org, uid and gid 10001
Data volume /var/lib/privacyfence-org/.privacyfence
Port 8765 (not published by the Compose file below)
Stop signal SIGINT, so docker stop shuts the daemon down cleanly
Health check Probes the daemon's well-known endpoint from inside the container
Without a bundle Refuses to start (exit 78) and says how to install one

Prerequisites

  • Docker Engine 24 or newer with the Compose plugin 2.24 or newer.
  • A public hostname with DNS pointing at the server, and ports 80 and 443 (TCP and UDP) reachable. Caddy obtains the certificate itself.
  • Everything else in the guide's Prerequisites except Python.

Files

Work in /opt/privacyfence-org. Fetch the two deploy files for the release you run (replace vX.Y.Z):

sudo install -d /opt/privacyfence-org && cd /opt/privacyfence-org
sudo curl -fsSLO https://raw.githubusercontent.com/privacyfence/privacyfence/vX.Y.Z/deploy/docker/compose.yaml
sudo curl -fsSLO https://raw.githubusercontent.com/privacyfence/privacyfence/vX.Y.Z/deploy/docker/Caddyfile

Compose file

compose.yaml runs the daemon read-only, with no capabilities and no published port. Only Caddy is reachable from outside. Both containers sit on a private network with fixed addresses, so the bundle can name Caddy as the one trusted proxy.

# PrivacyFence organization mode with Caddy in front. docs/org-mode-docker.md explains every line.
name: privacyfence-org

services:
  privacyfence:
    image: ghcr.io/privacyfence/privacyfence-org:${PRIVACYFENCE_IMAGE_TAG:-latest}
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    stop_grace_period: 30s
    env_file:
      - path: privacyfence.env
        required: false
    volumes:
      - pf-data:/var/lib/privacyfence-org/.privacyfence
    networks:
      pf:
        ipv4_address: 172.30.87.10

  caddy:
    image: caddy:2
    restart: unless-stopped
    depends_on:
      - privacyfence
    environment:
      PF_HOSTNAME: ${PF_HOSTNAME:?set PF_HOSTNAME in .env}
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    networks:
      pf:
        ipv4_address: 172.30.87.2

networks:
  pf:
    ipam:
      config:
        - subnet: 172.30.87.0/24

volumes:
  pf-data:
  caddy-data:
  caddy-config:

Caddyfile

{$PF_HOSTNAME} {
    reverse_proxy privacyfence:8765
}

.env

Next to compose.yaml, in .env:

PF_HOSTNAME=pf.acme.example.com
PRIVACYFENCE_IMAGE_TAG=X.Y

PF_HOSTNAME is the public hostname from the bundle's --server-issuer-url. Compose refuses to start without it.

Build the bundle

Build it as in section 6, with --server-bind-host 0.0.0.0 --server-trusted-proxy 172.30.87.2 in place of --server-trusted-proxy 127.0.0.1, and the default port. The daemon listens on the Compose network, which only Caddy can reach, and Caddy's fixed address is the one proxy it trusts.

Install the bundle

The first time, while nothing runs, from /opt/privacyfence-org:

docker compose run --rm --no-deps -T --entrypoint sh privacyfence -c 'umask 077 && cat > /var/lib/privacyfence-org/.privacyfence/org/org_config.json' < org_config.json

To replace it later, while the daemon runs, use exec and restart:

docker compose exec -T privacyfence sh -c 'umask 077 && cat > /var/lib/privacyfence-org/.privacyfence/org/org_config.json' < org_config.json
docker compose restart privacyfence

Use exec here, not run: a run container would claim the daemon's fixed address 172.30.87.10 while the daemon holds it.

Start

docker compose up -d
docker compose logs -f privacyfence

Wait for the Org mode active -- MCP-over-HTTP at https://... line, then go on with section 9.

Environment variables

For --audit-forwarding-http-bearer-token-env NAME, put NAME=token in privacyfence.env next to compose.yaml, mode 0600. The file is optional. Never put the token in the bundle.

Upgrade

Change PRIVACYFENCE_IMAGE_TAG in .env, then:

docker compose pull
docker compose up -d

The advice in the guide's Upgrade applies: read the release's changelog entry first, use the build_org_bundle.py that matches the release for future bundle changes, and back up first. Going back is safe only if the older version reads the data as the newer one left it.

Backup and restore

Stop the daemon, archive the data volume, start it again:

docker compose stop privacyfence
docker compose run --rm --no-deps -T --entrypoint tar privacyfence -C /var/lib/privacyfence-org/.privacyfence -czf - . > privacyfence-backup.tgz
docker compose start privacyfence

To restore into an empty volume, run the same command with -xzf - < privacyfence-backup.tgz instead. The guide's Backup lists what the directory holds. The tarball holds every person's connector credentials: encrypt it and restrict access.

Administrative commands

Run them in the running container, for the same fixed-address reason as above:

docker compose exec privacyfence privacyfence-app --revoke-scoped-tokens PRINCIPAL_ID

Verify the image

gh attestation verify oci://ghcr.io/privacyfence/privacyfence-org:X.Y.Z --owner privacyfence

"Verification succeeded" proves GitHub built that image from the tagged commit of this repository, with the build.yml and publish-org-image.yml workflows.

A bind-mounted data directory

Instead of the named volume, a host directory must be owned by 10001:10001 with mode 0700, or the daemon refuses to start:

sudo install -d -o 10001 -g 10001 -m 700 /srv/privacyfence-org