Skip to content

Document best practice for a site to listen on Tailscale _and_ other interfaces #88

@bcat

Description

@bcat

First off, thanks for this module! It's really helpful for integrating Tailscale into an existing Caddy reverse proxy setup with minimal changes!

One thing that wasn't obvious to me right away was how to make a site available on both normal physical interfaces and via a Tailscale node. (My use case: I have Caddy serving a number of different sites on my LAN. I want to expose a proper subset of those sites to a friend via Tailscale node sharing, without needing to spin up a separate Caddy instance for this.)

Let's say I start with this Caddyfile....

# Only listens on physical interfaces.
site.example.com {
  ...
}

If I add in Tailscale like so, the site starts listening on the Tailscale node addresses... but it also stops listening on other interfaces. Totally makes sense, as being able to share privately with Tailscale is important. It's just not the use case I specifically have. :)

{
  tailscale {
    shared {
      auth_key ...
      hostname shared
      state_dir /tailscale
    }
  }
}

# Only listens on Tailscale node.
# (Useful for some cases, but not what I want here.)
site.example.com {
  bind tailscale/shared

  ...
}

I natively tried also binding on [::], but that just causes Caddy to die with a panic: connection already exists error at startup (presumably since I have other sites without a bind directive that already set up servers on [::]:80 and [::]:443):

# It would be nice if this worked, but it doesn't!
site.example.com {
  bind [::] tailscale/shared

  ...
}

The best solution I could find is defining the site twice and using a snippet to share the implementation details:

(site) {
  ...
}

# Listens on non-Tailscale interfaces.
site.example.com {
  import site
}

# Listens on Tailscale node.
site.example.com {
  bind tailscale/shared
  import site
}

I'd be happy to send a quick PR adding an example like this to the README, but first I wanted to double check I'm not just being dumb and missing a cleaner way to enable this pattern without the duplicate site + snippet boilerplate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions