Caddy-L4 Caddyfile help

1. The problem I’m having:

I want to use caddy-l4 for services like nextcloud talk, stalwart-mail, etc. I’ve successfully built the docker image with L4 and Porkbun(my dns) but what I can’t seem to figure out is the caddyfile. Right now I’m just using it normally for all my services but I want to use L4 for some of them as well. I’ve looked everywhere for ages but I just don’t get what to put in it for L4. Please help.

2. Error messages and/or full log output:

No errors anywhere caddy works fine. Just can’t wrap my head around L4’s caddyfile entries.

caddy list-modules output:

admin.api.load
admin.api.metrics
admin.api.pki
admin.api.reverse_proxy
caddy.adapters.caddyfile
caddy.config_loaders.http
caddy.filesystems
caddy.listeners.http_redirect
caddy.listeners.proxy_protocol
caddy.listeners.tls
caddy.logging.cores.mock
caddy.logging.encoders.append
caddy.logging.encoders.console
caddy.logging.encoders.filter
caddy.logging.encoders.filter.cookie
caddy.logging.encoders.filter.delete
caddy.logging.encoders.filter.hash
caddy.logging.encoders.filter.ip_mask
caddy.logging.encoders.filter.query
caddy.logging.encoders.filter.regexp
caddy.logging.encoders.filter.rename
caddy.logging.encoders.filter.replace
caddy.logging.encoders.json
caddy.logging.writers.discard
caddy.logging.writers.file
caddy.logging.writers.net
caddy.logging.writers.stderr
caddy.logging.writers.stdout
caddy.network_proxy.none
caddy.network_proxy.url
caddy.storage.file_system
events
http
http.authentication.hashes.bcrypt
http.authentication.providers.http_basic
http.encoders.gzip
http.encoders.zstd
http.handlers.acme_server
http.handlers.authentication
http.handlers.copy_response
http.handlers.copy_response_headers
http.handlers.encode
http.handlers.error
http.handlers.file_server
http.handlers.headers
http.handlers.intercept
http.handlers.invoke
http.handlers.log_append
http.handlers.map
http.handlers.metrics
http.handlers.push
http.handlers.request_body
http.handlers.reverse_proxy
http.handlers.rewrite
http.handlers.static_response
http.handlers.subroute
http.handlers.templates
http.handlers.tracing
http.handlers.vars
http.ip_sources.static
http.matchers.client_ip
http.matchers.expression
http.matchers.file
http.matchers.header
http.matchers.header_regexp
http.matchers.host
http.matchers.method
http.matchers.not
http.matchers.path
http.matchers.path_regexp
http.matchers.protocol
http.matchers.query
http.matchers.remote_ip
http.matchers.tls
http.matchers.vars
http.matchers.vars_regexp
http.precompressed.br
http.precompressed.gzip
http.precompressed.zstd
http.reverse_proxy.selection_policies.client_ip_hash
http.reverse_proxy.selection_policies.cookie
http.reverse_proxy.selection_policies.first
http.reverse_proxy.selection_policies.header
http.reverse_proxy.selection_policies.ip_hash
http.reverse_proxy.selection_policies.least_conn
http.reverse_proxy.selection_policies.query
http.reverse_proxy.selection_policies.random
http.reverse_proxy.selection_policies.random_choose
http.reverse_proxy.selection_policies.round_robin
http.reverse_proxy.selection_policies.uri_hash
http.reverse_proxy.selection_policies.weighted_round_robin
http.reverse_proxy.transport.fastcgi
http.reverse_proxy.transport.http
http.reverse_proxy.upstreams.a
http.reverse_proxy.upstreams.multi
http.reverse_proxy.upstreams.srv
pki
tls
tls.ca_pool.source.file
tls.ca_pool.source.http
tls.ca_pool.source.inline
tls.ca_pool.source.pki_intermediate
tls.ca_pool.source.pki_root
tls.ca_pool.source.storage
tls.certificates.automate
tls.certificates.load_files
tls.certificates.load_folders
tls.certificates.load_pem
tls.certificates.load_storage
tls.client_auth.verifier.leaf
tls.ech.publishers.dns
tls.get_certificate.http
tls.get_certificate.tailscale
tls.handshake_match.local_ip
tls.handshake_match.remote_ip
tls.handshake_match.sni
tls.handshake_match.sni_regexp
tls.issuance.acme
tls.issuance.internal
tls.issuance.zerossl
tls.leaf_cert_loader.file
tls.leaf_cert_loader.folder
tls.leaf_cert_loader.pem
tls.leaf_cert_loader.storage
tls.permission.http
tls.stek.distributed
tls.stek.standard

