Deployment
Production Setup
The Docker Quickstart works for local testing. Before going to production, follow this guide.
Checklist
- [ ] Set
NODE_ENV=production - [ ] Set
ADMIN_SESSION_SECRET(32-byte hex) - [ ] Set
CREDENTIAL_MASTER_KEY(32-byte hex) - [ ] Configure CA-signed TLS certificates (or use reverse proxy with valid cert)
- [ ] Mount a persistent data volume
- [ ] Set up a reverse proxy (recommended)
- [ ] Change or delete default admin password
- [ ] Back up
./data/regularly
Environment variables
Generate secrets before deploying:
openssl rand -hex 32 # use for ADMIN_SESSION_SECRET
openssl rand -hex 32 # use for CREDENTIAL_MASTER_KEY
Set in docker-compose.yml:
environment:
- NODE_ENV=production
- ADMIN_SESSION_SECRET=your_64_char_hex_here
- CREDENTIAL_MASTER_KEY=your_64_char_hex_here
Or pass via a secrets manager:
environment:
- NODE_ENV=production
- ADMIN_SESSION_SECRET_FILE=/run/secrets/session_secret
- CREDENTIAL_MASTER_KEY_FILE=/run/secrets/master_key
secrets:
session_secret:
external: true
master_key:
external: true
Critical: If you lose CREDENTIAL_MASTER_KEY, all stored user credentials are unrecoverable.
TLS certificates
MCP Ambassador auto-generates self-signed certificates on first boot. For production, replace them with CA-signed certificates.
Option 1: Provide certificates directly
Mount certificates at startup:
volumes:
- ./certs/server.crt:/data/certs/server.crt:ro
- ./certs/server.key:/data/certs/server.key:ro
- ambassador-data:/data
The server loads certificates from /data/certs/ on startup.
Option 2: Reverse proxy (recommended)
Let a reverse proxy handle TLS termination. The Ambassador Server listens on HTTP behind the proxy.
# In docker-compose.yml environment:
TLS_DISABLED=true
Then configure nginx, Caddy, or Cloudflare Tunnel as described below.
Reverse proxy
Caddy (simplest)
ambassador.example.com {
reverse_proxy localhost:9443
}
api.ambassador.example.com {
reverse_proxy localhost:8443
}
Caddy automatically obtains Let's Encrypt certificates.
nginx
server {
listen 443 ssl;
server_name ambassador.example.com;
ssl_certificate /etc/letsencrypt/live/ambassador.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ambassador.example.com/privkey.pem;
location / {
proxy_pass http://localhost:9443;
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;
}
}
Cloudflare Tunnel
cloudflared tunnel create ambassador
cloudflared tunnel route dns ambassador ambassador.example.com
# config.yml
tunnel: your-tunnel-id
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: ambassador.example.com
service: http://localhost:9443
- hostname: api.ambassador.example.com
service: http://localhost:8443
- service: http_status:404
Persistent data
All state lives in ./data/:
./data/
ambassador.db # SQLite database
certs/ # TLS certificates
credential_master_key # Master key (permissions 0600)
Use a named Docker volume or a host-mounted directory:
volumes:
ambassador-data:
driver: local
driver_opts:
type: none
device: /srv/ambassador/data
o: bind
Back up this directory regularly. The database and credential master key are your most critical files.
Disabling seed accounts
In NODE_ENV=production, the seed accounts (admin / admin123, qa-tester / test1234) are not created. You must create the first admin user via the Admin API using the ADMIN_KEY shown in startup logs:
# Get the admin key from startup logs
docker logs ambassador 2>&1 | grep "ADMIN_KEY"
# Create first admin user
curl -k -X POST https://localhost:9443/v1/admin/users \
-H "X-Admin-Key: adminkey_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"email": "admin@example.com",
"password": "your-strong-password",
"is_admin": true
}'
Health monitoring
# Client API health
curl -k https://your-server:8443/health
# → {"status":"ok","version":"0.8.0-beta.1"}
# Admin API health
curl -k https://your-server:9443/health
# → {"status":"ok","version":"0.8.0-beta.1","admin":true}
These endpoints require no authentication and are suitable for load balancer health checks.