As usual nftables is a moving target. This feature appeared in nftables 0.9.4 released on 2020-04-01:
NAT mappings with concatenations. This allows you to specify the address and port to be used in the NAT mangling from maps, eg.
nft add rule ip nat pre dnat ip addr . port to ip saddr map { 1.1.1.1 : 2.2.2.2 . 30 }
You can also use this new feature with named sets:
nft add map ip nat destinations { type ipv4_addr . inet_service : ipv4_addr . inet_service \; }
nft add rule ip nat pre dnat ip addr . port to ip saddr . tcp dport map @destinations
So in OP's case that would be:
nft add ip dnatTable dnatChain 'dnat ip addr . port to tcp dport map { 80 : 172.17.0.3 . 8080 }'
Just for information, nftables 1.0.0 released on 2021-08-19 has a simplified syntax:
- Simplify syntax for NAT mappings. You can specify an IP range:
[...]
Or a specific IP and port.
... dnat to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
So OP's case can then be shortened to (and previous rule will be displayed as):
nft add ip dnatTable dnatChain 'dnat to tcp dport map { 80 : 172.17.0.3 . 8080 }'
Using a named map can help to separate rules from data. This would work too instead (using 0.9.4 syntax):
nft add map ip dnatTable portToIpPort '{ type inet_service : ipv4_addr . inet_service ; }'
nft add rule ip dnatTable dnatChain dnat ip addr . port to tcp dport map @portToIpPort
and then at any convenient time:
nft add element ip dnatTable portToIpPort '{ 80 : 172.17.0.3 . 8080 }'