Setting Up Pact Broker and PactFlow for Contract Testing
The Pact Broker is the hub of your contract testing workflow. Consumers publish pacts to it. Providers fetch pacts from it, run verification, and publish results back. The broker tracks which consumer/provider version pairs are compatible and answers the critical question: "can this version be safely deployed?"
You have two options: self-host the open-source Pact Broker, or use PactFlow (the hosted SaaS from the Pact maintainers). This guide covers both.
Self-Hosting with Docker Compose
The Pact Broker is a Ruby/Rack application backed by PostgreSQL. Running it locally or on a VM is straightforward with Docker Compose.
# docker-compose.yml
version: "3.8"
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: pact
POSTGRES_PASSWORD: pact_password
POSTGRES_DB: pact_broker
volumes:
- pact_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U pact"]
interval: 10s
timeout: 5s
retries: 5
pact-broker:
image: pactfoundation/pact-broker:latest
depends_on:
postgres:
condition: service_healthy
ports:
- "9292:9292"
environment:
PACT_BROKER_DATABASE_URL: "postgres://pact:pact_password@postgres/pact_broker"
PACT_BROKER_BASIC_AUTH_USERNAME: admin
PACT_BROKER_BASIC_AUTH_PASSWORD: admin_secret
PACT_BROKER_BASIC_AUTH_READ_ONLY_USERNAME: readonly
PACT_BROKER_BASIC_AUTH_READ_ONLY_PASSWORD: readonly_secret
PACT_BROKER_LOG_LEVEL: INFO
PACT_BROKER_BASE_URL: "http://localhost:9292"
# Allow public read (optional — useful for OSS projects)
# PACT_BROKER_ALLOW_PUBLIC_READ: "true"
volumes:
pact_postgres_data:Start it:
docker compose up -d
open http://localhost:9292The broker UI is available at http://localhost:9292. Log in with the admin credentials defined above.
Production Hardening
For a production deployment, add these environment variables:
environment:
# TLS termination happens at a load balancer; tell the broker its public URL
PACT_BROKER_BASE_URL: "https://pact.your-company.com"
# Disable the UI in favor of API-only access
# PACT_BROKER_DISABLE_SSL_VERIFICATION: "false"
# Webhook configuration
PACT_BROKER_WEBHOOK_SCHEME_WHITELIST: "https"
PACT_BROKER_WEBHOOK_HOST_WHITELIST: "api.github.com,hooks.slack.com"
# Allow specific IPs for webhook calls (CIDR notation)
# PACT_BROKER_WEBHOOK_HTTP_METHOD_WHITELIST: "POST"The PACT_BROKER_WEBHOOK_HOST_WHITELIST is a security control — it prevents the broker from being used as an SSRF proxy by limiting which hosts webhooks can call.
Configuring Webhooks
Webhooks are what make the contract testing feedback loop fast. Without webhooks, providers need to poll for new pacts. With webhooks, the broker notifies providers the moment a consumer publishes a contract change.
GitHub Actions Webhook
In the Pact Broker UI, navigate to Webhooks → Create webhook:
{
"events": [
{"name": "contract_content_changed"},
{"name": "contract_published_with_no_provider_versions"}
],
"request": {
"method": "POST",
"url": "https://api.github.com/repos/your-org/product-service/dispatches",
"headers": {
"Content-Type": "application/json",
"Accept": "application/vnd.github.v3+json",
"Authorization": "Bearer ${user.GitHubToken}"
},
"body": {
"event_type": "pact_changed",
"client_payload": {
"pact_url": "${pactbroker.pactUrl}",
"consumer_version": "${pactbroker.consumerVersionNumber}",
"consumer_branch": "${pactbroker.consumerVersionBranch}",
"provider": "${pactbroker.providerName}"
}
}
}
}Or via the Pact CLI:
pact-broker create-webhook \
--url "https://api.github.com/repos/your-org/product-service/dispatches" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $GITHUB_TOKEN" \
--data '{"event_type":"pact_changed"}' \
--request POST \
--description "Trigger provider verification on pact change" \
--contract-content-changed \
--broker-base-url http://localhost:9292 \
--broker-username admin \
--broker-password admin_secretSlack Notification Webhook
Add a Slack notification for verification failures:
{
"events": [{"name": "provider_verification_failed"}],
"request": {
"method": "POST",
"url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
"headers": {"Content-Type": "application/json"},
"body": {
"text": ":x: Provider verification failed!\n*Provider:* ${pactbroker.providerName}\n*Consumer:* ${pactbroker.consumerName}\n*Pact URL:* ${pactbroker.verificationResultUrl}"
}
}
}Using PactFlow (Hosted SaaS)
PactFlow is the enterprise version of the Pact Broker, hosted by the Pact maintainers. It adds:
- Secret management for webhook credentials (no plaintext tokens in webhook config)
- Role-based access control
- Team management
- Advanced can-i-deploy with environment tracking
- Bi-directional contract testing (OpenAPI + consumer tests without running provider)
Sign up at pactflow.io — the free tier supports 5 integrations (consumer/provider pairs), which is enough to evaluate the workflow.
After signing up, create a system account token under Settings → API Tokens. Copy the read/write token and store it in your CI secrets as PACT_BROKER_TOKEN.
Your broker URL will be https://your-org.pactflow.io.
All pact-broker CLI commands work identically against PactFlow — just replace the URL and use a Bearer token instead of basic auth:
pact-broker publish \
--consumer-app-version $GITHUB_SHA \
--branch $GITHUB_REF_NAME \
--broker-base-url https://your-org.pactflow.io \
--broker-token $PACT_BROKER_TOKEN \
pacts/Can-I-Deploy CLI Usage
can-i-deploy is the gate before deployment. It queries the broker for the verification matrix and returns a non-zero exit code if the deployment would be unsafe.
Basic usage:
# Can this version of order-service be deployed to production?
pact-broker can-i-deploy \
--pacticipant order-service \
--version $GITHUB_SHA \
--to-environment production \
--broker-base-url https://your-org.pactflow.io \
--broker-token $PACT_BROKER_TOKENOutput when safe:
Computer says yes \o/
CONSUMER | C.VERSION | PROVIDER | P.VERSION | SUCCESS?
---------------|-----------|-----------------|-----------|--------
order-service | abc123f | product-service | def456a | true
All verification results are published and successfulOutput when unsafe:
Computer says no ¯\_(ツ)_/¯
CONSUMER | C.VERSION | PROVIDER | P.VERSION | SUCCESS?
---------------|-----------|-----------------|-----------|--------
order-service | abc123f | product-service | MISSING | false
There is no verified pact between order-service (abc123f) and product-serviceRecording Deployments
When you actually deploy, record it in the broker:
pact-broker record-deployment \
--pacticipant order-service \
--version $GITHUB_SHA \
--environment production \
--broker-base-url https://your-org.pactflow.io \
--broker-token $PACT_BROKER_TOKENThis is what makes --to-environment production meaningful in subsequent can-i-deploy calls.
Environment-Based Verification Tags
In PactFlow, environments are first-class objects. Register them once:
pact-broker create-environment \
--name production \
--display-name Production \
--broker-base-url https://your-org.pactflow.io \
--broker-token $PACT_BROKER_TOKEN
pact-broker create-environment \
--name staging \
--display-name Staging \
--broker-base-url https://your-org.pactflow.io \
--broker-token $PACT_BROKER_TOKENIn your provider verification, use consumer_version_selectors to pull the right pacts:
// Verify pacts for consumers deployed to staging AND consumers on main branch
consumerVersionSelectors: [
{ deployedOrReleased: true }, // currently deployed anywhere
{ mainBranch: true }, // latest from main
{ matchingBranch: true }, // same branch name as provider (for feature branches)
],The matchingBranch: true selector is particularly useful for feature branch workflows: if a consumer has a feature/new-checkout branch and the provider has a matching feature/new-checkout branch, they automatically get tested against each other.
Network Visualization
The broker UI includes a network diagram showing all consumer/provider relationships and verification status. This is especially valuable when you have many services and need to understand the blast radius of a change.
Navigate to Network in the broker UI to see:
- All pacticipants (consumers and providers) as nodes
- Pact relationships as edges
- Color coding: green (verified), red (failed), grey (unverified)
For a self-hosted broker, the network view is available out of the box. For PactFlow, it's in the Matrix tab with additional filtering by environment.
Broker Maintenance
Cleaning Old Pact Versions
Pact versions accumulate over time. Clean up with:
# Keep only the latest 10 versions per pacticipant
pact-broker delete-pacticipant-version \
--broker-base-url http://localhost:9292 \
--broker-username admin \
--broker-password admin_secret \
--pacticipant order-service \
--version old-sha-hereOr automate cleanup with a scheduled job:
# Delete all versions tagged with branches that no longer exist in git
git branch -r | grep -v HEAD | sed 's/origin\///' > /tmp/live-branches.txt
# Then iterate pact-broker delete calls for dead branchesDatabase Backup
For a self-hosted broker, back up PostgreSQL regularly:
docker exec pact-broker-postgres-1 \
pg_dump -U pact pact_broker > pact_broker_backup_$(date +%Y%m%d).sqlStore backups in S3 or equivalent. A lost broker means re-publishing all pacts from CI history — painful but recoverable. A lost verification matrix means your can-i-deploy gates are blind until you rebuild it.
Self-Hosted vs PactFlow Decision
| Factor | Self-hosted | PactFlow |
|---|---|---|
| Cost | Infrastructure only | Per-integration pricing |
| Maintenance | Your team owns it | Managed |
| Secrets in webhooks | Plaintext in DB | Encrypted secret store |
| Bi-directional contracts | No | Yes (Enterprise) |
| RBAC | Basic (read-only user) | Full team/role model |
| Setup time | 30 minutes | 5 minutes |
For small teams (< 10 services), the free PactFlow tier is the fastest path. For large enterprises with compliance requirements around data residency, self-hosting gives you full control.