Deployment guide

WunderGraph Cosmo

Overview

Cosmo Router is the open source, Apache-2.0 licensed data plane of the WunderGraph Cosmo GraphQL federation platform. It is a single, statically compiled Go binary that federates many independent GraphQL services, called subgraphs, into one unified graph. Clients query that graph through a single endpoint.

The router plans and executes federated queries across your subgraphs, resolves shared entities by key and returns one joined response. A client can therefore ask for fields that live in several services in a single round trip.

This guide covers deploying Cosmo Router on Ubuntu 24.04 on Azure from the SecureAnyCloud Azure Marketplace image. The image runs the router standalone against a locally composed execution config, so it never needs the Cosmo Cloud control plane or a graph API token.

To make federation provable the moment the instance boots, the image ships a small two-subgraph demo (a products service and an inventory service), composed at build time with the Cosmo CLI. The router listens on the loopback interface and is fronted by nginx on port 80. An API key, generated uniquely for each VM on first boot and written to a 0600 root:root credentials file, gates the federated /graphql endpoint. A public /health endpoint stays open for liveness checks. The image is backed by 24/7 SecureAnyCloud support.

WunderGraph and Cosmo are trademarks of their respective owner. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by or sponsored by WunderGraph. It ships the free and open source, Apache-2.0 licensed Cosmo Router, unmodified. The Cosmo Studio SaaS control plane is not included.

What you get:

  • WunderGraph Cosmo Router v0.333.2 (the open source, Apache-2.0 licensed federation router), the official release binary run unmodified
  • A working demo supergraph composed at build time from two Apollo Federation v2 subgraphs (products on 127.0.0.1:4001, inventory on 127.0.0.1:4002), both bound to loopback only
  • nginx on port 80 reverse proxying the router, serving a public /health endpoint and gating /graphql behind a per-instance API key
  • cosmo-subgraph-products.service, cosmo-subgraph-inventory.service, cosmo-router.service and nginx.service as systemd units, enabled and active on boot
  • A unique API key generated perVM on first boot, never baked into the image, written to /root/wundergraph-cosmo-credentials.txt
  • Ubuntu 24.04 LTS base with latest security patches applied at build time
  • Azure Linux Agent for cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA

Prerequisites

  • An active Azure subscription
  • An SSH public key
  • A VNet and subnet in the target region
  • A subscription to the WunderGraph Cosmo Router listing on Azure Marketplace

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point. For higher request volumes, use Standard_D2s_v5 or larger.

In the network security group (NSG), allow inbound 22/tcp from your management network and 80/tcp for the GraphQL endpoint from the networks that call it.

1

Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for WunderGraph Cosmo Router by SecureAnyCloud, and select Create.

On Basics, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80). Then choose Review + create and Create.

2

Deploy from the Azure CLI

RG="cosmo-prod"; LOCATION="eastus"; VM_NAME="cosmo-01" IMAGE_URN="sac1647283583153:wundergraph-cosmo-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name cosmo-vnet --address-prefix 10.100.0.0/16 --subnet-name cosmo-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name cosmo-nsg az network nsg rule create -g "$RG" --nsg-name cosmo-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name cosmo-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:wundergraph-cosmo-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name cosmo-vnet --subnet cosmo-subnet --nsg cosmo-nsg --public-ip-sku Standard
3

Verify the services

Connect to the VM over SSH as azureuser. Confirm that the router, its two demo subgraphs and nginx are running, then check the listeners:

/opt/cosmo/router --version | head -5 systemctl is-active cosmo-subgraph-products cosmo-subgraph-inventory cosmo-router nginx | paste -sd' ' - sudo ss -tlnp | grep -E ':80 |:3002|:4001|:4002' | awk '{print $1" "$4}' | sort -u

