Pact Broker
Overview
Pact Broker is the open source contract testing hub for the Pact ecosystem. Consumer test runs publish their contracts to it, and providers fetch those contracts and publish their verification results back. can-i-deploy then answers the question a release pipeline needs answered: is this version safe to deploy, given everything it talks to?
The broker keeps the full history of every contract, the matrix of who verified what against which version, and the tags and branches that show where each version has reached. A web interface lets you read all of it. Webhooks can trigger the provider build when a consumer publishes a new contract, so both sides of an integration stay in step without a co-ordinated release.
This SecureAnyCloud image delivers Pact Broker 2.121.2 on Ubuntu 24.04 as a single node, served over TLS. A write credential and a separate read-only credential are generated on the first boot of your VM.
What you get:
- Pact Broker 2.121.2, the official upstream container image pinned by digest and run by systemd. The unit runs with
--pull=never, so the instance can never silently fetch a different build of the same version - nginx terminating TLS on port
443with a certificate generated for your VM. Pact Broker speaks plain HTTP only, and HTTP Basic sends the credential on every request, so the broker never leaves the loopback interface - PostgreSQL 16, native from the Ubuntu archive so it is patched by the same updates as the rest of the VM, with no TCP listener at all. The broker reaches it over a Unix socket, so port
5432is absent from every interface - Authentication required on every request, including the dashboard, the API, the status badges and the heartbeat. Pact Broker has no authentication of its own by default, and this image never lets an unauthenticated broker reach the network
- Two accounts, not one. A write credential for your pipeline and a separate read-only credential for people and dashboards, each with its own per-VM password
- The base URL the broker renders into every link, resolved per VM, so the
pactCLI tools and your CI jobs work against it immediately - The upstream example contracts that a new broker normally seeds itself with switched off, so the instance you receive is genuinely empty
- An on-VM self test that publishes a contract, reads it back, records a verification result, checks
can-i-deployboth ways and removes everything it created - The
postgresql,docker,pact-brokerandnginxsystemd services, enabled and active - 24/7 SecureAnyCloud support
Pact, Pact Broker and Ubuntu are trademarks of their respective owners. All product and company names are trademarks or registered trademarks of their respective holders, and their use does not imply any affiliation with or endorsement by them. This image packages the unmodified open source software, which is distributed under the MIT License.
Prerequisites
- An Azure subscription with permission to create virtual machines
- An SSH key pair
- A network security group (NSG) that allows inbound TCP 443 (the web interface and API) and 22 (SSH) from the addresses you trust. Nothing else needs to be open
Deploy from the Azure Marketplace
Search the Azure Marketplace for Pact Broker on Ubuntu 24.04 LTS by SecureAnyCloud, and choose Create.
Select a VM size. Standard_B2s (2 vCPU, 4 GiB) is the recommended size and is comfortable for a single team's broker. Add your SSH public key, allow inbound ports 443 and 22, and create the VM.
Deploy from the Azure CLI
Accept the image terms once per subscription, then deploy:
az vm image terms accept --urn sac:pact-broker-ubuntu-24-04:default:latest
az group create --name pact-broker-rg --location eastus
az vm create \
--resource-group pact-broker-rg \
--name pact-broker-vm \
--image sac1647283583153:pact-broker-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group pact-broker-rg --name pact-broker-vm --port 443 --priority 1010Standard_B2s (2 vCPU, 4 GiB) is the recommended size and is comfortable for a single team's broker.
Connect to your VM
ssh azureuser@<vm-public-ip>The account used to create the image does not exist on your VM. azureuser (or whichever administrator name you chose) is the only login.
Confirm the broker is running
First boot generates this VM's credentials and only then releases the broker. The first start therefore takes a little longer than later ones, while the database schema is created.
Check that all four services are running:
sudo systemctl is-active postgresql@16-main docker pact-broker nginxAll four report active. To see the broker container itself, run:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'The PORTS column reads 127.0.0.1:9292->9292/tcp. The broker is published to the loopback interface only. nginx is what the network reaches.
Check what the network can reach
sudo /usr/local/sbin/pact-broker-port-check.shThis script asserts the exact set of ports reachable from outside the VM, and fails if anything has changed. The shipped set is exactly 22 and 443.
There is deliberately no port 80. A port that exists only to redirect is still a port, and every URL this broker publishes is https from the first character. PostgreSQL has no TCP listener at all.
Retrieve the first-boot credentials
Every credential this broker uses was generated on this VM's first boot and exists on no other instance. They are written to a file that only root can read. Print it with the secrets masked:
sudo sed -E 's/^(PACT_BROKER_WRITE_PASSWORD=|PACT_BROKER_READ_ONLY_PASSWORD=|PACT_BROKER_DATABASE_PASSWORD=).*/\1<redacted>/' \
/root/pact-broker-credentials.txtThe masked output shows the shape of the file before you read the real values. The fields are:
- PACT_BROKER_URL: the web interface and API base URL for this VM
- PACT_BROKER_HOST: this VM's customer-facing address, resolved at first boot
- PACT_BROKER_WRITE_USER: the write account name, always pactbroker
- PACT_BROKER_WRITE_PASSWORD: the write password for this VM
- PACT_BROKER_READ_ONLY_USER: the read-only account name, always pactbroker-readonly
- PACT_BROKER_READ_ONLY_PASSWORD: the read-only password for this VM
- PACT_BROKER_DATABASE_USER: the PostgreSQL role the broker connects as
- PACT_BROKER_TLS_CERT: this VM's certificate, for clients that verify it
Read the non-secret values whenever you need them:
sudo grep -E '^PACT_BROKER_(URL|HOST|WRITE_USER|READ_ONLY_USER)=' /root/pact-broker-credentials.txtWhich credential goes where. The write credential can publish contracts, record verification results and delete data. Use it in your pipeline and nowhere else.
The read-only credential can browse the interface, read the matrix and answer can-i-deploy questions. Give this one to people and to dashboards. Keeping the two separate means that a person who only needs to look at the matrix never holds the credential that could rewrite it.
Confirm the door refuses everything but your credentials
sudo /usr/local/sbin/pact-broker-verify-auth.sh /root/pact-broker-credentials.txtThis is the check that matters most for this product. Pact Broker has no authentication at all unless credentials are supplied, so you need to know that the image really refuses everyone except you. Against the real TLS front door, the script proves that:
- the dashboard, the API, the heartbeat, the API browser and a status badge are all refused with
401when no credential is sent - weak and product-shaped password guesses are refused
- this VM's write credential is accepted
- this VM's read-only credential can read, and is refused when it tries to write
- the links the broker hands back carry
httpsand this VM's own address, with no plaintext link anywhere - the running broker reports version
2.121.2 - the broker's own port is unreachable from outside the VM, and nothing answers on
80or5432
Sign in to the web interface
Open https://<PACT_BROKER_HOST>/ in a browser. The broker uses HTTP Basic authentication, so there is no login form. The browser prompts for a username and password instead. Use the read-only account for day-to-day viewing.
The certificate is generated for your VM, so the browser warns about it until you install your own (see Step 14).
Each row is an integration between a consumer and a provider. The colour of the last-verified cell is the headline: green means the provider has verified the consumer's latest contract, and red means it has not.
Publish a contract from CI
A consumer's test run produces a contract file, and publishing it is a single authenticated PUT. First set the broker address and the write credential:
export PACT_BROKER_BASE_URL="https://<PACT_BROKER_HOST>"
export PACT_BROKER_USERNAME="<PACT_BROKER_WRITE_USER>"
export PACT_BROKER_PASSWORD="<PACT_BROKER_WRITE_PASSWORD>"These three variables are exactly what the official pact-broker CLI, the pact-cli container and every Pact client library read, so a pipeline needs no other configuration.
To publish a contract directly with curl, run:
sudo bash -c 'CF=/root/pact-broker-credentials.txt H=$(grep "^PACT_BROKER_HOST=" $CF | cut -d= -f2-) U=$(grep "^PACT_BROKER_WRITE_USER=" $CF | cut -d= -f2-) P=$(grep "^PACT_BROKER_WRITE_PASSWORD=" $CF | cut -d= -f2-) curl -sS --cacert /etc/ssl/pact-broker/pact-broker.crt --resolve "$H:443:127.0.0.1" \
-u "$U:$P" -o /dev/null -w "publish -> HTTP %{http_code}\n" \
-X PUT -H "Content-Type: application/json" \
-d "{\"consumer\":{\"name\":\"example-consumer\"},\"provider\":{\"name\":\"example-provider\"},\"interactions\":[{\"description\":\"a request for the example resource\",\"request\":{\"method\":\"GET\",\"path\":\"/example\"},\"response\":{\"status\":200}}],\"metadata\":{\"pactSpecification\":{\"version\":\"2.0.0\"}}}" \
"https://$H/pacts/provider/example-provider/consumer/example-consumer/version/1.0.0"'A 201 means the contract is stored. The consumer and provider are created automatically the first time they appear in a contract, so there is nothing to register in advance.
When you have finished with the worked example, remove it. Deleting a consumer or a provider also removes its contracts, versions and verification results:
sudo bash -c 'CF=/root/pact-broker-credentials.txt H=$(grep "^PACT_BROKER_HOST=" $CF | cut -d= -f2-) U=$(grep "^PACT_BROKER_WRITE_USER=" $CF | cut -d= -f2-) P=$(grep "^PACT_BROKER_WRITE_PASSWORD=" $CF | cut -d= -f2-) for n in example-consumer example-provider; do curl -sS --cacert /etc/ssl/pact-broker/pact-broker.crt --resolve "$H:443:127.0.0.1" \
-u "$U:$P" -o /dev/null -w "delete $n -> HTTP %{http_code}\n" -X DELETE \
"https://$H/pacticipants/$n" done'Record a verification result and ask can-i-deploy
The provider's build fetches the contract, runs it against the real provider, and publishes the outcome back. can-i-deploy then reads the matrix that these results build up.
The on-VM self test exercises both halves and both answers:
sudo /usr/local/sbin/pact-broker-roundtrip.sh /root/pact-broker-credentials.txtThe script publishes a contract with the write credential, reads it back with the read-only one, and records a verification result. It then asks can-i-deploy twice: once for the version it verified, which must be allowed, and once for a version whose contract was never verified, which must not be. Finally it deletes everything it created and confirms that the broker is empty again.
In a real pipeline, the same question is a single command before a deploy step. Set the read-only credential first:
export PACT_BROKER_BASE_URL="https://<PACT_BROKER_HOST>"
export PACT_BROKER_USERNAME="<PACT_BROKER_READ_ONLY_USER>"
export PACT_BROKER_PASSWORD="<PACT_BROKER_READ_ONLY_PASSWORD>"Reading the matrix needs only the read-only credential, so a deploy gate can run without the credential that could change the answer.
Read the matrix
The matrix shows which consumer version was verified against which provider version. It is what can-i-deploy consults.
Open it from any integration row in the dashboard.
Explore the API
Every page in the interface is backed by a link-following JSON API, and the broker ships an API browser for it. Open https://<PACT_BROKER_HOST>/hal-browser/browser.html.
The links in each response carry this VM's own address, because the base URL is resolved at first boot. A client can therefore follow them from a developer machine or a CI runner without any rewriting.
Back up your broker
Everything the broker knows (every contract, verification result, tag, branch and webhook) lives in one PostgreSQL database, so a backup is a single dump:
sudo -u postgres pg_dump -Fc pact_broker > /var/tmp/pact-broker-backup.dump && sudo ls -lh /var/tmp/pact-broker-backup.dumpCopy that file off the VM with scp and keep it somewhere durable. Restoring it onto a fresh instance restores the whole broker.
The credentials are not in the dump. They live in /etc/pact-broker/pact-broker.env, and a restored instance keeps the credentials that its own first boot generated, so a restore never hands anyone an old password.
Use your own certificate and domain
The shipped certificate is generated for your VM's address. To use a real domain, point DNS at the VM and replace the certificate and key. Then tell the broker its new address, so that the links it publishes match:
sudo cp /path/to/fullchain.pem /etc/ssl/pact-broker/pact-broker.crt
sudo cp /path/to/privkey.pem /etc/ssl/pact-broker/pact-broker.key
sudo chmod 0600 /etc/ssl/pact-broker/pact-broker.key
sudo sed -i 's#^PACT_BROKER_BASE_URL=.*#PACT_BROKER_BASE_URL=https://pact.your-domain.com#' /etc/pact-broker/pact-broker.env
sudo systemctl restart pact-broker nginxChanging PACT_BROKER_BASE_URL matters as much as the certificate. It is the address the broker writes into every link it returns, so a pipeline that follows those links goes wherever this value points.
Keep the image patched
The VM applies Ubuntu security updates automatically. Everything that terminates TLS, stores your data or faces the network (nginx, PostgreSQL and the container runtime) is an ordinary Ubuntu package, so it is covered. Check the update settings:
sudo grep -h 'APT::Periodic' /etc/apt/apt.conf.d/20auto-upgradesBoth lines read "1", which keeps the package lists refreshed and security updates installed without any action from you.
Pact Broker itself is a pinned upstream container image rather than an Ubuntu package, so apt never updates it and never warns you that it is old. The unit runs it with --pull=never and by digest, which stops an instance from quietly changing underneath you.
To move to a newer release, take the Step 13 backup first. Pull the tag you want with sudo docker pull, and replace the image reference in /etc/systemd/system/pact-broker.service with the new digest. Then run sudo systemctl daemon-reload and sudo systemctl restart pact-broker. The database schema migrates automatically on the next start.
Rotate the credentials
You can change both broker passwords at any time. Nothing is baked into the data. Rotate the write password with:
sudo bash -c 'NEW=$(openssl rand -hex 16) sed -i "s#^PACT_BROKER_BASIC_AUTH_PASSWORD=.*#PACT_BROKER_BASIC_AUTH_PASSWORD=${NEW}#" /etc/pact-broker/pact-broker.env sed -i "s#^PACT_BROKER_WRITE_PASSWORD=.*#PACT_BROKER_WRITE_PASSWORD=${NEW}#" /root/pact-broker-credentials.txt systemctl restart pact-broker echo "write password rotated"'Confirm that the new credential works and the full security posture still holds:
sudo /usr/local/sbin/pact-broker-verify-auth.sh /root/pact-broker-credentials.txtRotate the read-only password the same way, using PACT_BROKER_BASIC_AUTH_READ_ONLY_PASSWORD and PACT_BROKER_READ_ONLY_PASSWORD.
Troubleshooting
The broker will not start. The broker deliberately waits for first boot to complete, because a broker with no credentials file would be a broker with no authentication. Check the first-boot service and its sentinel file:
sudo systemctl show -p ActiveState -p Result pact-broker-firstboot.service
sudo test -f /var/lib/sac/data/pact-broker-firstboot.done && echo "first-boot sentinel present"Result=success together with the sentinel means first boot completed and the broker has been released. If the sentinel is missing, sudo journalctl -u pact-broker-firstboot.service shows why.
Everything returns 401. This is the intended behaviour when no credential is sent. Check that you are using the right account for the task: reads accept either credential, and writes accept only the write credential.
The links point at the wrong address. The broker publishes whatever PACT_BROKER_BASE_URL says. Check its value:
sudo grep '^PACT_BROKER_BASE_URL=' /etc/pact-broker/pact-broker.envCheck the whole appliance at once. Run the full self test:
sudo /usr/local/sbin/pact-broker-selftest.sh /root/pact-broker-credentials.txtSupport
SecureAnyCloud provides 24/7 support for this image. Support covers deployment, upgrades, TLS certificates and custom domains, CI and pipeline integration, credential and webhook management, and database backup and scaling.
Contact support through the Azure Marketplace listing or at secureanycloud.com.
Deploy on Azure
Launch Pact Broker 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