Simple HTTP only reverse proxy configuration using rootless Podman failed

1. The problem I’m having:

I try to configure/run a reverse proxy on rootless Podman on CoreOS in VM on Proxmox VE 9 on private LAN and failed :frowning:

$ curl --fail --silent --show-error http://kuma.home.lan:8080
curl: (56) Recv failure: Connection reset by peer 
$ curl --fail --silent --show-error http://kuma.home.lan:80 
curl: (7) Failed to connect to kuma.home.lan port 80 after 7 ms: Could not connect to server

The container I want to proxy/redir works (whoami from traefik). This is the test service for uptime-kuma to be used later.

$ curl --fail --silent --show-error http://kuma.home.lan:2001
Hostname: whoami.localhost
IP: 127.0.0.1
IP: ::1
IP: 192.168.1.13
IP: fe80::5835:1cff:fe82:545d
RemoteAddr: 169.254.1.2:35390
GET / HTTP/1.1
Host: kuma.home.lan:2001
User-Agent: curl/8.11.1
Accept: */*

I’m running Proxmox VE 9 with CoreOS42 as VM, running Podman’s rootless containers. Container’s ports are mapped on non-root Ports (8080, 8443 etc.) Hence, nftables’ forwarding is (hopefully correct) configured, to redirect traffic on port 80 to Container’s port 8080, resp. 433→8433. This setup isn’t using Certs and HTTPS, it’s pre-work for later even using Cert/HTTPS etc.

2. Error messages and/or full log output:

$ journalctl --user -u caddy --no-pager | less +G
Oct 17 19:22:02 coreos-test systemd[2966]: Reloading caddy.service - Caddy - The Ultimate Server with Automatic HTTPS...
Oct 17 19:22:02 coreos-test podman[6009]: 2025-10-17 19:22:02.984904499 +0200 CEST m=+0.205476429 container exec d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f (image=docker.io/library/caddy:2.10-alpine, name=caddy, pod_id=6fa5a5fae7f4adc86ca0865d0dd2aabb91c1dc9b31f686416c536975851954c9, org.opencontainers.image.documentation=https://caddyserver.com/docs, org.opencontainers.image.vendor=Light Code Labs, org.opencontainers.image.version=v2.10.2, PODMAN_SYSTEMD_UNIT=caddy.service, org.opencontainers.image.title=Caddy, org.opencontainers.image.description=a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go, org.opencontainers.image.licenses=Apache-2.0, org.opencontainers.image.source=https://github.com/caddyserver/caddy-docker, org.opencontainers.image.url=https://caddyserver.com)
Oct 17 19:22:03 coreos-test caddy[6009]: {"level":"info","ts":1760721723.1752846,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
Oct 17 19:22:03 coreos-test caddy[6009]: {"level":"info","ts":1760721723.179875,"msg":"adapted config to JSON","adapter":"caddyfile"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1817808,"logger":"admin.api","msg":"received request","method":"POST","host":":2019","uri":"/load","remote_ip":"127.0.0.1","remote_port":"34686","headers":{"Accept-Encoding":["gzip"],"Cache-Control":["must-revalidate"],"Content-Length":["934"],"Content-Type":["application/json"],"Origin":["http://:2019"],"User-Agent":["Go-http-client/1.1"]}}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.184274,"logger":"admin","msg":"admin endpoint started","address":":2019","enforce_origin":false,"origins":["//localhost","//192.168.1.13"]}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"warn","ts":1760721723.1843548,"logger":"admin","msg":"admin endpoint on open interface; host checking disabled","address":":2019"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1851923,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv0"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1852627,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv1"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.185309,"logger":"http.auto_https","msg":"adjusted config","tls":{"automation":{"policies":[{}]}},"http":{"http_port":80,"https_port":443,"servers":{"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"192.168.1.13:2001"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{"disable":true}},"srv1":{"listen":[":8086"],"routes":[{"handle":[{"body":"Hello World","handler":"static_response"}]}],"automatic_https":{"disable":true}}}}}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.1861422,"logger":"http","msg":"starting server loop","address":"[::]:443","tls":true,"http3":false}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.186211,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1862552,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.1864965,"logger":"http","msg":"starting server loop","address":"[::]:8086","tls":false,"http3":false}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"warn","ts":1760721723.1865473,"logger":"http","msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":8086"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"warn","ts":1760721723.186567,"logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":8086"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1865819,"logger":"http.log","msg":"server running","name":"srv1","protocols":["h1","h2","h3"]}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.1866632,"logger":"events","msg":"event","name":"started","id":"06e6b6e8-851b-49e9-be3f-4a011dc97242","origin":"","data":null}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.186714,"logger":"events","msg":"event","name":"stopping","id":"ea4ffbae-6797-4c98-8fa1-06adecf0fac8","origin":"","data":null}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1867507,"logger":"http","msg":"servers shutting down with eternal grace period"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1876256,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1877022,"logger":"admin.api","msg":"load complete"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1883607,"logger":"admin","msg":"stopped previous server","address":":2019"}
Oct 17 19:22:03 coreos-test podman[6009]: 2025-10-17 19:22:03.195969197 +0200 CEST m=+0.416541153 container exec_died d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f (image=docker.io/library/caddy:2.10-alpine, name=caddy, org.opencontainers.image.vendor=Light Code Labs, org.opencontainers.image.source=https://github.com/caddyserver/caddy-docker, org.opencontainers.image.description=a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go, org.opencontainers.image.version=v2.10.2, PODMAN_SYSTEMD_UNIT=caddy.service, org.opencontainers.image.title=Caddy, org.opencontainers.image.url=https://caddyserver.com, org.opencontainers.image.documentation=https://caddyserver.com/docs, org.opencontainers.image.licenses=Apache-2.0)
Oct 17 19:22:03 coreos-test systemd[2966]: Reloaded caddy.service - Caddy - The Ultimate Server with Automatic HTTPS.

3. Caddy version:

$ podman exec -ti caddy caddy version
v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=

4. How I installed and ran Caddy:

I run caddy as rootless container using Podman.

$ systemctl --user daemon-reload && systemctl --user start caddy
$ systemctl --user daemon-reload && systemctl --user status caddy
● caddy.service - Caddy - The Ultimate Server with Automatic HTTPS
     Loaded: loaded (/var/home/core/.config/containers/systemd/caddy/caddy.container; generated)
    Drop-In: /usr/lib/systemd/user/service.d
             └─10-timeout-abort.conf
     Active: active (running) since Fri 2025-10-17 16:16:05 CEST; 3h 21min ago
 Invocation: 73645fe3686243de9800dc34c411ffad
       Docs: https://caddyserver.com/docs/
   Main PID: 3151 (conmon)
      Tasks: 11 (limit: 9366)
     Memory: 55.8M (peak: 82.1M)
        CPU: 31.190s
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/caddy.service
             ├─libpod-payload-d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f
             │ └─3153 caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
             └─runtime
               └─3151 /usr/bin/conmon --api-version 1 -c d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f -u d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f -r /usr/bin/crun -b /var/home/core/.local/share/co>

Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"warn","ts":1760721723.186567,"logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":8086"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1865819,"logger":"http.log","msg":"server running","name":"srv1","protocols":["h1","h2","h3"]}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.1866632,"logger":"events","msg":"event","name":"started","id":"06e6b6e8-851b-49e9-be3f-4a011dc97242","origin":"","data":null}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"debug","ts":1760721723.186714,"logger":"events","msg":"event","name":"stopping","id":"ea4ffbae-6797-4c98-8fa1-06adecf0fac8","origin":"","data":null}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1867507,"logger":"http","msg":"servers shutting down with eternal grace period"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1876256,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1877022,"logger":"admin.api","msg":"load complete"}
Oct 17 19:22:03 coreos-test caddy[3151]: {"level":"info","ts":1760721723.1883607,"logger":"admin","msg":"stopped previous server","address":":2019"}
Oct 17 19:22:03 coreos-test podman[6009]: 2025-10-17 19:22:03.195969197 +0200 CEST m=+0.416541153 container exec_died d8358801ccd1d7e3e49f5289ba3fe1e739434c1f3bbc5cd94d0472bab9a59f4f (image=docker.io/library/caddy:2.10-alpine, name=caddy, o>
Oct 17 19:22:03 coreos-test systemd[2966]: Reloaded caddy.service - Caddy - The Ultimate Server with Automatic HTTPS.

a. System environment:

On CoreOS as Container’s host (in Proxmox VM)

$ cat /etc/os-release
NAME="Fedora Linux"
VERSION="42.20250929.3.0 (CoreOS)"
RELEASE_TYPE=stable
ID=fedora
VERSION_ID=42
VERSION_CODENAME=""
PLATFORM_ID="platform:f42"
PRETTY_NAME="Fedora CoreOS 42.20250929.3.0"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:42"
HOME_URL="https://getfedora.org/coreos/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-coreos/"
SUPPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
BUG_REPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=42
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=42
SUPPORT_END=2026-05-13
VARIANT="CoreOS"
VARIANT_ID=coreos
OSTREE_VERSION='42.20250929.3.0'
$ podman -v
podman version 5.6.1
$ systemctl --version
systemd 257 (257.9-2.fc42)
+PAM +AUDIT +SELINUX -APPARMOR +IMA +IPE +SMACK +SECCOMP -GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN -IPTC +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK +BTF +XKBCOMMON +UTMP +SYSVINIT +LIBARCHIVE
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

CoreOS DNS

$ nslookup kuma.home.lan
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
Name:   kuma.home.lan
Address: 192.168.1.13

CoreOS nftables:

$ sudo nft list ruleset
table inet filter {
        chain forward {
                type filter hook forward priority filter; policy accept;
        }
}
table inet caddy_redirect {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                tcp dport 80 redirect to :8080
                tcp dport 443 redirect to :8443
                udp dport 443 redirect to :8443
        }

        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
        }
}

On Proxmox:

~# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

There are no fw rules!

b. Command:

$ systemctl --user daemon-reload && systemctl --user start caddy
$ curl --fail --silent --show-error http://kuma.home.lan:8080
curl: (56) Recv failure: Connection reset by peer
$ curl --fail --silent --show-error http://kuma.home.lan:80
curl: (7) Failed to connect to kuma.home.lan port 80 after 7 ms: Could not connect to server
# whoami container I want to redir/proxy
$ curl --fail --silent --show-error http://kuma.home.lan:2001
Hostname: whoami.localhost
IP: 127.0.0.1
IP: ::1
IP: 192.168.1.13
IP: fe80::5835:1cff:fe82:545d
RemoteAddr: 169.254.1.2:35390
GET / HTTP/1.1
Host: kuma.home.lan:2001
User-Agent: curl/8.11.1
Accept: */*
$ netstat -tulpen|grep -E ':(80|8080|2019|8080|8086|443|8443|2001)'
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:8443            0.0.0.0:*               LISTEN      1000       90455      8283/pasta
tcp        0      0 0.0.0.0:2001            0.0.0.0:*               LISTEN      1000       88981      8289/pasta
tcp        0      0 0.0.0.0:2019            0.0.0.0:*               LISTEN      1000       90452      8283/pasta
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1000       90453      8283/pasta
tcp        0      0 0.0.0.0:8086            0.0.0.0:*               LISTEN      1000       90454      8283/pasta
udp        0      0 0.0.0.0:8443            0.0.0.0:*                           1000       90456      8283/pasta