Standard modules: 127

caddy.listeners.layer4
dns.providers.porkbun
layer4
layer4.handlers.echo
layer4.handlers.proxy
layer4.handlers.proxy_protocol
layer4.handlers.socks5
layer4.handlers.subroute
layer4.handlers.tee
layer4.handlers.throttle
layer4.handlers.tls
layer4.matchers.clock
layer4.matchers.dns
layer4.matchers.http
layer4.matchers.local_ip
layer4.matchers.not
layer4.matchers.openvpn
layer4.matchers.postgres
layer4.matchers.proxy_protocol
layer4.matchers.quic
layer4.matchers.rdp
layer4.matchers.regexp
layer4.matchers.remote_ip
layer4.matchers.remote_ip_list
layer4.matchers.socks4
layer4.matchers.socks5
layer4.matchers.ssh
layer4.matchers.tls
layer4.matchers.winbox
layer4.matchers.wireguard
layer4.matchers.xmpp
layer4.proxy.selection_policies.first
layer4.proxy.selection_policies.ip_hash
layer4.proxy.selection_policies.least_conn
layer4.proxy.selection_policies.random
layer4.proxy.selection_policies.random_choose
layer4.proxy.selection_policies.round_robin
tls.handshake_match.alpn

Non-standard modules: 38

Unknown modules: 0

3. Caddy version:

2.10.2

4. How I installed and ran Caddy:

Built the docker image using this dockerfile:

FROM caddy:builder AS builder
RUN xcaddy build
–with GitHub - caddy-dns/porkbun
–with GitHub - mholt/caddy-l4: Layer 4 (TCP/UDP) app for Caddy
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Ran using docker compose.

a. System environment:

Debian 13 with docker.

b. Command:

Deployed as a Portainer Stack using the compose file below.

c. Service/unit/compose file:

services:
    caddy:
        image: caddy-l4-porkbun:latest
        container_name: Caddy
        environment:
            - TZ=Asia/Kolkata
        volumes:
            - /var/lib/docker/volumes/caddy/Caddyfile:/etc/caddy/Caddyfile
            - /var/lib/docker/volumes/caddy/caddy_config:/config
            - /var/lib/docker/volumes/caddy/caddy_data:/data
        restart: always
        network_mode: host

d. My complete Caddy config:

# Porkbun API Key
{
        email mail@mail.com

        acme_dns porkbun {
                api_key pk1_xxx
                api_secret_key sk1_xxx
        }
}

# Metube
https://mt.example.com:443 {
        reverse_proxy 127.0.0.1:8081
}

# Navidrome
https://nvd.example.com:443 {
        reverse_proxy 127.0.0.1:4533
}

# Nextcloud AIO
https://nc.example.com:443 {
        reverse_proxy 127.0.0.1:11000
}

5. Links to relevant resources:

You’re not specifying which protocols you want to proxy, so I’m going to assume IMAPS, since you mentioned stalwart-mail.

You might find this example from the docs useful as a starting point:

Here’s an example:

# Porkbun API Key
{
        email mail@mail.com

        acme_dns porkbun {
                api_key pk1_xxx
                api_secret_key sk1_xxx
        }

        layer4 {
                :993 {
                        @imaps tls sni imap.example.com
                        route @imaps {
                                tls
                                proxy {
                                        upstream 127.0.0.1:143
                                }
                        }
                }
        }
}

# Metube
https://mt.example.com:443 {
        reverse_proxy 127.0.0.1:8081
}

# Navidrome
https://nvd.example.com:443 {
        reverse_proxy 127.0.0.1:4533
}

# Nextcloud AIO
https://nc.example.com:443 {
        reverse_proxy 127.0.0.1:11000
}

Now my logs just say:

Error: loading initial config: loading new config: layer4 app module: start: listen tcp :993: bind: address already in use

My stalwart mail compose uses the following ports:

        - 444:443

        - 8084:8080

        - 25:25

        - 587:587

        - 465:465

        - 143:143

        - 993:993

        - 4190:4190

        - 110:110

        - 995:995

For stalwart mail I have a caddy entry like this:

https://sw.example.com:443 {
reverse_proxy 127.0.0.1:8084
}

What exactly do I need to put in the caddyfile to have stalwart mail work with nextcloud? Sorry for imposing.

That means port 993 is already taken on your server. You need to reshuffle ports in your mail server compose file to make port 993 available for Caddy. I mean if that’s what you want.

No idea what that means.

Oh sorry I meant that I want to setup the stalwart mail serivce in my nextcloud mail app so I was unsure of how to structure the caddyfile for the correct ports and addresses.