Caddy on macOS breaks when WireGuard uses full-tunnel on the same host

1. The problem I’m having:

Hey everyone!
In my self-hosted setup I use Mac with Caddy (from homebrew) + Wireguard by wrapping my traffic (full-tunnel via wg-quick).
The problem in back-routing (I guess): when I start my Caddy server I can receive external requests (I see it from Caddy’s logs) but can’t respond properly:

  • Resources aren’t available for external client.
  • Let’s Encrypt can’t reach my server.

If I stop my WG, everything works well.
My hypnotize: outbound prefers the WG tunnel path.
So does anybody have experience how to manage routing/traffic for Caddy directly?

2. Error messages and/or full log output:

{"level":"error","ts":1774651202.645688,"msg":"challenge failed","identifier":"cloud.mydomain.com","challenge_type":"http-01","problem":{"type":"urn:ietf:params:acme:error:connection","title":"","detail":"some_ip: Fetching http://cloud.mydomain.com/.well-known/acme-challenge/DvY4vpi1sAQiM6MxlfNa4AG-nsJPozSCtjLIFP4z6b8: Timeout during connect (likely firewall problem)","instance":"","subproblems":null}}
{"level":"error","ts":1774651431.042287,"msg":"challenge failed","identifier":"cloud.mydomain.com","challenge_type":"tls-alpn-01","problem":{"type":"urn:ietf:params:acme:error:connection","title":"","detail":"some_ip: Timeout during connect (likely firewall problem)","instance":"","subproblems":null}}

3. Caddy version:

v2.11.2 h1:iOlpsSiSKqEW+SIXrcZsZ/NO74SzB/ycqqvAIEfIm64=

4. How I installed and ran Caddy:

Homebrew (brew install caddy)

a. System environment:

  • MacOS 26.4
  • Mac Mini M4
  • Caddy runs directly on the host, not in Docker
  • WireGuard client also runs directly on the same host
  • PF is the standard macOS PF setup
  • No custom PF changes are currently active

b. Command:

/opt/homebrew/bin/caddy run --config /opt/infra/etc/caddy/Caddyfile --adapter caddyfile

c. Service/unit/compose file:

d. My complete Caddy config:

{
	admin localhost:2019

	storage file_system {
		root /opt/infra/data/caddy
	}

	log {
		output file /opt/infra/logs/caddy/caddy.log {
			roll_size 50MB
			roll_keep 10
			roll_keep_for 720h
		}
		level INFO
	}
}

:80 {
	log {
		output file /opt/infra/logs/caddy/access.log {
			roll_size 50MB
			roll_keep 10
			roll_keep_for 720h
		}
		format json
	}

	respond "Hello World!"
}

cloud.mydomain.com {
	log {
		output file /opt/infra/logs/caddy/access.log {
			roll_size 50MB
			roll_keep 10
			roll_keep_for 720h
		}
		format json
	}
	respond "hello world"
}

5. Links to relevant resources: