Creating an Edge Agent in Portainer CE is way more complex that it should be. I will explain how to get Portainer Edge Agent to work vir Cloudflare Tunnel.
Portainer Server Docker Compose
Make sure you expose port 9000 and port 8000 on your container server. Your compose file should look something like this:
version: "3"
services:
portainer:
image: portainer/portainer-ce:latest
ports:
- 9443:9443 # Main UI/API (HTTPS)
- 9000:9000 # Required for Edge Agent communication for /api (non HTTPS)
- 8000:8000 # Required for Edge Agent tunnel connection and remote management
volumes:
- data:/data
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
volumes:
data:
Port 9000: Used by the Edge Agent to communicate with the /api endpoint
Port 8000: Used for the tunnel connection between the agent and the server and for the server to remotely manage the edge agent.
Portainer Edge Agent via Cloudflare Tunnel Setup
For this post, I assume you already have a Cloudflare Tunnel up and running. You need to add 2 new public hostnames to your tunnel:
Tunnel 1 will be for the Portainer Edge Agent to communicate with the /api endpoint:

Tunnel 2 will be for the Portainer Edge Agent to establish a tunnel with the server for remote management:

Cloudflare WAF Firewall Rule
To make things more secure, add a WAF rule to only allow your remote server’s IP address to communicate with these endpoints. I am hosting in Oracle Cloud (free VMs) and they both have static IP addresses:

If you want to see what your VMs public IP address is, run this command:
curl 'https://api.ipify.org?format=json'
Portainer Server Setup
In Portainer, enable Edge compute. A entirely new menu will appear on the left hand side.

Next, add a new environment.

Select “Docker Standalone” and click on the “Start Wzard” button:

Select “Edge Agent Standard” and enter your API tunnel URL, for example “https://port-rem-api.mydomain.com“. We will add the tunnel URL shortly.

Don’t worry too much about the rest of the settings now, but change polling to something a bit more suitable as the default could be a bit too much. I chose 30 seconds but you can change it later on.
Click on the “Create” button.
Where things get tricky
Decode the Base64 string
Once you’ve clicked on “Create” the docker command will be displayed that you must run on your remote server. We are interested in the “EDGE_KEY” value.
EDGE_KEY is a base64 encoded string that contains the API URL you just entered as well as a second URL for the tunnel that Portainer derives from the API URL. But we need to change this second URL. (It also contains keys and such so keep this base64 string and its contents secure).
This is what my docker command looks like:

Go to https://www.base64decode.org/ and paste that EDGE_KEY value and click on “Decode“.
You will see the first hostname (the API endpoint hostame that you entered) and then a pipe character, and then the second URL and then a pipe character again. We need to modify this second URL between the 2 pipe characters.
This is what my decoded base64 string looked like:

Modify and Encode the Bae64 string
Now go to https://www.base64encode.org/ Here we will modify our string and encode it back to base64. That second URL between the 2 pipe characters must be your “Portainer Tunnel” endpoint. In my case, my tunnel endpoint is “https://port-rem-tun.mydomain.com“

Make sure you ticked the “URL-safe encoding” tickbox, and click on “Encode“.
Modify the Portainer Edge Agent docker command
Now copy that docker command that the Portainer Server gave you and paste it into a text file, and overwrite the “EDGE_KEY” value with your newly encoded string. Make sure to keep the “<space>\” at the end of the string, similar to all the other lines.

Running the command on the remote Portainer client
Head over to your client and paste that entire docker command. It will probably download the image and then start up.
You can check the logs of that container with this command:
docker logs -f portainer_edge_agent
The URL circled in red is the second URL we added to our EDGE_KEY. If we didn’t change that URL to the correct URL, the tunnel connection would’ve failed. If the client experiences any connection errors, you will see it in these logs.

Portainer Server Dashboard
Back in your Portainer server go to your home dahsboard. You will see your newly added Portainer Edge Agent and its heartbeat. Clicking on it will open the usual management interface.

By using Edge Agents you don’t have to expose your portainer agents port 9001 to the internet anymore.