Running nginx as reverse proxy in docker container

I'm trying to run nginx in a docker container on my router. I have it working on a host inside the network and would like to move it to the router. I've tried to follow several guides but nothing has worked. Now I feel like my configurations are a mess of different attempts and I don't know what to configure or clean up anymore.

I'm not great at understanding routers, and maybe I shouldn't be trying to do this, but I'd like to learn how and why things are done for setting up the docker network and routing and/or forwarding traffic/ports to it. A lot of guides just say to do things this way but sometimes don't explain why. I'm hoping to learn while also setting this up.

Any help would be greatly appreciated.

Here's some of my (hopefully relevant) info:

OpenWRT 23.05

CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                                                                          NAMES
6f9c24d5b2cd   nginx:latest   "/docker-entrypoint.…"   7 seconds ago   Up 2 seconds   0.0.0.0:9080->9080/tcp, :::9080->9080/tcp, 80/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp   nginx


-------------



# Generated by iptables-save v1.8.8 on Tue Feb 11 11:22:22 2025
*mangle
:PREROUTING ACCEPT [2622725613:4746292475146]
:INPUT ACCEPT [19144102:2089736443]
:FORWARD ACCEPT [2589155080:4739365465498]
:OUTPUT ACCEPT [41640639:4979157215]
:POSTROUTING ACCEPT [2594224736:4741821923115]
COMMIT
# Completed on Tue Feb 11 11:22:22 2025
# Generated by iptables-save v1.8.8 on Tue Feb 11 11:22:22 2025
*nat
:PREROUTING ACCEPT [483:52358]
:INPUT ACCEPT [100:9088]
:OUTPUT ACCEPT [25:1500]
:POSTROUTING ACCEPT [174:13302]
:DOCKER - [0:0]
[11562682:818923772] -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
[0:0] -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
[0:0] -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
[0:0] -A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 9080 -j MASQUERADE
[0:0] -A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 9443 -j MASQUERADE
[0:0] -A DOCKER -i docker0 -j RETURN
[17:1020] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 9080 -j DNAT --to-destination 172.17.0.2:9080
[0:0] -A DOCKER ! -i docker0 -p tcp -m tcp --dport 9443 -j DNAT --to-destination 172.17.0.2:9443
COMMIT
# Completed on Tue Feb 11 11:22:22 2025
# Generated by iptables-save v1.8.8 on Tue Feb 11 11:22:22 2025
*filter
:INPUT ACCEPT [330:27702]
:FORWARD ACCEPT [10311:2341601]
:OUTPUT ACCEPT [493:65952]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
[2586127798:4739176657317] -A FORWARD -j DOCKER-USER
[2586127798:4739176657317] -A FORWARD -j DOCKER-ISOLATION-STAGE-1
[0:0] -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
[0:0] -A FORWARD -o docker0 -j DOCKER
[0:0] -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
[0:0] -A FORWARD -i docker0 -o docker0 -j ACCEPT
[0:0] -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 9080 -j ACCEPT
[0:0] -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 9443 -j ACCEPT
[0:0] -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
[2586127798:4739176657317] -A DOCKER-ISOLATION-STAGE-1 -j RETURN
[0:0] -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
[0:0] -A DOCKER-ISOLATION-STAGE-2 -j RETURN
[0:0] -A DOCKER-USER -i eth0 -o docker0 -j REJECT --reject-with icmp-port-unreachable
[2586127798:4739176657317] -A DOCKER-USER -j RETURN
COMMIT
# Completed on Tue Feb 11 11:22:22 2025


