NovaPanel
Docs

Run NovaPanel behind a Cloudflare Tunnel

Homelab setup: expose the admin and customer panels through cloudflared without opening ports. Plus the honest list of what doesn't fit through an HTTP-only tunnel.

This guide is for homelabbers who want to expose NovaPanel without opening ports on their router. Cloudflare Tunnel (cloudflared) forwards traffic from Cloudflare's edge to a daemon running on your panel host, so nothing has to be publicly routable.

Cloudflare Tunnel only carries HTTP/HTTPS. Anything else NovaPanel serves (mail, FTP, authoritative DNS) needs a different solution. Read the table below before you commit to this setup.

What works, what doesn't

SurfaceVia Cloudflare Tunnel?Notes
Admin panel (port 2087)YesPoint a tunnel hostname at http://localhost:2087
Customer panel (port 2083)YesPoint a tunnel hostname at http://localhost:2083
Customer websites (Caddy 80/443)With caveats — see belowCaddy's automatic HTTPS depends on Let's Encrypt reaching port 80/443
Mail — Postfix SMTP (25/465/587)NoNot HTTP. Use a relay (SES, Mailgun, Postmark) or expose mail ports directly
Mail — Dovecot IMAP/POP (143/993/995)NoSame — not HTTP
FTP / FTPS (21 + passive range)NoUse SFTP over SSH instead, and tunnel SSH with cloudflared access ssh
Authoritative DNS (53)NoUse Cloudflare DNS for your zones and point NS records at Cloudflare

If your homelab use case is panel plus customer websites only, this setup works well. If you also need first-party mail or FTP, you'll need a public IP for those ports — Cloudflare Tunnel can't carry them.

Step 1 — Install cloudflared on the panel host

NovaPanel runs on Debian/Ubuntu. Install Cloudflare's official package:

sudo mkdir -p /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
  | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
  https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install -y cloudflared

Authenticate against your Cloudflare account (this opens a browser):

sudo cloudflared tunnel login

Create the tunnel and grab its UUID:

sudo cloudflared tunnel create novapanel
# → Created tunnel novapanel with id <UUID>
# → Tunnel credentials written to /root/.cloudflared/<UUID>.json

Step 2 — Tunnel config (panel only)

Drop this at /etc/cloudflared/config.yml:

tunnel: <UUID>
credentials-file: /root/.cloudflared/<UUID>.json

ingress:
  # Admin panel
  - hostname: admin.example.com
    service: http://localhost:2087
  # Customer panel
  - hostname: panel.example.com
    service: http://localhost:2083
  # Catch-all (required) — everything else gets a 404 from cloudflared
  - service: http_status:404

Route the hostnames through the tunnel:

sudo cloudflared tunnel route dns novapanel admin.example.com
sudo cloudflared tunnel route dns novapanel panel.example.com

Install and start the systemd unit:

sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

The two hostnames now serve the panel over Cloudflare's edge TLS. NovaPanel doesn't need to bind to a public IP — localhost:2083 / localhost:2087 is enough.

Step 3 — Customer websites: choose one of two modes

NovaPanel's Caddy issues Let's Encrypt certificates for every customer domain using the HTTP-01 (port 80) or TLS-ALPN-01 (port 443) ACME challenges. When Cloudflare proxies a hostname (orange-cloud), the challenge requests never reach your origin — Caddy can't get a cert.

Two ways around it today:

Mode A — DNS proxy off (grey-cloud)

For domains served by NovaPanel's Caddy, set the Cloudflare DNS record to DNS only (the grey cloud icon, not the orange one). Cloudflare publishes the A/AAAA record but doesn't proxy the traffic — Let's Encrypt can hit port 80 on your origin directly, and Caddy issues a real public cert.

Trade-off: those hostnames lose Cloudflare's CDN, WAF, and DDoS protection. They still get DNS hosting. You still need ports 80 and 443 reachable on your origin for the grey-clouded domains, which usually means router port-forwarding for those two ports.

Mode B — Cloudflare Full (strict) with origin cert

Keep the orange cloud, but install a Cloudflare Origin CA cert on the origin and disable Caddy's ACME. Cloudflare's edge trusts the Origin CA but browsers don't — so it only works behind Cloudflare.

  1. In Cloudflare → SSL/TLS → Origin Server, create a 15-year Origin CA cert covering *.example.com and example.com.
  2. Save the cert + private key on the panel host.
  3. Edit the customer site's Caddyfile to use the origin cert explicitly and disable automatic HTTPS:
    example.com {
      tls /etc/caddy/origin/example.com.pem /etc/caddy/origin/example.com.key
      root * /srv/sites/<user>/public_html
      # ... rest of the site config NovaPanel generated
    }
  4. Set SSL/TLS mode to Full (strict) in the Cloudflare dashboard.
  5. Add the hostnames to your cloudflared ingress, pointing at https://localhost:443 with originServerName set to the customer domain.

This keeps the orange cloud (CDN, WAF, DDoS) and avoids opening 80/443 on your router, at the cost of editing per-site Caddy configs.

Heads-up: NovaPanel doesn't natively manage Origin CA certs yet. If you re-save the site in the panel, automatic Caddy regeneration will overwrite your tls override. Native support for ACME DNS-01 (Cloudflare DNS) and Origin CA certs is on the roadmap.

Verification checklist

After setup, walk through these to confirm everything is wired up:

  • sudo systemctl status cloudflared → active (running)
  • https://admin.example.com opens the admin login
  • https://panel.example.com opens the customer login
  • A test customer site loads over HTTPS without a cert warning
  • Cloudflare → Network → "Free WAF rules" don't flag the panel's WebSocket endpoints (/api/customer/sites/*/terminal, /api/customer/notifications/stream) — if they do, add a bypass rule for those paths

Common gotchas

  • WebSockets idle out after 100s. Cloudflare's default WebSocket idle timeout is 100 seconds; the panel's terminal and notification streams will reconnect, but for long terminal sessions consider Cloudflare Zero Trust which lets you raise the timeout.
  • Large file uploads cap at 100 MB on Free plan. Cloudflare's body size limit applies to the File Manager and database imports. Pro bumps it to 200 MB, Business to 500 MB, Enterprise is configurable.
  • r.Host includes the tunnel hostname. The panel's impersonation redirect uses r.Host to construct the customer panel URL — make sure both hostnames are on the same root domain so the redirect works (e.g. admin.example.com and panel.example.com, not admin.org-a.com and panel.org-b.com).