Setting up Caddy, tailscale for reverse proxy on Synology NAS

1. Caddy version:

caddy:latest (v2.6.7)

2. How I installed, and run Caddy:

Using Docker composer.yaml, build and run through GUI.

a. System environment:

Synology DSM 7.2.1-69057 Update 3
Docker version(Container Manager) - 20.10.2.3

a. Docker composer.yaml:

version: "3.7"
networks:
    proxy-network:
    name: proxy-network
services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    container_name: caddy
    networks:
      - proxy-network
    hostname: caddy
    depends_on:
      - tailscale 
    ports:
      - "8080:80"
      - "8443:443"
      - "8443:443/udp"
    volumes:
      - /volume1/docker/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /volume1/docker/caddy/data:/data
      - /volume1/docker/caddy/config:/config
      - /volume1/docker/tailscale/tmp/tailscaled.sock:/var/run/tailscale/tailscaled.sock

  tailscale:
    container_name: tailscaled
    image: tailscale/tailscale
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - /volume1/docker/tailscale/varlib:/var/lib
      - /volume1/docker/tailscale/tmp:/tmp
    environment:
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_AUTH_KEY=MY_TAILSCALE_AUTH_KEY

d. My complete Caddy config:

mytailscaledomain.ts.net {
 tls {
    get_certificate tailscale
 }

 handle_path /teslamate {
    reverse_proxy teslamate-app:1000 <<"teslamate-app" is the project name. The underlying container names are "teslamate-grafana-1". Tried both, neither worked>>
 }
 handle_path /immich {
    reverse_proxy immich-app:1001
 }
}

3. The problem I’m having:

Followed the above steps and tried reverse proxy setup on my Synology NAS.

After setting up everything and hit the URL: https://mytailscaledomain.ts.net/teslamate.
It’s defaulting to https://mytailscaledomain.ts.net:<DSM_HOME_HTTPS_PORT>/teslamate, and throwing the error - “Sorry, the page you are looking for is not found.”

Am i missing any other configurations on my NAS or Tailscale settings?
Please help. I’m pulling my hairs on this. Below are my docker and caddy file details.

5. What I already tried:

Tried following caddy documentation and couple of other online similar posts, I don’t have much expereince with caddy to try beyond this.

6. Links to relevant resources:

https://caddy.community/t/taiscale-synology-https-to-docker-services/18834
https://www.reddit.com/r/Tailscale/comments/104y6nq/docker_tailscale_and_caddy_with_https_a_love_story/

This is a Synology thing. It redirects HTTPS requests on 443 to 5001. There’s configuration to change it under Control Panel.

You’ll need to set up the port forwarding and/or tell Caddy what the custom https_port is, so Caddy knows it’s behind a NAT.

Thanks for your response.
Assuming my custom HTTPS port is 7001, could you please guide or post me to any existing posts on what configs are to be done in my caddy and NAS control panel setups?

Thank you!

It depends a lot on your infrastructure, so I can’t give an informed recommendation. Take this with a handful of salt.

Tell Caddy your HTTPS port is 8443 by adding the following at the top of your Caddyfile:

{
    https_port 8443
}

Change your docker-compose file accordingly.

Change the port-forwarding rule on your router to forward port 443 to 8443.

This should be it on a high level.

I believe I’m almost there now, but not expected output yet.
I freed up ports 80 and 443 for caddy. NAS runs on 192.168.0.1 static IP

Caddy docker composer changes

    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"

Caddyfile changes

(network_paths) {
	handle_path /docker {
		reverse_proxy 192.168.0.1:9000
	}
}

mytailscale.ts.net {
	import network_paths
}

https://192.168.0.1 {
	import network_paths
}

Hitting https://mytailscale.ts.net/docker is returning a portainer image instead of the whole web page.
I tried different combinations of caddy configs but still unable to render the login page.

This is the caddy logs with status 200:

{"level":"debug","ts":1712894789.088855,"logger":"http.handlers.rewrite","msg":"rewrote request","request":{"remote_ip":"172.17.0.1","remote_port":"35416","client_ip":"172.17.0.1","proto":"HTTP/2.0","method":"GET","host":"mysynology.ts.net","uri":"/docker","headers":{"Te":["trailers"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:124.0) Gecko/20100101 Firefox/124.0"],"Accept-Language":["en-US,en;q=0.5"],"Sec-Fetch-Dest":["document"],"Sec-Gpc":["1"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"],"Accept-Encoding":["gzip, deflate, br"],"Dnt":["1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"mysynology.ts.net"}},"method":"GET","uri":"/"}
{"level":"debug","ts":1712894789.0889485,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"192.168.0.33:9000","total_upstreams":1}
{"level":"debug","ts":1712894789.0910451,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"192.168.0.33:9000","duration":0.002032603,"request":{"remote_ip":"172.17.0.1","remote_port":"35416","client_ip":"172.17.0.1","proto":"HTTP/2.0","method":"GET","host":"mysynology.ts.net","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:124.0) Gecko/20100101 Firefox/124.0"],"X-Forwarded-Host":["mysynology.ts.net"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"],"Upgrade-Insecure-Requests":["1"],"Accept-Encoding":["gzip, deflate, br"],"Dnt":["1"],"Sec-Fetch-Dest":["document"],"X-Forwarded-For":["172.17.0.1"],"X-Forwarded-Proto":["https"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"],"Sec-Gpc":["1"],"Accept-Language":["en-US,en;q=0.5"],"Te":["trailers"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"mysynology.ts.net"}},"headers":{"Accept-Ranges":["bytes"],"Content-Encoding":["gzip"],"Content-Type":["text/html; charset=utf-8"],"Last-Modified":["Thu, 07 Dec 2023 08:10:18 GMT"],"X-Xss-Protection":["1; mode=block"],"Cache-Control":["no-cache, no-store, must-revalidate"],"Vary":["Accept-Encoding"],"X-Content-Type-Options":["nosniff"],"Date":["Fri, 12 Apr 2024 04:06:29 GMT"]},"status":200}

Another interesting observation, if i change the port to 9443, the request is throwing “Client sent an HTTP request to an HTTPS server.” error.

reverse_proxy http://192.168.0.33:9443

Figured it out. I had to add an asterisk in my path match. Updated Caddy below.

(network_paths) {
	handle_path /portainer* {
		reverse_proxy 192.168.0.33:9000
	}
}

Thanks to @ francislavoie from this thread - Reverse proxy returning blank page in docker