Quickstart: GCP (Compute Engine)
Axemere Gateway sits between your applications and AI providers (OpenAI, Anthropic, etc.), enforcing policies, tracking attribution, and recording every request. This guide deploys the gateway as a binary on a GCP Compute Engine VM with Cloud SQL PostgreSQL.
Table of Contents
Prerequisites
- Google Cloud SDK (
gcloud) configured with a project and credentials that can create Compute Engine VMs, Cloud SQL instances, VPCs, service accounts, and Secret Manager secrets - A GCP project with billing enabled
curlandjqfor testing (installed on the VM in Step 3)- An OpenAI or Anthropic API key to store as a credential. You can skip this and test with a policy denial instead.
Architecture
The gateway runs as a systemd service on the VM. Cloud SQL is accessible over a private VPC IP; no public database endpoint is exposed.
Steps
Step 1: Provision the VPC and Cloud SQL Instance
Create a VPC with private service access so Cloud SQL uses an internal IP:
PROJECT_ID="your-gcp-project-id" REGION="us-central1" ZONE="us-central1-a" gcloud config set project $PROJECT_ID # Create a dedicated VPC gcloud compute networks create axemere-vpc \ --subnet-mode=custom \ --project=$PROJECT_ID # Create a subnet gcloud compute networks subnets create axemere-subnet \ --network=axemere-vpc \ --region=$REGION \ --range=10.0.0.0/24 \ --project=$PROJECT_ID # Enable Private Service Access for Cloud SQL gcloud compute addresses create google-managed-services-axemere \ --global \ --purpose=VPC_PEERING \ --prefix-length=16 \ --network=axemere-vpc \ --project=$PROJECT_ID gcloud services vpc-peerings connect \ --service=servicenetworking.googleapis.com \ --ranges=google-managed-services-axemere \ --network=axemere-vpc \ --project=$PROJECT_ID
Create the Cloud SQL instance (private IP only):
DB_PASSWORD=$(openssl rand -hex 24) echo "DB password: $DB_PASSWORD" # save this gcloud sql instances create axemere-db \ --database-version=POSTGRES_15 \ --tier=db-g1-small \ --region=$REGION \ --network=axemere-vpc \ --no-assign-ip \ --project=$PROJECT_ID gcloud sql databases create mvgc_gateway \ --instance=axemere-db \ --project=$PROJECT_ID gcloud sql users set-password postgres \ --instance=axemere-db \ --password="$DB_PASSWORD" \ --project=$PROJECT_ID
Get the private IP:
DB_IP=$(gcloud sql instances describe axemere-db \ --project=$PROJECT_ID \ --format='value(ipAddresses[0].ipAddress)') echo "Cloud SQL private IP: $DB_IP"
Step 2: Create the Compute Engine VM
Create a service account for the VM with minimal permissions:
gcloud iam service-accounts create axemere-gateway \ --display-name="Axemere Gateway" \ --project=$PROJECT_ID # Grant access to Secret Manager (for reading credentials) gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:axemere-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \ --role="roles/secretmanager.secretAccessor"
Create the VM on the same VPC:
gcloud compute instances create axemere-gateway \ --project=$PROJECT_ID \ --zone=$ZONE \ --machine-type=e2-medium \ --network=axemere-vpc \ --subnet=axemere-subnet \ --no-address \ --service-account="axemere-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \ --scopes=cloud-platform \ --image-family=debian-12 \ --image-project=debian-cloud \ --boot-disk-size=20GB \ --tags=axemere-gateway
Add a firewall rule to allow inbound traffic on port 7080 from within the VPC:
gcloud compute firewall-rules create axemere-gateway-internal \ --network=axemere-vpc \ --allow=tcp:7080 \ --source-ranges=10.0.0.0/24 \ --target-tags=axemere-gateway \ --project=$PROJECT_ID
For SSH access, add your IP to a firewall rule on port 22, or use
gcloud compute sshvia IAP:gcloud compute ssh axemere-gateway --tunnel-through-iap --zone=$ZONE
Step 3: Install the Gateway Binary
SSH into the VM and install the gateway:
gcloud compute ssh axemere-gateway --tunnel-through-iap --zone=$ZONE
On the VM:
# Install the Axemere APT repository curl -fsSL https://raw.githubusercontent.com/Axemere-LLC/mvgc-apt/main/gpg.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/mvgc.gpg echo "deb [signed-by=/etc/apt/keyrings/mvgc.gpg arch=$(dpkg --print-architecture)] \ https://raw.githubusercontent.com/Axemere-LLC/mvgc-apt/main stable main" \ | sudo tee /etc/apt/sources.list.d/mvgc.list sudo apt update && sudo apt install -y mvgc-gateway jq
Verify the install:
mvgc-gateway --version
Step 4: Store Secrets in Secret Manager
From your local machine (not the VM), store the admin token and database password in GCP Secret Manager:
ADMIN_TOKEN=$(openssl rand -hex 32) # Admin token echo -n "$ADMIN_TOKEN" | gcloud secrets create axemere-admin-token \ --data-file=- --project=$PROJECT_ID # Database URL echo -n "postgres://postgres:${DB_PASSWORD}@${DB_IP}:5432/mvgc_gateway?sslmode=disable" \ | gcloud secrets create axemere-db-url \ --data-file=- --project=$PROJECT_ID # AI provider key (optional — skip if testing with policy denial) echo -n "sk-..." | gcloud secrets create axemere-openai-key \ --data-file=- --project=$PROJECT_ID
Step 5: Configure and Start the Gateway
On the VM, create a startup script that reads secrets from Secret Manager at boot:
sudo mkdir -p /etc/mvgc sudo tee /etc/mvgc/env-from-secrets.sh << 'EOF' #!/bin/bash # Reads secrets from GCP Secret Manager and exports them as env vars. # Run as root at service start. export_secret() { local name="$1" local var="$2" local val val=$(gcloud secrets versions access latest --secret="$name" 2>/dev/null) if [ -n "$val" ]; then export "$var"="$val" fi } export_secret axemere-db-url DATABASE_URL export_secret axemere-admin-token MVGC_ADMIN_TOKEN export_secret axemere-openai-key OPENAI_API_KEY EOF sudo chmod 700 /etc/mvgc/env-from-secrets.sh
Create the systemd unit:
sudo tee /etc/systemd/system/mvgc-gateway.service << 'EOF' [Unit] Description=Axemere Gateway After=network-online.target Wants=network-online.target [Service] Type=simple EnvironmentFile=-/etc/mvgc/mvgc.env ExecStartPre=/bin/bash /etc/mvgc/env-from-secrets.sh ExecStart=/usr/bin/mvgc-gateway Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now mvgc-gateway
Check status:
sudo systemctl status mvgc-gateway sudo journalctl -u mvgc-gateway -f
Step 6: Verify the Deployment
From within the VPC (or via port-forward through IAP):
# If testing locally via IAP tunnel: gcloud compute start-iap-tunnel axemere-gateway 7080 --local-host-port=localhost:7080 --zone=$ZONE & curl -s http://localhost:7080/healthz | jq .
Expected response:
{"status":"ok", "version":"...", "node_id":"...", ...}
Step 7: Register a Workload and Send Your First Request
Export your admin token (from Secret Manager):
export MVGC_ADMIN_TOKEN=$(gcloud secrets versions access latest --secret=axemere-admin-token) GATEWAY_URL="http://localhost:7080" # adjust to your VM's internal IP if calling from another VM
Register a workload:
curl -s -X PUT "${GATEWAY_URL}/v1/admin/workloads" \ -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "workload_id": "wl-quickstart", "org_id": "org-quickstart", "name": "Quickstart Workload", "default_attribution": { "project_id": "proj-quickstart" }, "allowed_connection_types": ["direct_api"] }' | jq .
Submit a request (or test a policy denial without a key):
# Option A: with an OpenAI key stored in the gateway curl -s -X POST "${GATEWAY_URL}/v1/actions:execute" \ -H "Content-Type: application/json" \ -d '{ "schema": "mvgc.action_request.v2", "org_id": "org-quickstart", "workload_id": "wl-quickstart", "action": { "type": "ai.infer", "method": "POST", "target_host": "api.openai.com", "target_path": "/v1/chat/completions", "params": { "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Say hello in one sentence."}], "max_tokens": 50 } }, "attribution": { "project_id": "proj-quickstart" } }' | jq . # Option B: test a policy denial (no key needed) curl -s -X POST "${GATEWAY_URL}/v1/actions:execute" \ -H "Content-Type: application/json" \ -d '{ "schema": "mvgc.action_request.v2", "org_id": "org-quickstart", "workload_id": "wl-quickstart", "action": { "type": "ai.infer", "method": "POST", "target_host": "api.example-blocked.com", "params": {"model": "test"} }, "attribution": { "project_id": "proj-quickstart" } }' | jq .
Production Considerations
- Cloud SQL tier. The example uses
db-g1-small. Usedb-n1-standard-4or larger for production workloads. Enable high availability in the Cloud SQL settings. - VM sizing.
e2-mediumhandles light traffic. Usen2-standard-4for sustained load. - Load balancing. For external access or multiple VMs, put an internal GCP Load Balancer in front. Use a managed SSL certificate for TLS termination.
- Secret rotation. Rotate
axemere-admin-tokenand provider keys in Secret Manager. The gateway reads secrets at startup; restart the service after rotation. - IAP for SSH. Use Identity-Aware Proxy (
--tunnel-through-iap) instead of opening port 22 to the internet. No external IP is required on the VM. - VPC firewall. Keep the gateway port (7080) restricted to internal VPC ranges. Only expose it externally via a load balancer with TLS.
- Connecting to the control plane. To manage this gateway from
console.axemere.ai, see the Self-Hosted + CP Connected guide.
Next Steps
| Task | Where to look |
|---|---|
| Configure credentials and policies | Configuration Reference |
| Connect to the Axemere control plane | CP Connected Onboarding |
| Set up monitoring and observability | Network Operations Guide |
| Integrate your application | Developer Integration Guide |
| Use the managed gateway service | Managed Gateway Guide |
Cleanup
# Stop and remove the gateway service (on the VM) sudo systemctl stop mvgc-gateway sudo systemctl disable mvgc-gateway # From your local machine: gcloud compute instances delete axemere-gateway --zone=$ZONE --project=$PROJECT_ID gcloud sql instances delete axemere-db --project=$PROJECT_ID gcloud secrets delete axemere-admin-token --project=$PROJECT_ID gcloud secrets delete axemere-db-url --project=$PROJECT_ID gcloud secrets delete axemere-openai-key --project=$PROJECT_ID gcloud compute networks delete axemere-vpc --project=$PROJECT_ID