
Modern hybrid cloud setups often require seamless, secure communication between services across cloud providers. While traditional VPNs can serve this purpose, they typically involve centralized architectures and complicated routing setups that may limit scalability and introduce latency.
Headscale, an open-source alternative to Tailscale's coordination server, allows you to self-host and manage your own Tailscale mesh VPN. Built on WireGuard, Tailscale creates direct, encrypted tunnels between endpoints without routing traffic through a central hub, delivering secure, low-latency networking with full control over device access and network rules.
By deploying lightweight gateway nodes in Vultr and Google Cloud Platform (GCP), and using Terraform and Ansible to handle provisioning and configuration, you can:
In this guide, you'll build a fully automated setup to connect Vultr and GCP environments using Headscale, enabling reliable peer-to-peer communication without relying on a traditional VPN gateway.
Before you begin, you need to:
Using Tailscale and a self-hosted Headscale controller, you can create a fully decentralized mesh VPN across cloud environments. This setup enables direct, encrypted communication between geographically or logically separated networks, including cloud providers like Vultr and GCP.
In this implementation, each cloud provider hosts a Gateway Node that participates in the mesh network. These gateways bridge their respective Virtual Private Clouds (VPCs), allowing traffic from internal application servers (Client Nodes) in one cloud to securely reach resources in the other, without exposing public services or relying on complex VPN hubs.
tailnet peer or Tailscale client in each Vultr and GCP region to join the mesh network across multiple datacenters..This implementation is automated using Terraform, Ansible, and Cloud-Init, enabling consistent, repeatable deployments across Vultr and GCP with minimal manual configuration
can_ip_forward = true).tailscale_servers) into Headscale, advertises VPC subnets, approves routes on Headscale, and injects Tailscale routes into the main table while preserving each node's local VPC kernel route.In this section, you set up the infrastructure with Terraform and configure instances after deployment using the provided Ansible playbooks. The following example uses these regions to illustrate the peering mesh:
You can expand or reduce the regions to match your requirements. The design supports multiple Vultr and GCP locations in the same mesh.
Clone the GitHub repository and navigate to the terraform directory under vpc-peering/vultr-gcp-regions.
Update the terraform.tfvars file with your credentials.
Populate your credentials and adjust regions if needed.
Populate the Vultr API Key and GCP Service Account Key in the terraform.tfvars file to enable API access for provisioning resources.
Adjusting regions allows you to scale deployments and optimize network performance.
vultr_regions and gcp_regions maps to redefine where gateway and client nodes deploy.headscale_region to move the Headscale server to a different region. The instance_plan and instance_os_id values are pre-selected to achieve maximum performance and ensure software compatibility.vultr_regions or gcp_regions maps. Terraform automatically iterates through all listed regions to provision nodes.This setup has been validated on Ubuntu 22.04, which is recommended for stability, security, and consistent results.
Tips for Region Selection:
List available Vultr regions using the API to choose appropriate datacenter locations for your instances.
List all GCP regions and zones with the gcloud CLI to determine where to deploy your VPCs and instances.
Initialize the Terraform working directory.
The command above sets up Terraform and downloads any required providers.
Review the execution plan.
Apply the Terraform manifests.
Your output should look similar to the one below:
When prompted, type yes to approve and apply the configuration. Provisioning may take up to 5 minutes depending on the number of regions.
Outputs:
These outputs provide important information about your deployed infrastructure:
Client and Gateway Summaries
client_summary: Contains details of all client nodes (Vultr and GCP), including Name, Private IP, Public IP, Provider, and Subnet. gateway_summary: Contains details of all gateway nodes with the same information.Headscale Control Server IP
headscale_control_server_ip: Shows the public IP of the Headscale control server, which coordinates the mesh network.Navigate to the Ansible directory:
Terraform automatically generates an inventory.yml file in this directory. Ansible uses this file to configure the mesh network, listing all Vultr and GCP gateway and client nodes.
Run the Ansible Playbook to configure the private network mesh.
This playbook waits for SSH and cloud-init, creates Headscale users and preauth keys for each gateway, enrolls the gateways into Headscale while advertising their VPC subnets, approves routes, enables IP forwarding, and updates the main routing table without affecting existing VPC routes.
To manually add more clients to the mesh network:
Vultr: During instance provisioning, select the vultr-x-gcp-mesh-script startup script and choose the corresponding VPC under VPC Networks. The script automatically configures the instance and adds all OSPF-announced routes. This startup script currently supports only Debian and RHEL instances.
GCP: When provisioning, select the respective VPC. No additional installation is required because the GCP VPC routes already contains the necessary routes.
Once deployment is complete, confirm that all Vultr and GCP regions are connected through the WireGuard mesh controlled by Headscale. Run the included Ansible playbooks to test both private and public routes, and capture metrics such as latency, throughput, and overall mesh stability.
Run the provided Ansible playbook to execute iperf3 and ping tests between regions using both private and public IPs.
The network tests may take 5-10 minutes to complete, depending on the number of regions being tested.
After completion, the playbook creates a network_test_results/ directory which contains the following results files:
network_results.txt: Consolidated output of all tests, including latency and bandwidth for public and private paths.public_results_table.txt: Tabular summary of region-to-region performance over the public internet.private_results_table.txt: Tabular summary of private mesh performance via Headscale.Review the test results for the public internet performance between nodes.
Your output should be similar to the one below:
Review the Private IP test results to validate the performance over the Tailscale mesh.
Your output should be similar to the one below:
Public tests usually show higher speeds due to direct internet routing and optimized infrastructure. Private mesh tests use encrypted WireGuard tunnels, which add overhead and may reduce throughput.
This guide demonstrated how to establish a secure, multi-region private mesh network between Vultr and GCP using Headscale and Tailscale. By leveraging Terraform and Ansible, you automated provisioning and configuration to ensure consistent deployments across regions. Gateway and client nodes exchanged traffic over encrypted, low-latency tunnels, while FRRouting and GCP custom routes enabled seamless cross-datacenter connectivity.
0 Comments
Be the first to comment and share your perspective with the community.