c. Service/unit/compose file:

~/.config/containers/systemd/caddy/caddy.container:

[Unit]
Description=Caddy - The Ultimate Server with Automatic HTTPS
Documentation=https://caddyserver.com/docs/
After=network-online.target

[Container]
Image=docker.io/library/caddy:2.10-alpine
ContainerName=caddy

Pod=caddy.pod

Volume=%h/.config/containers/systemd/caddy/conf:/etc/caddy:Z
Volume=caddy_data:/data:Z
Volume=caddy_config:/config:Z

ReloadCmd=sh -c "caddy reload --config /etc/caddy/Caddyfile --force"

[Service]
Restart=always

[Install]
WantedBy=default.target

~/.config/containers/systemd/caddy/caddy.pod:

[Unit]
Description=Caddy Pod
After=network-online.target
Wants=network-online.target

[Pod]
PodName=caddy
HostName=caddy.localhost

PublishPort=8080:80
PublishPort=8443:443/tcp
PublishPort=8443:443/udp
PublishPort=2019:2019

PublishPort=8086:8086

~/.config/containers/systemd/caddy/whoami.container:

[Unit]
Description=WhoAmI - HTTP Service that returns information about the incoming request
Documentation=https://hub.docker.com/r/traefik/whoami
After=network-online.target

[Container]
Image=docker.io/traefik/whoami
ContainerName=whoami

Pod=whoami.pod

[Service]
Restart=always

[Install]
WantedBy=default.target

~/.config/containers/systemd/caddy/whoami.pod:

[Unit]
Description=WhoAmI Pod
After=network-online.target
Wants=network-online.target

[Pod]
PodName=whoami
HostName=whoami.localhost

PublishPort=2001:80

~/.config/containers/systemd/caddy/caddy.nft rules, independent of container

table inet filter {
        chain forward {
                type filter hook forward priority filter; policy accept;
        }
}
table inet caddy_redirect {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                tcp dport 80 redirect to :8080
                tcp dport 443 redirect to :8443
                udp dport 443 redirect to :8443
        }

        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
        }
}

d. My complete Caddy config:

{
	debug

	http_port 80
	https_port 443
	auto_https off

	email olaf@home.lan

	admin :2019 {
		# Only allow requests from localhost to prevent remote code execution
		origins localhost 192.168.1.13
		# enforce_origin
	}
}


:8086 {
    # works as expected
	respond "Hello World"
}

kuma.home.lan {
    # this response works as expected, reverse_proxy doesn't
	#respond "Hello World"
	reverse_proxy 192.168.1.13:2001
}

5. Links to relevant resources:

Just a guess

maybe it should be

reverse_proxy 169.254.1.2:2001

or alternatively

reverse_proxy host.containers.internal:2001

(I believe host.containers.internal is normally resolved to 169.254.1.2 when using rootless podman)

A container can’t connect to a service on the host by using the IP address of the host’s main network interface when using pasta.

Instead host.containers.internal should be used. For details, see

example: connect to host’s main network interface using pasta and host.containers.internal

You could also set AddHost=myservice.example.com:host-gateway under the [Container] section in the quadlet file caddy.container

For details, see

example: connect to host’s main network interface using pasta and --add-host=example.com:host-gateway

Thank you! It was helpful since I wasn’t aware about the ‘bridge’ with host.containers.internal.