The version prints 0.333.2, and all four services report active. The listeners show nginx on 0.0.0.0:80, the router on 127.0.0.1:3002 and the two subgraphs on 127.0.0.1:4001 and 127.0.0.1:4002. The router and subgraphs are bound to the loopback interface, so the only public entry point is nginx on port 80:

Router: Version: 0.333.2 active active active active LISTEN 0.0.0.0:80 LISTEN 127.0.0.1:3002 LISTEN 127.0.0.1:4001 LISTEN 127.0.0.1:4002
4

Check the health endpoint

The router exposes a public, unauthenticated /health liveness endpoint, served through nginx on port 80. It suits a load balancer health probe:

curl -s -o /dev/null -w 'GET /health -> HTTP %{http_code}\n' http://localhost/health
curl -s http://localhost/health; echo

This returns HTTP 200 with the body OK. The endpoint needs no authentication. The federated GraphQL endpoint, covered in the next steps, is gated separately.

5

Review the per-instance API key

An API key protects the federated /graphql endpoint. The key is generated uniquely on the first boot of each VM and written, along with the resolved public URL, to a 0600 root:root credentials file. The key is masked in this guide, but the real value is on your VM. View the file details:

sudo stat -c '%A %U:%G %n' /root/wundergraph-cosmo-credentials.txt
sudo grep -E '^COSMO_API_KEY=|^COSMO_URL=' /root/wundergraph-cosmo-credentials.txt \
  | sed -r 's/(COSMO_API_KEY)=.*/\1=<PER-VM-64-HEX-API-KEY>/'

The file is -rw------- root:root. The COSMO_API_KEY is a 64 hex character value unique to this VM. It is created by openssl rand -hex 32 on first boot, before nginx starts serving traffic. There is no default or known key in the image, and the same key gates every call to the federated endpoint.

6

Verify the API security model

The federated /graphql endpoint requires the per-instance API key in the X-API-Key header. Confirm that a missing or wrong key is rejected with 401, while the per-instance key is accepted with 200:

KEY=$(sudo grep '^COSMO_API_KEY=' /root/wundergraph-cosmo-credentials.txt | cut -d= -f2-) Q='{"query":"{ topProducts { id } }"}' curl -s -o /dev/null -w '/graphql (no key) -> HTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' -d "$Q" http://localhost/graphql curl -s -o /dev/null -w '/graphql (wrong key) -> HTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' -H 'X-API-Key: wrong' -d "$Q" http://localhost/graphql curl -s -o /dev/null -w '/graphql (real key) -> HTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' -H "X-API-Key: $KEY" -d "$Q" http://localhost/graphql

The calls with no key and with a wrong key are rejected with 401, and the call with the real per-instance key returns 200.

This is the security model. The health probe is open, and the per-instance key gates the federated GraphQL endpoint, so the router is never left as an open, unauthenticated data gateway.

7

Run a real federated query

This is the point of a federation router: one query, one endpoint, and data joined from several subgraphs. The products subgraph serves the topProducts field. The inventory subgraph serves inStock and deliveryEstimate for the same products.

The router plans the query, calls both subgraphs, resolves the shared Product entity by its key and returns a single joined response:

KEY=$(sudo grep '^COSMO_API_KEY=' /root/wundergraph-cosmo-credentials.txt | cut -d= -f2-)
curl -s -X POST http://localhost/graphql \
  -H 'Content-Type: application/json' \
  -H "X-API-Key: $KEY" \
  -d '{"query":"{ topProducts { id name price inStock deliveryEstimate } }"}' \
  | python3 -m json.tool

For each product, the response joins name and price from the products subgraph with inStock and deliveryEstimate from the inventory subgraph, in a single result:

{ "data": { "topProducts": [ { "id": "1", "name": "Cosmo Router T-Shirt", "price": 24.99, "inStock": true, "deliveryEstimate": "2-3 business days" }, { "id": "2", "name": "GraphQL Federation Mug", "price": 12.5, "inStock": false, "deliveryEstimate": "3-4 weeks" }, { "id": "3", "name": "WunderGraph Cap", "price": 18, "inStock": true, "deliveryEstimate": "1-2 business days" } ] } }

