How to Securely Publish Self‑Hosted Apps with Cloudflare Tunnel and Zero Trust (Docker How‑To)

Overview

Cloudflare Tunnel lets you expose private services to the internet without opening inbound firewall ports or managing a reverse proxy. It establishes an outbound-only, encrypted connection from your host to Cloudflare’s edge, and pairs perfectly with Cloudflare Zero Trust Access for SSO, device checks, and detailed auditing. In this tutorial, you will deploy Cloudflare Tunnel with Docker, route multiple apps under different subdomains, and protect them with Zero Trust policies. The steps are simple, reproducible, and friendly to environments behind NAT or CGNAT.

Prerequisites

- A Cloudflare account with a domain managed by Cloudflare DNS.

- Docker and Docker Compose v2 on a Linux host (Ubuntu/Debian/Alpine are fine).

- At least one internal web service running in Docker or on the host (e.g., Grafana on port 3000, Nextcloud on 80, Syncthing on 8384).

- Optional but recommended: an identity provider (Google, GitHub, Azure AD, Okta) to enforce SSO via Zero Trust Access.

Step 1 — Create a Tunnel in Cloudflare Zero Trust

1) Go to Cloudflare dashboard > Zero Trust > Access > Tunnels and click “Create a tunnel.” Name it (e.g., home-net).

2) Choose the Docker option. Cloudflare will generate a TUNNEL_TOKEN for you. Keep this token safe; it lets cloudflared authenticate the tunnel without storing local credentials.

3) You can add “Public Hostnames” later in the dashboard, or define routing via a local config file. This guide shows both approaches, starting with the fast token-only method.

Step 2 — Fast Deploy with Docker Compose (Token Method)

Create a directory (e.g., /opt/cloudflared) and a Docker Compose file. Replace TUNNEL_TOKEN_VALUE with the token you copied in Step 1.

version: "3.8"
services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    environment:
      - TUNNEL_TOKEN=TUNNEL_TOKEN_VALUE
    # Optional metrics for monitoring
    # ports:
    #   - "2000:2000"
    # command: tunnel --no-autoupdate run --metrics 0.0.0.0:2000

Bring it up:

docker compose up -d

Cloudflared will dial out to Cloudflare’s edge over QUIC/TLS. No inbound ports are needed. Next, from the Zero Trust dashboard, add “Public Hostnames” for each app:

- grafana.example.com → http://localhost:3000 (or your internal service URL)

- files.example.com → http://localhost:80

- sync.example.com → http://localhost:8384

Cloudflare automatically creates DNS records for these hostnames and routes traffic through the tunnel.

Step 3 — Config-Driven Ingress (Advanced, Reproducible)

If you prefer everything as code, create a named tunnel and a local config file. This offers better portability and version control. First, create the tunnel credentials once on any machine (can be the same host):

# Authenticate and create a named tunnel (locally)
docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel login

docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel create home-net

# Show tunnels (note the Tunnel UUID)
docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel list

This creates a credentials file named after the tunnel UUID. Now write config.yml in the same directory:

# ~/.cloudflared/config.yml
tunnel: HOME_NET_TUNNEL_UUID
credentials-file: /home/nonroot/.cloudflared/HOME_NET_TUNNEL_UUID.json
ingress:
  - hostname: grafana.example.com
    service: http://grafana:3000
  - hostname: files.example.com
    service: http://nextcloud:80
  - hostname: sync.example.com
    service: http://syncthing:8384
  - service: http_status:404
protocol: quic
warp-routing:
  enabled: false

If your apps run in Docker, place cloudflared on the same user-defined network so it can reach them by container name. Example Compose:

version: "3.8"
networks:
  apps:
    driver: bridge

services:
  grafana:
    image: grafana/grafana:latest
    networks: [apps]
    expose:
      - "3000"
    environment:
      - GF_SERVER_ROOT_URL=http://grafana.example.com

  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    networks: [apps]
    volumes:
      - ~/.cloudflared:/home/nonroot/.cloudflared:ro

Finally, register DNS routes (one-time):

docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel route dns home-net grafana.example.com

docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel route dns home-net files.example.com

docker run --rm -it \
  -v ~/.cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel route dns home-net sync.example.com

Step 4 — Protect with Zero Trust Access

Go to Zero Trust > Access > Applications > Add an application > Self-hosted. For each hostname you exposed, create an app and a policy:

- Choose your subdomain (e.g., grafana.example.com) and path (/*).

- Set a policy that requires SSO (Google/GitHub/Azure AD/Okta) or One-Time Pin.

- Optionally restrict to emails ending with @yourcompany.com or specific GitHub teams.

- Enable device posture checks if you use Cloudflare WARP (e.g., only allow managed devices).

With Access policies enforced, even if a URL leaks, visitors must authenticate before the origin sees any traffic.

Troubleshooting Tips

- 502/504 on a hostname: verify the upstream address in config points to a reachable service (container name + port or localhost + port). If using Docker networks, ensure both services share the same network.

- DNS not resolving: confirm the tunnel is “Healthy” and that the DNS record exists in your Cloudflare DNS zone. Propagation is usually instant because the record is proxied.

- Large uploads: consider enabling chunked uploads or tuning upstream app limits (e.g., client_max_body_size for Nginx-based apps). Cloudflare supports HTTP/2/3 on the edge; the tunnel runs over QUIC by default.

- Conflicting ports: cloudflared does not require inbound ports. If you exposed metrics on 2000, make sure it doesn’t collide with other services.

Security and Operations Best Practices

- Principle of least privilege: restrict Access policies to known users or groups; avoid wildcard “Allow all.”

- Separate hostnames for admin panels and public apps. Apply stricter policies (MFA, device checks) to admin endpoints.

- Use config as code where possible. Commit your cloudflared config.yml to a private repo and use environment-specific files for staging/production.

- Monitor tunnel health: expose metrics (–metrics 0.0.0.0:2000) and scrape with Prometheus. Alert on disconnects or high reconnect counts.

- Keep images updated: pin to a recent cloudflared tag and schedule updates. Test changes in staging first.

Wrap-Up

You deployed Cloudflare Tunnel with Docker, routed multiple internal services under friendly subdomains, and enforced Zero Trust Access in front of them—without opening a single inbound port. This pattern scales from homelabs to production, simplifies TLS and DNS, and raises your security baseline with SSO, device posture, logging, and revocation. If you outgrow manual steps, codify everything with config.yml, Compose files, and IaC for a repeatable, auditable setup.

🔐 How to Secure Your Linux Server Against Cyber Threats

Securing your Linux server against cyber threats is crucial for data safety and system stability. As cyberattacks become more sophisticated, applying fundamental security measures can protect your server from unauthorized access and breaches. In this article, we will explore essential techniques to enhance your Linux server security.

1. Keep Your System Updated

Updating your operating system and installed software ensures vulnerabilities are patched.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Enable automatic security updates:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

2. Use a Firewall (UFW or IPTables)

A firewall helps control traffic and block unwanted connections.

Using UFW:

sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status

Using IPTables:

sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -j DROP
sudo iptables-save > /etc/iptables.rules

3. Disable Root SSH Access & Change Default Port

For better security, disable root login and change the SSH port.

sudo nano /etc/ssh/sshd_config

Modify the following lines:

PermitRootLogin no
Port 2222
sudo systemctl restart sshd

4. Prevent Brute-Force Attacks with Fail2Ban

Fail2Ban blocks suspicious IP addresses after multiple failed login attempts.

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

To configure Fail2Ban settings:

sudo nano /etc/fail2ban/jail.local

Add the following lines:

[sshd]
enabled = true
bantime = 3600
maxretry = 5
sudo systemctl restart fail2ban

5. Enable Two-Factor Authentication (2FA) for SSH

Adding an extra authentication step enhances security.

sudo apt install libpam-google-authenticator
google-authenticator

6. Limit User Privileges with Sudo

Restricting admin privileges minimizes security risks.

sudo visudo

7. Monitor Logs & Detect Unauthorized Changes

Regularly check logs to identify security threats early.

sudo journalctl -xe
sudo cat /var/log/auth.log

Use AIDE to track system file changes:

sudo apt install aide
sudo aideinit

Conclusion

To secure your Linux server, a proactive approach is essential. Regular updates, firewalls, SSH security measures, and monitoring logs significantly reduce vulnerabilities. Implementing these practices will strengthen your server’s security and help prevent cyber threats.

SSH


How to Setup a Secure Home VPN Server

How to Setup a Secure Home VPN Server

In an era where data privacy is more important than ever, setting up your own VPN (Virtual Private Network) server at home is a great way to secure your internet traffic and protect sensitive information. In this guide, we’ll walk you through the process step-by-step, ensuring a seamless and secure setup.

Why Set Up a VPN at Home?

A VPN encrypts your internet traffic, making it nearly impossible for hackers or unauthorized users to intercept your data. While commercial VPNs are widely available, hosting your own VPN ensures complete control over your data and eliminates subscription fees.

Requirements

  • Reliable internet connection
  • A spare computer or Raspberry Pi
  • OpenVPN or WireGuard software
  • Static IP address or dynamic DNS service

Step 1: Install the VPN Software

Download and install OpenVPN or WireGuard on your server device. Both are free and open-source tools that provide robust security. Follow the installation instructions specific to your operating system (Windows, Linux, or macOS).

Step 2: Configure the VPN Server

Once installed, configure the server by generating encryption keys and setting up user profiles. For OpenVPN, use the EasyRSA tool to create certificates. If using WireGuard, generate public and private keys using the built-in key management tool.

Step 3: Port Forwarding

Access your router settings and forward the necessary ports (e.g., 1194 for OpenVPN or 51820 for WireGuard) to your VPN server's local IP address. This ensures that external devices can connect to your VPN.

Step 4: Set Up a Static IP or Dynamic DNS

To make your VPN accessible from anywhere, configure a static IP address or use a dynamic DNS service like No-IP or DuckDNS. This step links your VPN to a domain name or fixed address.

Step 5: Configure Client Devices

Download the corresponding VPN client software on your devices (PC, smartphone, tablet) and import the configuration file generated by your VPN server. Test the connection to ensure proper functionality.

Step 6: Enable Firewall and Security

Set up a firewall on your VPN server to block unauthorized access. Additionally, regularly update the software to patch any security vulnerabilities.

Conclusion

Setting up a home VPN server provides unparalleled security and control over your internet traffic. While the process may seem complex at first, following these steps ensures a smooth and secure setup. Take the time to secure your data today and enjoy the peace of mind that comes with it.

Visual Representation:

Home VPN


Popular Posts

Install Ollama and Open WebUI on Ubuntu 24.04 with NVIDIA GPU Acceleration (Step-by-Step)

Install Ollama + Open WebUI on Ubuntu 24.04 with NVIDIA GPU Acceleration (Step-by-Step)

Install a Local AI Chatbot on Ubuntu 24.04 with Ollama and Open WebUI (Step-by-Step)

Trending Now

Recovering from Btrfs Boot Failures Using GUI Tools on Fedora

By the end of this guide the reader will be able to identify a Btrfs‑based Fedora installation, boot from a live USB, list and restore snapshots using the graphical utilities btrfs‑assistant and snapper, and verify that the system returns to a functional state without resorting to the command line. Understanding the Btrfs Layout Used by Fedora Fedora Workstation and Fedora KDE install the root filesystem as a single Btrfs partition that contains two default sub‑volumes. One sub‑volume holds the traditional “/” hierarchy, while the second is dedicated to /var/lib/machines . The latter exists to keep container images out of snapshot operations; it remains empty on systems that do not run virtual machines. Because Btrfs stores data in sub‑volumes rather than separate partitions, a snapshot captures the state of an entire sub‑volume at a point in time. The installer (Anaconda) automatically registers these sub‑volumes with the snapper service. Snapper maintains a series of read‑only ...