Deploy an edge tunnel gateway with the edge-tunnel-gateway template
Deploy an edge tunnel gateway with the edge-tunnel-gateway template
Stand up a Pangolin tunnel endpoint on one Quake AI instance using the validated OpenTofu template edge-tunnel-gateway. You apply the template, complete the Pangolin dashboard setup, connect a Newt tunnel client from a private machine, register a resource with TLS on a domain, add an access policy, and confirm the service is reachable only through the endpoint.
The tunnel endpoint holds the only public floating IP. The private service stays behind a subnet with no inbound ports open and no address on the internet. You operate the endpoint and the tunnel client yourself; this is a self-hosted tunnel gateway you run in one region, not a global edge network or DDoS scrubber.
Monthly cost estimate
Pricing calculator ↗Sized as a custom package on shared vCPU.
Partial estimate: Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.
What each resource is for
Tunnel
s1a.small · 2 shared vCPU, 2 GiB RAM, 0.5 Gbps
s1a.small
s1a.small · 2 shared vCPU, 2 GiB RAM, 0.5 Gbps
Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.
Included in baseline
s1a.small
2 shared vCPU, 2 GiB RAM, 0.5 Gbps
s1a.small
2 shared vCPU, 2 GiB RAM, 0.5 Gbps
Compute + RAM rate basis
4 vCPU + 4 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.
Block storage (40 GiB)
40 GiB at $0.08/GiB/mo
Public IP (included)
1 included with the custom package
Package promotional discount
Flat −$5.00/mo on the custom package (same promotion as named plans).
Included at no charge
These line items are zero on Quake AI. Many other providers meter them separately.
Data transfer (inbound and outbound)
Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.
AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.
Learn morePrivate networking
Private networks, subnets, Neutron routers, and security groups are included with the plan.
VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.
Control-plane API requests
OpenStack API calls for provisioning and management are included.
Some managed services on other clouds meter API calls or charge for premium control-plane features.
Pricing data last validated: . For current rates, check quake.ai/pricing.
Prerequisites#
You need:
- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (
source openrc.sh). See the OpenStack CLI guide. - An SSH keypair that already exists in your project. Record its name for the
key_namevariable. - A copy of the
edge-tunnel-gatewaytemplate directory from the template reference page. - Two hostnames on a domain you control: one for the Pangolin dashboard (for example
pangolin.example.com) and one for the resource you expose (for exampleapp.example.com). Both DNS A records point at the endpoint floating IP. Follow How to point a domain at a Quake AI resource.
Step 1: Apply the tunnel endpoint template#
Copy the template's example variables file and set key_name, domain, base_domain, and letsencrypt_email:
cp terraform.tfvars.example terraform.tfvarskey_name = "YOUR_KEY_NAME"
domain = "pangolin.example.com"
base_domain = "example.com"
letsencrypt_email = "[email protected]"Point pangolin.example.com at the floating IP before Traefik requests a certificate. If DNS is not ready yet, apply with domain empty first, test over HTTP on the floating IP, then update domain and re-apply.
Initialize, preview, and apply:
tofu init
tofu plan
tofu applyOpenTofu provisions a private network, router, security group, data volume, endpoint instance, and floating IP. cloud-init installs Docker, starts Pangolin, Gerbil, and Traefik, generates a server secret on first boot, and writes setup instructions to /root/tunnel-admin-credentials.
Record the outputs:
tofu output floating_ip
tofu output dashboard_url
tofu output tunnel_endpoint
tofu output admin_hintWait three to five minutes for cloud-init and container health checks to finish.
Step 2: Complete Pangolin initial setup#
No admin credential ships in the repository. SSH to the endpoint and read the generated credentials file:
ssh ubuntu@YOUR_FLOATING_IP 'sudo cat /root/tunnel-admin-credentials'Note the dashboard_url, setup_token, and tunnel_endpoint values.
In a browser, open the dashboard URL from the credentials file (for example https://pangolin.example.com/auth/initial-setup). Paste the setup token, create the admin account, and sign in.
Port 3001 is not open in the endpoint security group. If you need the raw dashboard API during troubleshooting, open an SSH tunnel:
ssh -L 3001:127.0.0.1:3001 ubuntu@YOUR_FLOATING_IPStep 3: Stand up a private service and connect Newt#
This walkthrough runs the tunnel client on a private Quake AI instance with no floating IP. The same Newt container runs on a home lab or any machine behind CGNAT that has outbound internet access.
- Create a security group for the private host. Allow SSH from your workstation while you configure it; do not attach a floating IP.
openstack security group create tunnel-private-sg \
--description "Private service host; no inbound application ports from the internet"
openstack security group rule create \
--protocol tcp --dst-port 22 --remote-ip YOUR_IP/32 tunnel-private-sg- Launch a small instance on any private network that reaches the internet outbound (the template's
edge-tunnel-gateway-networks). Serve a test page on port 8080:
cat > private-cloud-init.yaml <<'EOF'
#cloud-config
package_update: true
packages:
- nginx
runcmd:
- |
echo 'private app ok' > /var/www/html/index.html
printf '%s\n' 'server {' ' listen 8080;' ' root /var/www/html;' '}' > /etc/nginx/sites-available/default
systemctl restart nginx
EOF
openstack server create \
--flavor s1a.small \
--image Ubuntu-24.04 \
--key-name YOUR_KEY_NAME \
--network edge-tunnel-gateway-net \
--security-group tunnel-private-sg \
--user-data private-cloud-init.yaml \
tunnel-privateWait until the instance reports ACTIVE. SSH to the private host and confirm the app responds locally:
curl -s http://127.0.0.1:8080/The response body should include private app ok.
-
In the Pangolin dashboard, create an organization if prompted, then create a Site for your private network. Pangolin prints a site ID and secret for the Newt client.
-
On the private host, run Newt with the site credentials and your dashboard URL:
docker run -d --name newt --restart unless-stopped --network host \
-e PANGOLIN_ENDPOINT=https://pangolin.example.com \
-e NEWT_ID=YOUR_SITE_ID \
-e NEWT_SECRET=YOUR_SITE_SECRET \
fosrl/newtReturn to the dashboard and confirm the site reports Online. Newt maintains an outbound WireGuard tunnel to tunnel_endpoint (UDP port 51820 on the floating IP).
Step 4: Register a resource with TLS#
In the Pangolin dashboard, add a Resource (or Application) for the private service:
- Set the public hostname to
app.example.com(a name under yourbase_domain). - Set the internal target to
http://127.0.0.1:8080on the site where Newt runs. - Enable HTTPS for the resource. Traefik on the endpoint obtains a certificate through the HTTP-01 challenge once DNS resolves.
Create a DNS A record for app.example.com pointing at YOUR_FLOATING_IP. Wait until it resolves:
dig +short app.example.comFrom your workstation, request the resource hostname. Pangolin should route traffic over the tunnel:
curl -s -o /dev/null -w "%{http_code}\n" https://app.example.com/Until you add an access policy in the next step, the status code may be 200 or 302 depending on your resource defaults. A 502 usually means Newt is offline or the internal target is wrong; check the site status in the dashboard and docker logs newt on the private host.
Step 5: Add an access policy#
In the Pangolin dashboard, open the resource you created and attach an access policy. For a first deployment, One-time PIN or Email PIN is enough to confirm identity-aware access without wiring an OIDC provider.
Save the policy. Unauthenticated browser requests should redirect to the Pangolin login or PIN flow instead of reaching the app directly.
Verify from your workstation without a session:
curl -s -o /dev/null -w "%{http_code}\n" https://app.example.com/The status code should be 302 or 401, not 200 with the app body. Sign in through a browser, complete the PIN or SSO step Pangolin presents, then load https://app.example.com/ again. The page should show private app ok.
Step 6: Confirm the private service is not reachable directly#
The private host has no floating IP and no inbound application ports open to the internet. Confirm you cannot reach the app on the private address from your workstation:
curl -s -o /dev/null -w "%{http_code}\n" --connect-timeout 3 http://PRIVATE_INSTANCE_IP:8080/ || echo "unreachable"The command should time out or fail to connect. Traffic to the app flows only through the authenticated tunnel endpoint at https://app.example.com/.
If you run Newt on a home network instead of a Quake AI private instance, the same check applies: the home router keeps port 8080 closed inbound; only the outbound tunnel carries traffic.
What you built#
- Applied the
edge-tunnel-gatewaytemplate to provision a private network, endpoint security group, data volume, Pangolin stack, and floating IP - Completed Pangolin initial setup and signed in to the dashboard
- Connected Newt from a private host over an outbound WireGuard tunnel
- Registered a resource at
app.example.comwith TLS terminated at the endpoint - Added an access policy so unauthenticated requests do not reach the private app
- Confirmed the private service is not reachable directly from the internet
Scope of this deployment#
This endpoint runs in one region on a VM you operate. It exposes a service through your Quake AI VM; it is not a global edge network and it does not absorb volumetric DDoS traffic. Quake AI has no anycast, no global PoPs, and no first-party CDN. For geographic distribution and edge absorption, front the origin with a third-party CDN.
The walkthrough adds a second small instance for the private service host; that VM is not part of the OpenTofu template. Newt also runs on a home lab or any outbound-only network when you prefer not to host the private side on Quake AI.
Next steps#
- Edge tunnel gateway template: parameters, ports, and resource map
- Edge reverse proxy template: TLS termination without tunneling when both sides sit on Quake AI
- Edge WAF template: L7 filtering on a public front door
- API gateway template: rate limits and key auth for APIs
- Security hardening checklist: audit security groups and floating IP usage
Clean up#
When you no longer need the deployment, destroy the OpenTofu stack and delete the private host you created in step 3:
tofu destroy
openstack server delete tunnel-private
openstack security group delete tunnel-private-sgOn the private host before deletion, stop Newt with docker rm -f newt. Remove DNS A records for pangolin.example.com and app.example.com.
See Also
Terraform and OpenTofu on Quake AI
Prerequisite
Networks
Prerequisite
Authoring IaC templates for Quake AI
Shares: Volumes, Security Groups
Deploy an API gateway with the api-gateway template
Shares: Volumes, Security Groups
Deploy a regional edge cache with the edge-cache template
Shares: Volumes, Security Groups