Cloudflare announced a few days ago that they are extending their Spectrum product, previously only available for Enterprise Plan, to Pro and Business Plan customers. Cloudflare has been offering reverse proxy services for HTTP and HTTPS traffic for years, but with Spectrum, it extends the security and performance benefits such as advanced DDoS attack protection and Argo Smart Routing technology to arbitrary TCP and UDP protocols, this includes SSH and Minecraft traffic. This is a good news for hobbyists, startups and small-medium size enterprises who need the enterprise-grade security but can't afford the Enterprise Plan.

I came across a Terraform recipe to automatically create an economical Minecraft server in a docker container running in Google Cloud. I can't remember how many times I forgot to turn off test resources running on a cloud services, only to be surprised by the bills later. By using preemptible instance, the VM will shut down automatically after 24 hours, but persists the world between sessions due to having a persistent disk. A reserved public IP is not really needed if you're using this recipe since you're connecting to the Spectrum app hostname instead of the IP address, but nice to have. This setup is perfect for me and my family to play occasionally.

This post explains how I extended the Terraform recipe to proxy the Minecraft and SSH traffic with Cloudflare Spectrum, adding an additional security layer to the server.

First, I installed Terraform, making sure that the version is at least v0.12 and above.

terraform version

I'm using gcloud command line tool to manage resources in Google Cloud. First, we'll need to authenticate the SDK to Google Cloud

gcloud auth application-default login

Next, I'll get the Project ID and save it in an environment variable to be used later. This is the project under which the resources created in Google Cloud will be charged for.

DEVSHELL_PROJECT_ID="$(gcloud config get-value project)"

Next, I created a service account to authorize Terraform to manage the resources under the project.

gcloud iam service-accounts create terraform --display-name "Terraform service account"

I created the service account key in JSON format to be used in the Terraform recipe. Here's where the project ID we exported previously in the environment variable is used for. This key can only be created once, so please remember where you downloaded it - in this case it is saved as key.json in the current directory.

gcloud iam service-accounts keys create ./key.json \
  --iam-account terraform@$

Next, we will clone the repository containing the Terraform recipe.

git clone

By default, Terraform will use a local backend to manage the state. However since I use Cloudflare frequently, I prefer to use Cloudflare Workers as the remote backend. Remote backend is especially useful if you work in a team with a few developers modifying the Terraform recipes at the same time. You can follow the guide here to setup. To use, I simply dropped the file describing the remote backend into the repository. However if you're the only person using the Terraform recipe, feel free to skip this and use the default local backend instead.

Next, I created Cloudflare API Token to authorize Terraform to manage resources in Cloudflare. Spectrum requires Zone Settings:Edit permission, so I added this one.

API Token authorizing Terraform to manage zone settings

I opened terraform.tfvars file in a text editor to add the generated API token, along with other values pertaining to the Cloudflare and Google Cloud account that I'm using.

# Cloudflare
cloudflare_api_token = "YOUR_CLOUDFLARE_API_TOKEN"
cloudflare_zone_id = "YOUR_CLOUDFLARE_ZONE_ID"
cloudflare_spectrum_hostname = "YOUR_SPECTRUM_HOSTNAME"

gcp_project = "YOUR_GCP_PROJECT_ID"
credentials_file = "key.json"
gcp_region = "asia-southeast1"
gcp_zone = "asia-southeast1-b"

When ready, I initialized Terraform in the directory containing the repository.

terraform init

I'd like to check all the resources that will be created first before we go ahead creating them.

terraform plan

If there's any errors, I'll add debug or trace level logging for additional information that will be useful to understand what went wrong, and run the terraform plan command again.


Once everything's good, I run the following command to let Terraform create the resources in Google Cloud and Cloudflare.

terraform apply 

This will seamlessly create the preemptible VM instance with docker container itzg/minecraft-server (feel free to change this to other container that you prefer) in a private network, segregated with a firewall that only allows Minecraft and SSH traffic from Cloudflare IPv4 IP ranges. This will ensure that no one will be able to directly scan or attack the server directly without proxying through Cloudflare. When connecting to the server from the Minecraft or SSH client, we'll have to specify the Spectrum app hostname (e.g. instead of the IP address.

When I'm done, I can simply stop the VM instance.

gcloud compute instances stop minecraft

Or if I don't need them anymore, I can destroy all the resources with a single command.

terraform destroy

Cost estimate

Approximate operating cost for Google Cloud is just slightly above $2 if I run it for 24 hours.

Reserved IP address$1.46/month
Reserved 10Gb disk$0.40
Preemptible VM instance$0.01/hour

We do need to have a paid plan with Cloudflare to use Spectrum though, but a Pro Plan will suffice for this use case.

Cloudflare PlanProtocolData AllowanceCost
ProSSH, Minecraft5GB/month$20/month
BusinessSSH, Minecraft, RDP10GB/month$200/month
EnterpriseSSH, Minecraft, RDP10GB/monthCustom price

The bandwidth we're paying are basically for Argo Smart Routing, the same technology behind the hugely popular Cloudflare VPN app Warp+. After the free cap we will be billed $1/GB. Traffic generated from DDoS attacks do not incur charges.

This was a good self-quarantine weekend project for me. Play safely, everyone!

This post is also available on DEV.