This is a walkthrough for how I use a personal domain name hosted at Cloudflare to direct traffic via Tailscale to a sidecar NGINX Proxy Manager (NPM) Docker container on my Synology NAS which, in turn, I use to obtain SSL certificates and reverse proxy requests to various Docker containers. Purchasing a domain, transferring the nameservers to Cloudflare, setting up a Tailscale network, etc. are beyond the scope of this post.
What’s The Point?
Instead of having to access my containers via MagicDNS at addresses like http://nas:port (non-SSL, having to remember port numbers or use a homepage/bookmarks, etc.), I can visit https://containerA.mycustomdomain.com to connect to containerA with a valid SSL certificate and only when I’m on my Tailscale network.
With this set up, I don’t need any port forwarding on my router either!
Create The Stack
Here’s a docker compose stack I created in Portainer to create two containers: a tailscale instance (tailscale-npm) and NPM (npm in a sidecar configuration).
services:
tailscale-npm:
image: tailscale/tailscale
container_name: NPM-TS
restart: unless-stopped
hostname: npm
environment:
- TS_AUTHKEY=*obtain auth key from Tailscale admin*
- TS_STATE_DIR=/var/lib/tailscale
- TS_USERSPACE=false
volumes:
- /volume1/docker/tailscale/state:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
cap_add:
- net_admin
- sys_module
npm:
image: jc21/nginx-proxy-manager:latest
container_name: NPM
restart: unless-stopped
depends_on:
- tailscale-npm
network_mode: service:tailscale-npm
labels:
- com.centurylinklabs.watchtower.enable=false
volumes:
- /volume1/docker/npm/data:/data
- /volume1/docker/npm/letsencrypt:/etc/letsencrypt
- /volume1/docker/npm/config.json:/app/config/production.json
A few things to note:
- You’ll need to generate a key on the Tailscale admin page for the relevant section (bolded) in the docker compose stack.
- I use Watchtower to update most of my containers; however, for the NPM container, you can see the label com.centurylinklabs.watchtower.enable=false to exclude it from auto-updates.
- On my NAS, my Docker containers are located in the /volume1/docker/ path. I made folders for tailscale/state and npm/data, npm/letsencrypt, and a config.json file directly under the npm folder. This config.json file has the following:
{
"database": {
"engine": "knex-native",
"knex": {
"client": "sqlite3",
"connection": {
"filename": "/data/database.sqlite"
}
}
}
}
Create The Cloudflare Redirect
After building the stack, you should have a machine on your Tailnet called “npm.” Find the IP address for this machine (100.x.x.x.). You’ll navigate to your custom domain’s Cloudflare page and then DNS > Records. Click Add Record and create the following:
- Type: A
- Name: *
- IPv4 address: your NPM machine’s IP address from the Tailscale page (typically 100.x.x.x)
- Uncheck Proxy status (DNS only)
- TTL: Auto
You should then see an entry that looks similar to this.

Now we’ve basically said that anything.yourcustomdomain.com gets redirected to your NPM container over your Tailnet. Great! Now we need to set up NPM.
Generate Cloudflare Token
- Login to the Cloudflare API page. Select Create Token > Edit zone DNS (Use Template)
- Set Permissions to Zone / DNS / Edit.
- Set Zone Resources to Include / Specific Zone / the domain you’re using
- Click Continue To Summary > Create Token > copy and paste the token somewhere safe for now
Set Up NPM
- Navigate to your NPM’s Tailnet address at port 81 (e.g., http://100.xxx.xxx.xxx:81)
- Once you’re on the NPM admin page, Click SSL Certificates in the top bar.
- Then click Add SSL > Let’s Encrypt
- Your domain name is the wildcard you used as the A record in Cloudflare (*.yourdomain.tld). Type your email address.
- Toggle on Use a DNS Challenge. Select Cloudflare as the DNS provider. In the Credentials File Content, copy and paste the Cloudflare token you generated earlier to replace the pre-populated token. For example, you’ll have: dns_cloudflare_api_token=your-cloudflare-token
- Agree to the Let’s Encrypt Terms of Service and click Save. It may take a few seconds, but eventually, you should have a valid wildcard SSL certificate as shown below.

Set Up Proxy Hosts
For the purposes of this section, I’ll pretend like I’m reverse proxying a request for a Dozzle container on port 9005.
- On NPM, navigate to Dashboard > Proxy Hosts > Add Proxy Host
- For domain, I’ll use dozzle.mydomain.tld (fill in whatever your domain is).
- Scheme, forward IP, and port are self explanatory. List the local address of your NAS (e.g. 192.168.x.x) and the port number of the container.
- On the SSL tab, select the certificate you created in the previous section and toggle all the options.
- Test out your domain and see if your container loads with HTTPS.


I’m by no means an expert, but if you have questions or need clarification, please drop me a comment below with questions!



Leave a Reply