Neither subgraph can answer this query on its own. The products service does not know stock levels, and the inventory service does not know names or prices. The router makes them queryable as one graph.

8

Swap in your own subgraphs

The demo supergraph is composed from two local subgraph schemas and a small graph config. To federate your own services, edit the graph config, point it at your subgraphs, recompose the execution config with the Cosmo CLI and restart the router:

# The graph config lists each subgraph name, routing URL and SDL file:
sudo cat /etc/cosmo/graph.yaml
# The composed execution config the router loads at startup:
sudo head -c 300 /etc/cosmo/execution-config.json; echo

To point the router at your own subgraphs, replace the routing_url and schema.file entries in /etc/cosmo/graph.yaml with your services. Install the Cosmo CLI (npm install -g wgc). Recompose with sudo wgc router compose -i /etc/cosmo/graph.yaml -o /etc/cosmo/execution-config.json, then run sudo systemctl restart cosmo-router.

The two bundled Node demo subgraphs are systemd services (cosmo-subgraph-products, cosmo-subgraph-inventory). You can disable them once you have wired in your own upstreams.

For production graphs managed across a team, connect the router to the Cosmo Cloud schema registry instead of a static file. See the WunderGraph Cosmo documentation.

9

Call the endpoint from your own machine

From anywhere that can reach the VM on port 80, call the federated endpoint at the VM public IP with your per-instance API key. The /health check needs no credentials:

curl http://<vm-ip>/health
curl -s -X POST http://<vm-ip>/graphql \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: <YOUR-PER-VM-API-KEY>' \
  -d '{"query":"{ topProducts { id name inStock } }"}'

Fetch <YOUR-PER-VM-API-KEY> from /root/wundergraph-cosmo-credentials.txt on the VM (sudo grep '^COSMO_API_KEY=' /root/wundergraph-cosmo-credentials.txt).

Distribute the key to your GraphQL clients as their gateway credential. To rotate it, edit /etc/nginx/conf.d/cosmo-apikey.conf and reload nginx.

10

Use your own domain and HTTPS (production)

The image serves the endpoint over plain HTTP on port 80, which suits a load balancer or private network. For production, terminate TLS in front of the router so that credentials and payloads travel encrypted. You have two options:

  • Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port 80.
  • Install your own certificate. Add a TLS server block to /etc/nginx/sites-available/wundergraph-cosmo that references your certificate and key, open 443/tcp on the NSG, then run sudo systemctl reload nginx.

In both cases, point a DNS name you control at the VM public IP and use that name (and https://) for your GraphQL consumers.

11

Server components

  • Federation router: WunderGraph Cosmo Router v0.333.2 (open source, Apache-2.0)
  • Router endpoint: /graphql on loopback 127.0.0.1:3002, fronted by nginx on :80
  • API front: nginx on :80 reverse proxying to 127.0.0.1:3002
  • Demo subgraphs: products (127.0.0.1:4001) and inventory (127.0.0.1:4002), Apollo Federation v2, loopback only
  • Execution config: static, composed at build time by the Cosmo CLI (wgc router compose), with no control plane needed
  • Health endpoint: /health (public, no auth)
  • API key: per instance, generated on first boot into /root/wundergraph-cosmo-credentials.txt, and required on /graphql through X-API-Key

Support

Every SecureAnyCloud image is backed by 24/7 support with a guaranteed 24-hour response SLA. For help with this image, contact sales@secureanycloud.com.

WunderGraph and Cosmo are trademarks of their respective owner. This image ships the free and open source, Apache-2.0 licensed Cosmo Router, unmodified, and is not affiliated with or endorsed by WunderGraph. The Cosmo Studio SaaS control plane is not included.

Deploy on Azure

Launch WunderGraph Cosmo Router on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure