This guide walks you through deploying the Prebid Sales Agent for a single publisher using Docker Compose. The stack bundles PostgreSQL, the Sales Agent application, and an optional Nginx reverse proxy – everything you need to run in production.
The Docker Compose stack includes four services that work together:
┌──────────────────────────────────────────────────────────┐
│ Nginx (reverse proxy) │
│ Port 80/443 → upstream :8000 │
└──────────────────────────┬───────────────────────────────┘
│
┌──────────────────────────┴───────────────────────────────┐
│ adcp-server (Sales Agent) │
│ Port 8000 │
│ ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ Admin UI │ │MCP Server│ │ A2A Server │ │
│ │ (Flask) │ │(FastMCP) │ │ (JSON-RPC) │ │
│ └──────────┘ └──────────┘ └────────────────────┘ │
└──────────────────────────┬───────────────────────────────┘
│
┌──────────────────────────┴───────────────────────────────┐
│ PostgreSQL 16 │
│ Port 5432 │
│ Volume: postgres_data │
└──────────────────────────────────────────────────────────┘
| Service | Role | Port | Notes |
|---|---|---|---|
postgres |
Database | 5432 (internal) | PostgreSQL 16, persistent volume |
adcp-server |
Sales Agent application | 8000 | MCP, A2A, Admin UI, health check |
nginx |
Reverse proxy | 80, 443 | SSL termination, custom domain routing |
| Requirement | Minimum Version | Notes |
|---|---|---|
| Docker | 20.10+ | Install Docker |
| Docker Compose | 2.0+ | Included with Docker Desktop |
| Git | 2.0+ | To clone the repository |
No Python, Node.js, or other runtime is needed. Everything runs inside Docker containers.
Clone the repository and create your environment configuration:
git clone https://github.com/prebid/salesagent.git
cd salesagent
cp .env.template .env
Edit the .env file with your configuration. The following variables are the most important:
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Auto-configured | postgresql://salesagent:salesagent@postgres:5432/salesagent |
Set automatically by Docker Compose; override only if using an external database |
ENCRYPTION_KEY |
Yes | None | Fernet symmetric key for encrypting API keys and adapter credentials |
ADCP_AUTH_TEST_MODE |
No | false |
Set to true for evaluation only; enables test credentials |
CREATE_DEMO_TENANT |
No | false |
Creates a demo tenant with sample data on first boot |
Generate an encryption key:
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Or if you do not have Python installed locally:
docker run --rm python:3.12-slim python -c "
from cryptography.fernet import Fernet
print(Fernet.generate_key().decode())
"
Store your ENCRYPTION_KEY securely. If lost, all encrypted data (API keys, adapter credentials) becomes unrecoverable. Back it up outside of the deployment directory.
Launch all services in the background:
docker compose up -d
Docker Compose will:
ghcr.io/prebid/salesagent imageFirst startup may take 30-60 seconds while the database initializes and migrations run.
Confirm that all services are running and healthy:
# Check container status
docker compose ps
# Verify health endpoint
curl http://localhost:8000/health
Expected health response:
{"status": "ok"}
Verify all endpoints are accessible:
| Service | URL | Expected Result |
|---|---|---|
| Admin UI | http://localhost:8000/admin |
Login page or setup wizard |
| MCP Server | http://localhost:8000/mcp/ |
MCP endpoint (requires auth) |
| A2A Server | http://localhost:8000/a2a |
A2A endpoint (requires auth) |
| Health Check | http://localhost:8000/health |
{"status": "ok"} |
| Agent Card | http://localhost:8000/.well-known/agent.json |
JSON agent descriptor |
On first launch with no tenants configured, the Sales Agent enters Setup Mode. This provides a guided workflow to create your first publisher tenant.
http://localhost:8000/adminADCP_AUTH_TEST_MODE=true, log in with password test123When ADCP_AUTH_TEST_MODE=true, the following credentials are available for evaluation:
| Credential | Value | Purpose |
|---|---|---|
| Admin UI password | test123 |
Access the admin dashboard |
| MCP auth token | test-token |
Authenticate MCP tool calls |
| A2A bearer token | test-token |
Authenticate A2A requests |
Test credentials must be disabled before going to production. Remove ADCP_AUTH_TEST_MODE from your .env file and configure SSO authentication instead.
For production use, replace test credentials with OAuth:
/admin and navigate to Settings > SSO Configurationhttps://yourdomain.com/admin/auth/callbackSee the Admin UI Guide for detailed SSO configuration instructions.
To serve the Sales Agent on your own domain (e.g., ads.yourpublisher.com):
Update the Nginx configuration in nginx.conf with your domain:
server {
listen 80;
server_name ads.yourpublisher.com;
location / {
proxy_pass http://adcp-server:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
HTTPS is mandatory for production deployments. The Sales Agent enforces HTTPS for all non-localhost connections.
The recommended approach for SSL is Let’s Encrypt with automatic certificate renewal:
# Install certbot (example for Ubuntu)
sudo apt install certbot python3-certbot-nginx
# Obtain and configure certificate
sudo certbot --nginx -d ads.yourpublisher.com
Certbot will automatically update your Nginx configuration to handle SSL termination and redirect HTTP to HTTPS.
If you have an existing certificate, configure Nginx manually:
server {
listen 443 ssl;
server_name ads.yourpublisher.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
proxy_pass http://adcp-server:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
# Follow all service logs
docker compose logs -f
# View only the Sales Agent logs
docker compose logs -f adcp-server
# View only PostgreSQL logs
docker compose logs -f postgres
# View last 100 lines
docker compose logs --tail=100 adcp-server
# Stop all services (data preserved)
docker compose down
# Start all services
docker compose up -d
# Restart only the Sales Agent (e.g., after config change)
docker compose restart adcp-server
When updating to a new version:
# Pull latest images
docker compose pull
# Restart with new images
docker compose up -d
Migrations run automatically on startup, so database schema updates are applied when the new image boots.
This destroys all data including tenants, products, advertisers, campaigns, and audit logs. Use only for fresh starts during evaluation.
# Stop services and remove data volumes
docker compose down -v
# Start fresh
docker compose up -d
docker compose logs adcp-server
Common issues:
| Symptom | Cause | Fix |
|---|---|---|
connection refused on database |
PostgreSQL not ready | Wait 30 seconds and retry; check docker compose ps |
ENCRYPTION_KEY not set |
Missing environment variable | Add ENCRYPTION_KEY to .env file |
| Port 8000 already in use | Another service on that port | Stop the conflicting service or change the port in docker-compose.yml |
| Migration errors | Database schema conflict | Reset with docker compose down -v && docker compose up -d |
Ensure you are passing the auth token in the correct header format:
# MCP uses x-adcp-auth header
curl -H "x-adcp-auth: test-token" http://localhost:8000/mcp/
# A2A uses Authorization: Bearer header
curl -H "Authorization: Bearer test-token" http://localhost:8000/a2a
The /health endpoint checks database connectivity. Verify PostgreSQL is running:
docker compose ps postgres
docker compose logs postgres