-------------


Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.0.1:7681        0.0.0.0:*               LISTEN      15329/ttyd
tcp        0      0 0.0.0.0:9080            0.0.0.0:*               LISTEN      26787/docker-proxy
tcp        0      0 0.0.0.0:9443            0.0.0.0:*               LISTEN      26803/docker-proxy
tcp        0      0 140.141.255.107:53      0.0.0.0:*               LISTEN      14058/dnsmasq
tcp        0      0 10.100.0.2:53           0.0.0.0:*               LISTEN      14058/dnsmasq
tcp        0      0 192.168.9.1:53          0.0.0.0:*               LISTEN      14058/dnsmasq
tcp        0      0 192.168.0.1:22          0.0.0.0:*               LISTEN      3629/dropbear
tcp        0      0 192.168.0.1:80          0.0.0.0:*               LISTEN      16962/uhttpd
tcp        0      0 192.168.0.1:443         0.0.0.0:*               LISTEN      16962/uhttpd
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      14058/dnsmasq
tcp        0    712 192.168.0.1:22          192.168.0.5:59612       ESTABLISHED 18347/dropbear
tcp        0      0 :::9080                 :::*                    LISTEN      26793/docker-proxy
tcp        0      0 :::9443                 :::*                    LISTEN      26812/docker-proxy
tcp        0      0 fe80::bcf1:80ff:fe27:f94f:53 :::*                    LISTEN      14058/dnsmasq
tcp        0      0 :::12865                :::*                    LISTEN      2987/netserver
tcp        0      0 fe80::ec29:29a7:7f:616a:53 :::*                    LISTEN      14058/dnsmasq
tcp        0      0 fe80::8ea6:82ff:fe70:7ee:53 :::*                    LISTEN      14058/dnsmasq
tcp        0      0 fe80::c050:bdff:fe2c:c195:53 :::*                    LISTEN      14058/dnsmasq
tcp        0      0 fe80::7852:548e:ee7c:3316:53 :::*                    LISTEN      14058/dnsmasq
tcp        0      0 ::1:53                  :::*                    LISTEN      14058/dnsmasq


-------------



config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fd51:b6e7:6f94::/48'

config device
        option name 'br-lan'
        option type 'bridge'
        list ports 'eth1'

config interface 'lan'
        option device 'br-lan'
        option proto 'static'
        option ipaddr '192.168.0.1'
        option netmask '255.255.255.0'
        option ip6assign '60'
        list dns '192.168.0.24'

config interface 'wan'
        option device 'eth0'
        option proto 'dhcp'

config interface 'wan6'
        option device 'eth0'
        option proto 'dhcpv6'

config interface 'OpenVPN'
        option proto 'none'
        option device 'tun0'
        option force_link '1'
        list dns '192.168.0.24'

config interface 'vpn'
        option proto 'none'
        option delegate '0'
        option device 'tun0'

config interface 'docker'
        option device 'docker0'
        option proto 'none'

config device
        option type 'bridge'
        option name 'docker0'
        option bridge_empty '1'


-------------


config defaults
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'
        option drop_invalid '1'

config zone 'lan'
        option name 'lan'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        list network 'lan'
        list device 'tun+'

config zone 'wan'
        option name 'wan'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        list network 'wan'
        list network 'OpenVPN'

config forwarding
        option src 'lan'
        option dest 'wan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'
        option enabled '0'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config redirect
        option dest 'lan'
        option target 'DNAT'
        option name 'HTTP'
        list proto 'tcp'
        option src 'wan'
        option src_dport '80'
        option dest_ip '192.168.0.24'

config redirect
        option dest 'lan'
        option target 'DNAT'
        option name 'HTTPS'
        list proto 'tcp'
        option src 'wan'
        option src_dport '443'
        option dest_ip '192.168.0.24'

config forwarding
        option dest 'OpenVPN'
        option src 'lan'

config rule 'ovpn'
        option name 'Allow-OpenVPN'
        option src 'wan'
        option dest_port '1194'
        option proto 'udp'
        option target 'ACCEPT'

config include 'pbr'
        option fw4_compatible '1'
        option type 'script'
        option path '/usr/share/pbr/firewall.include'

config zone 'vpn'
        option name 'vpn'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        option masq '1'
        option mtu_fix '1'
        list network 'vpn'

