0 Like
Using a Public Domain to Access Private Services

Using a Public Domain to Access Private Services

16 PV0 LikeHomelabTailScaleCloudflare
In the previous article, I explained how to configure a Mac mini homelab and use Tailscale to connect to it remotely. Although Tailscale provides MagicDNS, you still have to access services using the MagicDNS:port format. This article walks through, step by step, how to use a public domain with HTTPS to access services running inside your private network.

The limitation of Tailscale

Imagine the following setup:

  • Two Docker services:

    • Service A running on port `3000`

    • Service B running on port `3001`

  • A domain `abc.com` managed by Cloudflare

  • A public VPS with an IPv4 address

In a traditional setup, you would expose these services like this:

  • `a.abc.com` → Service A

  • `b.abc.com` → Service B

You’d add DNS records in Cloudflare and use Nginx to reverse-proxy traffic to the corresponding services.

Jietu20260126-182953@2x
Jietu20260126-182953@2x

What Changes with Tailscale?

Tailscale provides MagicDNS, for example:

homelab.xxxx.ts.net

However, MagicDNS does not support wildcard subdomains, which means:

You cannot access services via:

  • `a.homelab.xxxx.ts.net`

  • `b.homelab.xxxx.ts.net`

Instead, Tailscale offers the `serve` feature, which forces you to use:

  • `homelab.xxxx.ts.net:3000`

  • `homelab.xxxx.ts.net:3001`

Technically it works—but let’s be honest: It’s ugly.

Jietu20260126-174228
Jietu20260126-174228

Can We Use a Public Domain?

Yes—but with a catch.

You can configure a wildcard subdomain in Cloudflare, such as:

*.homelab.abc.com

and point it to your homelab’s private IP address (reachable only through Tailscale).

With this setup, once the Tailscale tunnel is active, you can access:

  • `a.homelab.abc.com`

  • `b.homelab.abc.com`

via a reverse proxy (Nginx or Caddy).

Sounds perfect, right?

Jietu20260126-183842
Jietu20260126-183842

The HTTPS Problem

There’s one critical issue.

Modern browsers strongly discourage (or outright block) visiting websites without HTTPS.

You might think:

“No problem, I’ll just get a Let’s Encrypt certificate.”

Unfortunately, that won’t work.

Let’s Encrypt will not issue certificates for:

  • Private IP addresses

  • Non-publicly routable endpoints (like Tailscale IPs)

So how do we get HTTPS?

Using a Cloudflare API Token

The solution is DNS-01 validation using Cloudflare’s API. Go to: https://dash.cloudflare.com/profile/api-tokens and click Create Token.

Jietu20260126-185900
Jietu20260126-185900
Use the Edit zone DMS template.

Jietu20260126-185916
Jietu20260126-185916
Set the permissions as follows:

Permissions

  • Zone → Zone → Read

  • Zone → DNS → Edit

Zone Resources

  • Include → All zones

Finish the setup and save the generated API token. Important: This token is shown only once—store it securely.

Jietu20260126-190004
Jietu20260126-190004

Reverse Proxy Your Services (with Caddy)

This time, we’ll use Caddy v2 instead of Nginx, because Caddy handles HTTPS automation extremely well.

Create the following structure:

. ├── caddy_config/ ├── caddy_data/ ├── Caddyfile └── docker-compose.yml

Jietu20260126-191720@2x
Jietu20260126-191720@2x

docker-compose.yml

Paste your Cloudflare API token into the environment section and start Caddy with: `docker-compose up -d`

services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" environment: - CLOUDFLARE_API_TOKEN=<YOUR_CLOUDFLARE_API_TOKEN> volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config volumes: caddy_data: caddy_config:

Install the Cloudflare DNS Module

The official Caddy image does not include the Cloudflare DNS module by default. Run the following commands:

docker exec -it caddy sh caddy add-package github.com/caddy-dns/cloudflare

Tailscale DNS Configuration (Important)

Before configuring Caddy, go to Tailscale Admin Console and add public DNS resolvers:

  • Cloudflare DNS: `1.1.1.1`

  • Google DNS: `8.8.8.8`

Path:

DNS → Global nameservers

This ensures DNS-01 validation works correctly inside the Tailscale network.

Jietu20260126-193300
Jietu20260126-193300

Configure the Caddyfile

Below is a minimal example.
Visiting `a.homelab.abc.com` will return a static response:

{ acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} } a.homelab.abc.com { respond / "Hello, world! This is a static response from Caddy." 200 }

Once this is working, you can replace `respond` with `reverse_proxy` to point to your actual services.

Using Tailscale to Manage Your Homelab

PREVIOUS POST

Using Tailscale to Manage Your Homelab

    Search by