Expose kubernetes service using CloudFlare Argo Tunnel

Last February, I blogged how to use Inlets which allows you to expose your on-prem kubernetes cluster to the big bad internet using an exit node which is a public VPS. It works as it claims but this may not be for everyone as you need to pay extra $/mo for the public VPS. Like anything in IT- there’s many ways to skin a cat. Now, i’ll be detailing another way to achieve this using CloudFlare Argo Tunnel which unlike inlets, would not need a public VPS. Caveat Emptor: since we would be routing our traffic out to CloudFlare – you will lose control of security and data once it reaches cloudflare. If you prefer to have full control of data end-to-end, Inlets will be a better approach. Having said that, let’s get into it.

Disclaimer: I’m not affiliated with CloudFlare and Inlets. All information here are based on personal experience using the solutions.

Introduction to CloudFlare Argo Tunnel: https://blog.cloudflare.com/tunnel-for-everyone/

Last April 2021, CloudFlare announce Argo Tunnel will be free for everyone. This provided a low cost ramp-up to expose your on-premise kubernetes. There’s no guide in the internets to do this so i’ve decided to document it for everyone.

Overview of the Architecture


  • Domain Name that is controlled by CloudFlare
  • Kubernetes Cluster in your On-Premise (i’m using Tanzu Kubernetes Grid :))

Argo Tunnel Creation

Follow the steps on this link: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps. You only need to do:

  • Setup.
    • Use any linux/ mac machine by installing cloudflared installer
  • Create Tunnel
    • We need to manually create a tunnel cloudflared executable.

After creating tunnel, you’ll have the following files generated in .cloudflared directory:

  • cert.pem
  • tunnel-ID.json
    • We will be needing the JSON file for the kubernetes setup

Setup your On-Premise Kubernetes Cluster

Full manifest here: https://github.com/dmnt3d/k8s-cloudflared/blob/main/cloudflared.yaml

Items to edit are the following:

  • Copy the contents of the JSON file to credential-file configmap
apiVersion: v1
  cred.json: |-
      "AccountTag": # SUPPLY from *.json file in ~/.cloudflared/,
      "TunnelSecret": # SUPPLY from *.json file in ~/.cloudflared/,
      "TunnelID":# SUPPLY from *.json file in ~/.cloudflared/ ,
      "TunnelName": # SUPPLY from *.json file in ~/.cloudflared/
kind: ConfigMap
  name: credential-file
  namespace: cloudflared
  • Edit the config-file depending on the Ingress-Controller that will handle all request
    • Below, I use traefik to handle all the request. (Traefik service running in Traefik namespace) The config is setup to use a catch*all to allow us to route all traffic for all the CNAME records we will create later.
    • You can also create specific Hostname/ Service in case you need to create specific service mapping.
apiVersion: v1
  default.yaml: |-
    tunnel: <INSERT TUNNEL ID>
    credentials-file: /etc/cloudflared/cred.json
#    - hostname: # ADD specific hostname if needed
#      service: # 
# use catch-all service
    - service: http://traefik.traefik
kind: ConfigMap
  name: config-file
  namespace: cloudflared
  • Prometheus port is exposed at port 9090
        prometheus.io/path: /metrics
        prometheus.io/port: "9090"
        prometheus.io/scrape: "true"
        app: cloudflared
      - args:
        - tunnel
        - --config
        - /etc/cloudflared/default.yaml
        - --metrics

Dynamically Create CNAME for the Argo Tunnel

The following items details steps on how to create DNS records to our ArgoTunnel to create routing back to our on-prem resource.


ArgoTunnel relies on CNAME record to your tunnel ID to be able to route the traffic. Once external DNS is setup in your kubernetes cluster, this can easily setup as a k8s resource using the following manifest:

kind: Service
apiVersion: v1
  name: cname-test
    external-dns.alpha.kubernetes.io/hostname: # CLOUDFLARE PUBLIC DOMAIN
    external-dns.alpha.kubernetes.io/ttl: "120" # optional
  type: ExternalName
  externalName: #TUNNEL ID.cfargotunnel.com

Putting it all together

Now once we have everything setup, the way to expose a web application in the internet would require the following:

  • Deploy your application to your kubernetes cluster.
    • Make sure it has a service (duh). ClusterIP is enough
  • Create an Ingress to expose it behind your IngressController
    • In my example above, i’m using Traefik as an ingresscontroller with an IngressRoute CRD
    • Make sure the IngressRoute/ FQDN/ Public Domain should be the same as the public record cloudflare will be hosting
  • Create another Service to create a CNAME to create the public CNAME record for the service

After that -your service should now be accessible from outside 🙂

Hope that helps.

Leave a Reply


This site uses Akismet to reduce spam. Learn how your comment data is processed.