Deployment guide

Tailcall

Overview

Tailcall is an open-source GraphQL platform that turns your existing REST, gRPC and GraphQL services into a single, unified GraphQL API. You describe how to compose your upstreams with a small set of declarative schema directives. The gateway then resolves each field by calling the right upstream, batching and caching requests along the way.

There is no resolver code to write and no runtime to provision, because Tailcall ships as a single self-contained native binary. This SecureAnyCloud image installs the official Tailcall binary at /usr/local/bin/tailcall. It runs as the tailcall service, bound to loopback 127.0.0.1:8000, behind nginx on port 80. nginx is ready for your TLS certificate.

Works out of the box. The image ships a starter configuration at /etc/tailcall/config.graphql. It composes the public jsonplaceholder.typicode.com REST API into GraphQL, so a real query returns composed data the moment the VM boots. Replace the configuration with your own upstreams and restart the service.

Secure by default, with no baked credential. Tailcall is an API gateway, so its GraphQL endpoint is reachable by design. The starter config fronts only a public demo API and defines no admin interface and no secret. This image carries no baked or shared credential. The gateway binds to loopback and is exposed only through nginx, which serves just the GraphQL endpoint and a health check. You supply any secrets your own upstreams require at runtime. They are never baked into the image.

Note on the interface. Tailcall is an API-first product. The GraphQL endpoint answers POST requests at /graphql, and there is no bundled browser UI. Drive it with curl, a GraphQL client or your own application. This guide uses curl.

What you get:

  • Tailcall (verified at v1.6.14) as the official single native binary
  • tailcall.service bound to 127.0.0.1:8000, running as a dedicated non-root user
  • nginx reverse proxy on port 80, ready for TLS
  • A working starter config at /etc/tailcall/config.graphql
  • tailcall-firstboot.service, which resolves the instance endpoint URL and writes a root-only info note
  • Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet.

Tailcall is lightweight, and throughput scales with the upstreams it calls. The recommended VM size is Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and light workloads. For production traffic, use Standard_D2s_v5 or larger.

1

Deploy from the Azure Portal

Search the Marketplace for Tailcall on Ubuntu 24.04 and choose your VM size. Attach an NSG that allows TCP 22 (SSH) from your management network. Allow TCP 80 and 443 (the GraphQL endpoint) from the networks and applications that need to query it. In production, front port 80 with TLS (see Step 8).

2

Deploy from the Azure CLI

RG="tailcall-prod"; LOCATION="eastus"; VM_NAME="tailcall-01" IMAGE_URN="sac1647283583153:tailcall-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 tailcall-vnet --address-prefix 10.90.0.0/16 --subnet-name tailcall-subnet --subnet-prefix 10.90.1.0/24 az network nsg create -g "$RG" --name tailcall-nsg az network nsg rule create -g "$RG" --nsg-name tailcall-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 tailcall-nsg --name allow-web --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 443 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:tailcall-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 tailcall-vnet --subnet tailcall-subnet --nsg tailcall-nsg --public-ip-sku Standard
3

Connect via SSH

ssh azureuser@<vm-ip>
4

Verify the services are running

Tailcall listens on loopback 127.0.0.1:8000, and nginx fronts it on port 80. nginx serves the health endpoint directly, so it always answers when the proxy is up.

sudo systemctl start tailcall nginx 2>/dev/null || true
sudo systemctl is-active tailcall nginx ss -tln | grep -E ':80 |127.0.0.1:8000' echo "GET /health -> HTTP $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/health) $(curl -s http://127.0.0.1/health)"

Both services report active, the two listening sockets appear, and GET /health -> HTTP 200 ok.

5

Send your first GraphQL query

The starter config exposes a Query type backed by the public demo REST API, plus an offline greet resolver. POST a GraphQL query to /graphql, and the gateway resolves each field by calling the upstream.

curl -s -H 'Content-Type: application/json' \
  -d '{"query":"{ posts { id title } }"}' http://127.0.0.1/graphql | head -c 400; echo
curl -s -H 'Content-Type: application/json' \
  -d '{"query":"{ greet }"}' http://127.0.0.1/graphql

The first query returns a JSON data.posts array of { id, title } objects. Tailcall fetches them from the REST upstream and reshapes them into GraphQL. The second query returns {"data":{"greet":"Hello World!"}} from the offline @expr resolver. Together they show that the gateway serves both remote and local fields.

6

Compose across upstreams

Tailcall's strength is stitching data together. In the starter config, Post.user is resolved with @call. A single GraphQL query therefore joins each post to its author, who lives at a different REST endpoint. You can also fetch a single record by argument.

curl -s -H 'Content-Type: application/json' \
  -d '{"query":"{ posts { id title user { id name email } } }"}' http://127.0.0.1/graphql | head -c 500; echo
curl -s -H 'Content-Type: application/json' \
  -d '{"query":"{ user(id: 1) { id name email } }"}' http://127.0.0.1/graphql

The first query returns posts with a nested user object joined from the users endpoint. The second returns a single user selected by the id argument. One GraphQL request triggers several upstream calls, which Tailcall batches and caches for you.

7

Replace the config with your own upstreams

/etc/tailcall/config.graphql drives the gateway entirely. Point the @http directives at your own REST endpoints, add types and fields, and use @grpc, @call and @expr as needed. Validate the config before you restart, so a typo never takes the gateway down.

sudo cat /etc/tailcall/config.graphql | sed -n '13,20p'
sudo -u tailcall /usr/local/bin/tailcall check /etc/tailcall/config.graphql

tailcall check reports Config ... ok when the schema is valid. After you edit the file, apply the change with a restart:

sudo systemctl restart tailcall
sudo systemctl is-active tailcall

For the full directive reference (@http, @grpc, @graphQL, @call, @expr, @server, @upstream), see the Tailcall documentation at https://github.com/tailcallhq/tailcall.

8

Enable HTTPS

nginx fronts the gateway on port 80 and is ready for TLS. Point a DNS record at the VM, then obtain a certificate with certbot. Certbot configures nginx for port 443.

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

After the certificate is issued, restrict the NSG so that only port 443 (and port 22 from your management network) is reachable. Then POST your queries to https://your-domain.example.com/graphql.

First boot, persistence and updates

The gateway binary and starter config live on the OS disk and are captured into the image, so a VM launched from this image is ready immediately. The tailcall-firstboot.service oneshot resolves this VM's endpoint URL and writes a root-only info note at /root/tailcall-info.txt. It then disables itself, so later reboots are unaffected.

There is no credential to rotate, because the shipped gateway fronts only a public demo API. The OS is patched at build time and ships with unattended security upgrades enabled.

/usr/local/bin/tailcall --version systemctl is-enabled tailcall nginx tailcall-firstboot.service
sudo sed -n '1,3p' /root/tailcall-info.txt;
sudo grep -E '^TAILCALL_URL' /root/tailcall-info.txt export DEBIAN_FRONTEND=noninteractive;
sudo apt-get update -y >/dev/null 2>&1 echo "held packages: $(apt-mark showhold | tr '\n' ' ')[none]"

Support

Every SecureAnyCloud deployment includes 24/7 support with a 24 hour response SLA. Contact sales@secureanycloud.com for help with deployment, configuration or scaling.

This is a repackaged open source software product with additional charges for SecureAnyCloud support services. Tailcall is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.

Deploy on Azure

Launch Tailcall 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