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.

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.

Previous
Docker (Quickstart)