Skip to content

Support configurable hostPort in helm chart #3321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5d59c04
feat: hostPort configurable
Gasoid Apr 24, 2025
6dcba57
Merge branch 'main' into feat/hostPort
Gasoid Apr 30, 2025
b956397
Merge branch 'main' of github.com:nginx/nginx-gateway-fabric into fea…
Gasoid May 29, 2025
1f7f09a
Merge branch 'main' of github.com:nginx/nginx-gateway-fabric into fea…
Gasoid May 29, 2025
185ced7
feat: hostPort for agent-grpc configurable
Gasoid May 29, 2025
40f2b70
feat: helm schema
Gasoid May 29, 2025
bbf9a67
feat: helm config
Gasoid May 29, 2025
8317ade
hostPort crds
Gasoid Jun 10, 2025
ab12cf4
Merge branch 'main' of github.com:nginx/nginx-gateway-fabric into fea…
Gasoid Jun 10, 2025
bfe712d
hostPort crds
Gasoid Jun 10, 2025
fb1abc5
Merge branch 'main' into feat/hostPort
Gasoid Jun 10, 2025
343d24a
omitempty is obsolete
Gasoid Jun 10, 2025
f9ba83d
hostPort tests
Gasoid Jun 11, 2025
13897e1
Merge branch 'main' into feat/hostPort
Gasoid Jun 11, 2025
eed4093
gateway listener is ok
Gasoid Jun 11, 2025
50b3400
comments typo
Gasoid Jun 11, 2025
bb6fb37
Merge branch 'main' into feat/hostPort
Gasoid Jun 11, 2025
60fd6fd
Merge branch 'main' into feat/hostPort
Gasoid Jun 11, 2025
995eaa5
hostPorts list
Gasoid Jun 12, 2025
8f2e721
hostPorts list
Gasoid Jun 12, 2025
ed1d0c5
Merge branch 'main' into feat/hostPort
Gasoid Jun 12, 2025
3253d56
hostPorts list
Gasoid Jun 12, 2025
4a2868d
Merge branch 'feat/hostPort' of github.com:Gasoid/nginx-gateway-fabri…
Gasoid Jun 12, 2025
2e6a1ce
Merge branch 'main' into feat/hostPort
Gasoid Jun 12, 2025
1dc1850
Merge branch 'main' into feat/hostPort
Gasoid Jun 13, 2025
d45d0e7
Merge branch 'main' into feat/hostPort
Gasoid Jun 13, 2025
aba383d
Update apis/v1alpha2/nginxproxy_types.go
Gasoid Jun 14, 2025
e8522bb
Update apis/v1alpha2/nginxproxy_types.go
Gasoid Jun 14, 2025
cc0b116
Update apis/v1alpha2/nginxproxy_types.go
Gasoid Jun 14, 2025
abefe49
hostPort consistent naming
Gasoid Jun 14, 2025
191cfdd
Merge branch 'main' into feat/hostPort
Gasoid Jun 14, 2025
809f714
hostPort consistent naming
Gasoid Jun 14, 2025
837e6da
description fixed
Gasoid Jun 14, 2025
7bdf85b
Update apis/v1alpha2/nginxproxy_types.go
Gasoid Jun 16, 2025
53fa9e6
Update charts/nginx-gateway-fabric/values.yaml
Gasoid Jun 16, 2025
6b0f5f5
Merge branch 'main' into feat/hostPort
Gasoid Jun 16, 2025
7053cd4
feat: filterEmptyFields
Gasoid Jun 16, 2025
e8545de
feat: proper indentation
Gasoid Jun 16, 2025
74ca56b
feat with is not needed
Gasoid Jun 16, 2025
6043820
feat if is not needed
Gasoid Jun 16, 2025
03b10b2
Merge branch 'main' into feat/hostPort
Gasoid Jun 16, 2025
6a02149
feat if result
Gasoid Jun 16, 2025
4fcc11e
Merge branch 'feat/hostPort' of github.com:Gasoid/nginx-gateway-fabri…
Gasoid Jun 16, 2025
1cc4cb7
feat: deployment supports hostPort
Gasoid Jun 17, 2025
4d52817
Merge branch 'main' into feat/hostPort
Gasoid Jun 17, 2025
b132050
Merge branch 'main' into feat/hostPort
Gasoid Jun 18, 2025
5d9fdc2
Merge branch 'main' into feat/hostPort
Gasoid Jun 18, 2025
4a294ad
Merge branch 'main' into feat/hostPort
Gasoid Jun 18, 2025
33ed8d4
Merge branch 'main' into feat/hostPort
Gasoid Jun 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions apis/v1alpha2/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ type ContainerSpec struct {
// +optional
Lifecycle *corev1.Lifecycle `json:"lifecycle,omitempty"`

// HostPorts are the list of ports to expose on the host.
//
// +optional
HostPorts []HostPort `json:"hostPorts,omitempty"`

// VolumeMounts describe the mounting of Volumes within a container.
//
// +optional
Expand Down Expand Up @@ -608,3 +613,16 @@ type NodePort struct {
// kubebuilder:validation:Maximum=65535
ListenerPort int32 `json:"listenerPort"`
}

// HostPort exposes an nginx container port on the host.
type HostPort struct {
// Port to expose on the host.
// kubebuilder:validation:Minimum=1
// kubebuilder:validation:Maximum=65535
Port int32 `json:"port"`

// ContainerPort is the port on the nginx container to map to the HostPort.
// kubebuilder:validation:Minimum=1
// kubebuilder:validation:Maximum=65535
ContainerPort int32 `json:"containerPort"`
}
5 changes: 3 additions & 2 deletions charts/nginx-gateway-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,10 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `certGenerator.ttlSecondsAfterFinished` | How long to wait after the cert generator job has finished before it is removed by the job controller. | int | `30` |
| `clusterDomain` | The DNS cluster domain of your Kubernetes cluster. | string | `"cluster.local"` |
| `gateways` | A list of Gateway objects. View https://gateway-api.sigs.k8s.io/reference/spec/#gateway for full Gateway reference. | list | `[]` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{"hostPorts":[]},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
| `nginx.config` | The configuration for the data plane that is contained in the NginxProxy resource. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.container` | The container configuration for the NGINX container. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.container` | The container configuration for the NGINX container. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{"hostPorts":[]}` |
| `nginx.container.hostPorts` | A list of HostPorts to expose on the host. This configuration allows containers to bind to a specific port on the host node, enabling external network traffic to reach the container directly through the host's IP address and port. Use this option when you need to expose container ports on the host for direct access, such as for debugging, legacy integrations, or when NodePort/LoadBalancer services are not suitable. Note: Using hostPort may have security and scheduling implications, as it ties pods to specific nodes and ports. | list | `[]` |
| `nginx.debug` | Enable debugging for NGINX. Uses the nginx-debug binary. The NGINX error log level should be set to debug in the NginxProxy resource. | bool | `false` |
| `nginx.image.repository` | The NGINX image to use. | string | `"ghcr.io/nginx/nginx-gateway-fabric/nginx"` |
| `nginx.imagePullSecret` | The name of the secret containing docker registry credentials. Secret must exist in the same namespace as the helm release. The control plane will copy this secret into any namespace where NGINX is deployed. | string | `""` |
Expand Down
4 changes: 3 additions & 1 deletion charts/nginx-gateway-fabric/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,7 @@ Filters out empty fields from a struct.
{{- $result = merge $result (dict $key $value) }}
{{- end }}
{{- end }}
{{- $result | toYaml }}
{{- if $result }}
{{- $result | toYaml }}
{{- end }}
{{- end }}
8 changes: 6 additions & 2 deletions charts/nginx-gateway-fabric/templates/nginxproxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ spec:
{{- end }}
container:
{{- if .Values.nginx.container }}
{{- toYaml .Values.nginx.container | nindent 8 }}
{{- with .Values.nginx.container }}
{{- include "filterEmptyFields" . | nindent 8 }}
{{- end }}
{{- end }}
image:
{{- toYaml .Values.nginx.image | nindent 10 }}
Expand All @@ -35,7 +37,9 @@ spec:
{{- end }}
container:
{{- if .Values.nginx.container }}
{{- toYaml .Values.nginx.container | nindent 8 }}
{{- with .Values.nginx.container }}
{{- include "filterEmptyFields" . | nindent 8 }}
{{- end }}
{{- end }}
image:
{{- toYaml .Values.nginx.image | nindent 10 }}
Expand Down
26 changes: 26 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,32 @@
},
"container": {
"description": "The container configuration for the NGINX container. This is applied globally to all Gateways managed by this\ninstance of NGINX Gateway Fabric.",
"properties": {
"hostPorts": {
"description": "A list of HostPorts to expose on the host.\nThis configuration allows containers to bind to a specific port on the host node,\nenabling external network traffic to reach the container directly through the host's IP address and port.\nUse this option when you need to expose container ports on the host for direct access,\nsuch as for debugging, legacy integrations, or when NodePort/LoadBalancer services are not suitable.\nNote: Using hostPort may have security and scheduling implications, as it ties pods to specific nodes and ports.",
"items": {
"properties": {
"containerPort": {
"maximum": 65535,
"minimum": 1,
"required": [],
"type": "integer"
},
"port": {
"maximum": 65535,
"minimum": 1,
"required": [],
"type": "integer"
}
},
"required": [],
"type": "object"
},
"required": [],
"title": "hostPorts",
"type": "array"
}
},
"required": [],
"title": "container",
"type": "object"
Expand Down
28 changes: 27 additions & 1 deletion charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,33 @@ nginx:

# -- The container configuration for the NGINX container. This is applied globally to all Gateways managed by this
# instance of NGINX Gateway Fabric.
container: {}
container:
# @schema
# type: array
# items:
# type: object
# properties:
# port:
# type: integer
# required: true
# minimum: 1
# maximum: 65535
# containerPort:
# type: integer
# required: true
# minimum: 1
# maximum: 65535
# @schema
# -- A list of HostPorts to expose on the host.
# This configuration allows containers to bind to a specific port on the host node,
# enabling external network traffic to reach the container directly through the host's IP address and port.
# Use this option when you need to expose container ports on the host for direct access,
# such as for debugging, legacy integrations, or when NodePort/LoadBalancer services are not suitable.
# Note: Using hostPort may have security and scheduling implications, as it ties pods to specific nodes and ports.
hostPorts: []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we need to have this field uncommented in order to render its schema, I'd like to avoid having it rendered in the NginxProxy resource if it's empty. If you take a look at charts/nginx-gateway-fabric/templates/nginxproxy.yaml, you can see how we do this for the service: section with a function that's defined in templates/_helpers.tpl. I'm hoping we can use that function in a similar way to only render fields in the container section if they actually have values set. If that works properly, then we can actually uncomment all the fields under this container spec, so then they'll also render in the README.

You should be able to use helm template to verify that everything gets rendered properly in the YAML.

Copy link
Author

@Gasoid Gasoid Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i adjusted template. Since filterEmptyFields didn't work properly with empty containersection i had to add if condition.

i see only 1 issue with newline:
Screenshot 2025-06-16 at 18 15 20

i have tried to fix this obsolete newline, but i have not found solution. i will try to find solution later. Maybe you have idea. If section is not empty, there is no issue

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah fixing that empty newline would be nice. I would think adding another - to the template lines, although that may result in config being pulled up to the wrong line.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i tried various things to fix this issue. If i add - it fixes newline when container is empty, but it fails if container contains something

# - port: 80
# containerPort: 80

# -- The resource requirements of the NGINX container.
# resources: {}

Expand Down
16 changes: 16 additions & 0 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,22 @@ spec:
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
hostPorts:
description: |-
List of ports to expose on the host.
items:
port:
description: |-
Number of port to expose on the host.
type: integer
containerPort:
description: |-
ContainerPort is the port on the nginx container to map to the HostPort.
type: integer
required:
- port
- containerPort
type: array
resources:
description: Resources describes the compute resource
requirements.
Expand Down
16 changes: 16 additions & 0 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,22 @@ spec:
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
hostPorts:
description: |-
List of ports to expose on the host.
items:
port:
description: |-
Number of port to expose on the host.
type: integer
containerPort:
description: |-
ContainerPort is the port on the nginx container to map to the HostPort.
type: integer
required:
- port
- containerPort
type: array
resources:
description: Resources describes the compute resource
requirements.
Expand Down
9 changes: 9 additions & 0 deletions internal/controller/provisioner/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,15 @@ func (p *NginxProvisioner) buildNginxPodTemplateSpec(
container.Command = append(container.Command, "/agent/entrypoint.sh")
container.Args = append(container.Args, "debug")
}

for _, hostPort := range containerSpec.HostPorts {
for i, port := range container.Ports {
if hostPort.ContainerPort == port.ContainerPort {
container.Ports[i].HostPort = hostPort.Port
}
}
}

spec.Spec.Containers[0] = container
}
}
Expand Down
12 changes: 12 additions & 0 deletions internal/controller/provisioner/objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ func TestBuildNginxResourceObjects_NginxProxyConfig(t *testing.T) {
Name: "gw",
Namespace: "default",
},
Spec: gatewayv1.GatewaySpec{
Listeners: []gatewayv1.Listener{
{Name: "port-8443", Port: 8443, Protocol: "tcp"},
},
},
}

resourceName := "gw-nginx"
Expand Down Expand Up @@ -293,6 +298,7 @@ func TestBuildNginxResourceObjects_NginxProxyConfig(t *testing.T) {
corev1.ResourceCPU: resource.Quantity{Format: "100m"},
},
},
HostPorts: []ngfAPIv1alpha2.HostPort{{ContainerPort: int32(8443), Port: int32(8443)}},
},
},
},
Expand Down Expand Up @@ -344,6 +350,12 @@ func TestBuildNginxResourceObjects_NginxProxyConfig(t *testing.T) {
g.Expect(container.ImagePullPolicy).To(Equal(corev1.PullAlways))
g.Expect(container.Resources.Limits).To(HaveKey(corev1.ResourceCPU))
g.Expect(container.Resources.Limits[corev1.ResourceCPU].Format).To(Equal(resource.Format("100m")))

g.Expect(container.Ports).To(ContainElement(corev1.ContainerPort{
ContainerPort: 8443,
Name: "port-8443",
HostPort: 8443,
}))
}

func TestBuildNginxResourceObjects_Plus(t *testing.T) {
Expand Down