config forwarding
        option src 'lan'
        option dest 'vpn'

config forwarding
        option src 'vpn'
        option dest 'lan'

config forwarding
        option src 'vpn'
        option dest 'wan'

config rule
        option name 'Allow-OpenVPN-Server'
        option src 'wan'
        option dest_port '1194'
        option proto 'udp'
        option target 'ACCEPT'

config zone 'docker'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        option name 'docker'
        list network 'docker'

config redirect
        option dest 'docker'
        option target 'DNAT'
        option name 'nginx-test'
        list proto 'tcp'
        option src 'wan'
        option src_dport '9080'
        option dest_port '9080'
        option dest_ip '172.17.0.2'

Nothing will work with iptables-legacy.

The only reason that is there is because docker sets it up I guess. I don't do anything specific with it myself. I saw that someone else asked for that info in another related thread so I just included it here.

I thought I saw somewhere in all the guides and setup instructions that there was a way to tell docker to not use iptables? I don't remember where I might've seen that though.

Personally, I'd love to not use it. I use nftables for other things and am a little more familiar with it with blocking and allowing traffic, but not with routing or NATing and masquerading. I sort of know what those do but never really understood how they work under the covers.

Here's the contents of /etc/config/dockerd. I guess this is where the iptables flag resides.

# The following settings require a restart of docker to take full effect, A reload will only have partial or no effect:
# log_driver
# bip
# blocked_interfaces
# extra_iptables_args
# device

config globals 'globals'
#       option alt_config_file '/etc/docker/daemon.json'
        option data_root '/opt/docker/'
#       option log_driver 'local'
        option log_level 'warn'
        option iptables '1'
#       list hosts 'unix:///var/run/docker.sock'
#       option bip '172.18.0.1/24'
#       option fixed_cidr '172.17.0.0/16'
#       option fixed_cidr_v6 'fc00:1::/80'
#       option ipv6 '1'
#       option ip '::ffff:0.0.0.0'
#       list dns '172.17.0.1'
#       list registry_mirrors 'https://<my-docker-mirror-host>'
#       list registry_mirrors 'https://hub.docker.com'

# If your organization uses a proxy server to connect to the internet, you may need to configure the proxy.
# See https://docs.docker.com/engine/daemon/proxy/ for more details
config proxies 'proxies'
#       option http_proxy 'http://proxy.example.com:3128'
#       option https_proxy 'https://proxy.example.com:3129'
#       option no_proxy '*.test.example.com,.example.org,127.0.0.0/8'

# Docker doesn't work well out of the box with fw4. This is because Docker relies on a compatibility layer that
# naively translates iptables rules. For the best compatibility replace the following dependencies:
# `firewall4` -> `firewall`
# `iptables-nft` -> `iptables-legacy`
# `ip6tables-nft` -> `ip6tables-legacy`

# Docker undermines the fw3 rules. By default all external source IPs are allowed to connect to the Docker host.
# See https://docs.docker.com/network/iptables/ for more details.

# firewall config changes are only additive i.e firewall will need to be restarted first to clear old changes,
# then docker restarted to load in new changes.
config firewall 'firewall'
        option device 'docker0'
        list blocked_interfaces 'wan'
#       option extra_iptables_args '--match conntrack ! --ctstate RELATED,ESTABLISHED' # allow outbound connections

You do have firewall4 installed.
Most stable is to "expose" ports manually https://openwrt.org/docs/guide-user/virtualization/docker_host and disable docker/podman firewall handling.

I've read over that guide multiple times. I'm only looking at the Docker CE portion since I'm not using lxc or any other direct container or cgroup stuff, and I'm definitely not using Podman. But I don't see anything about disabling firewall handling. Is that the section labeled Firewall/Zone under Podman?

Yes, doc is a bit out of order, but set iptables to none in docker or podman and strictly map ports to respective containers

Turning off iptables in the /etc/config/dockerd seems to have cleared a lot of stuff up and things seem to be working. Thanks!

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.