Self-Hosted Gateway
Self-Hosted + Control Plane: Your gateway runs on your own infrastructure and is managed from the Axemere cloud: policies, credentials, and analytics synchronized automatically. For the standalone gateway with no cloud connectivity, see Free Gateway.
Target time: under 5 minutes from cold start to first proxied request.
Prerequisites
- Docker: install Docker
- An Axemere account: sign in at console.axemere.ai
- An active Core Platform subscription. Activate it on the Services page.
curlandjqfor testing (included in most Linux/macOS installs)
Step 1: Generate an Enrollment Token
- Go to console.axemere.ai/gateways and click Enroll Node
- Enter a label for this node (e.g.
prod-us-east,local-dev,bst-keith-dev) - Click Generate Token
The dialog shows everything you need pre-filled. Click the .env file tab, then click Copy to copy the entire block.
The token is shown once and cannot be retrieved again. If you close the dialog before copying, generate a new token.
Tokens expire in 24 hours. If the gateway fails to start before the token is used, generate a new one.
Step 2: Start the Gateway with Docker
There are two Compose files:
docker-compose.postgres.yaml: includes a bundled PostgreSQL container. Use this one unless you already have a PostgreSQL 15+ instance you want to point the gateway at.docker-compose.yaml: gateway only. Requires you to setDATABASE_URLin.envpointing at your existing PostgreSQL 15+ instance.
Download the Compose file, then paste the .env content you copied in Step 1:
curl -fsSL https://github.com/Axemere-LLC/mvgc-releases/releases/latest/download/docker-compose.postgres.yaml \ -o docker-compose.yaml
Paste your copied .env content into a file named .env in the same directory, then start the gateway:
docker compose up -d
Wait a few seconds for the database to initialize, then verify:
curl -s http://localhost:7080/healthz | jq '{status, cp_status}'
Expected:
{"status": "ok", "cp_status": "connected"}
If cp_status is "offline" or "unconfigured", see Troubleshooting.
Step 3: Verify the Node Appears in the Console
- Go to console.axemere.ai/gateways
- Your node should appear with a green Connected status badge
If the node does not appear after 30 seconds, check the gateway logs:
docker compose logs gateway --tail=50
Step 4: Send Your First Request
4a. Add a Credential
The gateway uses this to authenticate to your AI provider on your behalf.
- Go to console.axemere.ai/credentials
- Click Add Credential
- Select your provider (e.g. Anthropic or OpenAI), enter your API key, enable Org Default, and save
Org Default makes the credential available to all gateway requests. Without it, the transparent proxy URL below (which carries no workload context) will fail to resolve a key.
The Control Plane syncs the credential to your gateway automatically. No restart needed.
4b. Send Your First Request
# The gateway resolves your credential from the Control Plane automatically
curl -s -X POST http://localhost:7080/proxy/anthropic/v1/messages \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 64,
"messages": [{"role": "user", "content": "Hello"}]
}' | jq .Check the Dashboard at console.axemere.ai. Your request appears in the traffic chart within a few seconds. For the full record (provider, model, tokens, latency, and policy decision), open the Records page.
To test policy enforcement (no API key needed):
curl -s -X POST http://localhost:7080/v1/actions:execute \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "schema": "mvgc.action_request.v2", "org_id": "'"$ORG_ID"'", "action": { "type": "ai.infer", "method": "POST", "target_host": "api.example-blocked.com", "params": {"model": "test"} } }' | jq .
This should return decision: "deny", confirming the gateway's default policy blocks unknown targets.
Install on your platform
Running somewhere other than Docker? Choose your platform:
Troubleshooting
cp_status: "offline" after startup
- Verify
MVGC_CP_ADDRisus.cp.axemere.ai:9090 - Verify
MVGC_ORG_IDmatches your Org ID (visible under Settings) - Verify
MVGC_BOOTSTRAP_TOKENwas copied in full and has not expired; manage tokens on the Gateways page - Check that port
9090outbound is not blocked by a firewall - Check the gateway logs:
docker compose logs gateway --tail=50
cp_status: "unconfigured"
The gateway started without MVGC_BOOTSTRAP_TOKEN set in .env.
Stop the stack, set the variable, and restart.
Node appears in console but shows Degraded
The gateway is connected but the last heartbeat took longer than usual. This clears automatically. If it persists, check the gateway logs for CP sync errors.
cp_status: "connected" but no data in the dashboard
The gateway is connected. Dashboard data appears after the first request is processed.
Check that the request returned decision: "allow" (not "deny") and that org_id in
the request body matches your Org ID.
Next Steps
| Goal | Resource |
|---|---|
| Add team members to your org | Team Guide |
| Configure provider credentials via the console | Credentials Guide |
| Write policies to allow/deny by workload or cost | Policies Guide |
| Deploy on GCP, AWS, or bare-metal | Install Guides |
| Monitor gateway health and request traffic | Network Operations Guide |