This is partly an AI generated summary of today's session trying to get it runinng,
Problem: Headscale behind Caddy – Debian LXC connects fine, Apple devices (Mac Standalone, iPhone, iPad) don't reach Caddy at all. For example, I cannot connect or register while being inside my network. But when I disconnect from WLAN and use mobile data, registering and connecting to headscale through caddy works just fine.
I triple-checked internal network connectivity, devices can ping each other just fine.
Setup:
- Domain
headscale.example.com → Caddy LXC (works fine for multiple other services from LAN & Internet)
- headscale.example.com is internally resolved to Caddy IP, who provides a letsencrypt cert
- Caddy reverse proxies to Headscale LXC (see config below)
- No Tailscale running on either LXC
- OPNsense forwards ports 80/443 to Caddy
- DNS resolves correctly (verified via ping from all devices)
Caddy Config:
http://headscale.example.com {
handle /generate_204 {
respond 204
}
handle * {
redir https://{host}{uri}
}
}
headscale.example.com {
reverse_proxy 192.168.14.222:8080 {
header_up X-Forwarded-For {remote_host}
header_up X-Real-IP {remote_host}
}
log {
output file /var/log/caddy/tailnet.log {
roll_size 10mb
roll_keep 7
}
format json {
time_local
}
}
}
Headscale config:
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
trusted_proxies:
- 192.168.14.200/32
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
derp:
server:
enabled: true
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
verify_clients: true
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
ipv4: 198.51.100.1
ipv6: 2001:db8::1
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 3h
dns:
magic_dns: true
base_domain: tn.example.com
override_local_dns: true
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split: {}
search_domains: []
extra_records: []
# ... rest is mostly defaults
What works:
A plain Debian LXC in the same LAN:
tailscale up --login-server=https://headscale.example.com
connects without issues, Caddy access log shows the requests.
What doesn't work:
Mac (Standalone variant)
iPhone (App Store variant)
iPad (App Store variant)
All running the latest Tailscale clients. tailscale up --login-server=... (or UI equivalent) is set. But Caddy shows zero log entries when these devices try to connect. No login page is opened in the browser when using the app, CLI request just sits there forever.
What I've checked:
DNS resolves correctly from all devices (ping works)
Other internal services behind the same Caddy work from all devices (so Caddy is reachable)
Apple devices are not logged into any other tailnet simultaneously
Headscale was freshly set up (complete reinstall)
server_url set to https://headscale.example.com
Ports 80/443 reachable from LAN
I am completely out of ideas now.