Setting up Cloudflare Argo & Access on a Raspberry Pi


A few nights ago I was casually browsing on /r/SelfHosted when I came across a post mentioning how insecure some of the home servers are regarding to their WAN access.

The obvious answer is Cloudflare Argo & Cloudflare Access. Before explaining everything, let’s clarify what this guide is about: If you are looking to access your homeserver from outside, for example, your Raspberry Pi, in a secure way without exposing ports on your own, this guide is for you.

Before you begin you will need a few things:

  • A Cloudflare account
  • A registered domain name with access to the DNS panel (ideally through Cloudflare but at the very least point the nameservers to them)
  • A Raspberry Pi or a server with your favorite Linux distribution
  • Some basic knowledge of Docker, shell commands and networking

What’s Cloudflare Argo & Access ?

Cloudflare Argo tunnels allows you to create an encrypted tunnel between your homeserver and the Cloudflare servers. This is done seamlessly, in a few lines of shell commands.

As you can see from the image below (from the Cloudflare Blog), you should consider the tunnel like a third party making sure you get the fastest access with the least risks of exposing your services.

Argo Tunnel

On the technical side you get a few features as a bonus like TLS certificates, DDOS protection and smart routing.

Another Cloudflare service is Access, which is part of Cloudflare Teams and it allows you to use their zero-trust infrastructure to access your services securely. What we are most interested in are the access policies and the application dashboard.

Cloudflare Access

If you are interested in more technical information you should consider reading their developer documentation.

Practical deployment

To illustrate how easy and magical it is, I will deploy from start to finish three docker containers (portainer, gluetun & librespeed) on a Raspberry Pi.

Get your Raspberry Pi OS Lite image and use balenaEtcher to write it down on your SD card. You can add an “ssh” file without any extensions to make your Raspberry Pi headless and accessible from your computer or just plug-it in.

Let’s get some updates:

sudo apt update 
sudo apt upgrade

We can now install Docker:

curl -sSL | sh

Add permissions to the current user:

sudo usermod -aG docker ${USER}

Let’s also install docker-compose:

sudo apt-get install libffi-dev libssl-dev
sudo apt install python3-dev
sudo apt-get install -y python3 python3-pip
sudo pip3 install docker-compose

You can enable the docker service:

sudo systemctl enable docker

Let’s deploy our docker containers, but before that a bit of explanation about the containers we are going to use:

  • Portainer is a GUI for docker.
  • Gluetun is a super awesome, vpn docker container, that allows you to route any other service through that container for additional privacy.
  • Librespeed is just a lightweight speedtest implementation and will serve as an exemple of network routing.

All these containers are just here to illustrate this practical example and are not necessary for the Cloudflare side of things.

Let’s start with Portainer:

docker run -d -p 8000:8000 -p 9443:9443 --name portainer \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data \

You can go to http://[your-machine-ip]:9443 and finish the Portainer setup on your own (and follow the official guide if needed)

If you did everything right, your Portainer dashboard should look like this (without the two other containers at this moment): Portainer

Now we can docker compose gluetun and librespeed in one file, please note that I’m using PIA vpn but you can use something else and even skip if needed. This is just an example of how to route a container through another one:

mkdir gluetunAndLibrespeed
cd gluetunAndLibrespeed
touch docker-compose.yml
nano docker-compose.yml

And paste the following lines:

The documentation of gluetun is here if you need help for your vpn.

version: "2.1"
    image: qmcgaw/gluetun
    container_name: gluetun
      - NET_ADMIN
      - /home/pi/gluetunAndLibrespeed:/gluetun
      - VPNSP=private internet access
      - 7777:80
    restart: unless-stopped

    container_name: librespeed
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
      - /home/pi/gluetunAndLibrespeed:/config
    network_mode: "service:gluetun"
      - gluetun
    restart: unless-stopped

To get this started, make sure to still be in the folder:

docker-compose up -d

Finally the Cloudflare part!

Let’s setup Cloudflare teams to configure our access rules and our dashboard

Go to the Teams area, you should have a configuration page with a teams name selection. I suggest you spend some time on the Teams dashboard to configure a default policy for your apps (I only use the one-time pin), once your understand the basics (policies, dashboard, etc..) let’s add our first self-hosted application.

Cloudflare Self-Hosted Selection

Cloudflare Self-Hosted Application setup

Once you are ready to add your first application, just give it a name, then a subdomain (like librespeed.[YOUR_DOMAIN].tld), choose your domain in the list, then click next to add the policy configuration that you feel comfortable with and you’re pretty much done for the web configuration.

We can use the tunnel as a service, docker container or standalone like we are doing right now. I’m following (and you should too) the great documentation provided by Cloudflare:

tar -xvzf cloudflared-stable-linux-arm.tgz
sudo cp ./cloudflared /usr/local/bin
sudo chmod +x /usr/local/bin/cloudflared
cloudflared -v

Let’s authenticate:

cloudflared tunnel login

Once this is done, you should have choosen a hostname (like “pi”) and we will use that for the creation of our tunnels. If I want to expose my librespeed container, I will create the tunnel:

cloudflared tunnel create pi librespeed.[YOUR_DOMAIN].tld

Finally you modify the configuration file the .cloudflared directory and it should look like this:

# url: http://localhost:9000
credentials-file: /home/pi/.cloudflared/XXXXXXXXXXXXXXXXXXX.json

- hostname: librespeed.[YOUR_DOMAIN].tld
  service: http://localhost:7777
- service: http_status:404

Congratulations, go to [YOUR_NAME] and that’s it, I will include a few screenshots of how it looks like in the browser.

Teams url access

Teams pin code

Teams dashboard

Teams librespeed app

If you enjoyed this guide you can also check the previous one about iCloud custom domains.