Kubernetes Gateway API Configuration for Artifactory
Route external traffic into Artifactory using Gateway API resources as an alternative to Ingress, with TLS termination, path routing, and controller-specific extension filters.
The JFrog Artifactory and Artifactory HA Helm charts support the Kubernetes Gateway API as an alternative to Ingress. Set gatewayApi.enabled: true and the chart creates a Gateway and an HTTPRoute, plus a controller-specific extension filter if you configure one. The result is comparable to the Ingress support.
Use the Gateway API in any of these cases:
- Your cluster already runs a Gateway controller.
- You want route configuration kept separate from load balancer ownership.
- You need routing that Ingress annotations cannot express portably.
Chart Version Requirements
| Chart | Minimum version | Released |
|---|---|---|
artifactory | 107.161.15 | July 27, 2026 |
artifactory-ha | 107.161.15 | July 27, 2026 |
jfrog-platform | 11.6.0 | Bundles Artifactory 107.161.15 |
How Traffic Flows
External requests reach the Gateway, which terminates TLS and hands off to the HTTPRoute. The HTTPRoute matches on path and forwards to the Artifactory service.
flowchart LR
C["Client"] --> LB["Load Balancer<br/>(provisioned by controller)"]
LB --> GW["Gateway<br/>TLS termination"]
GW --> HR["HTTPRoute<br/>path matching"]
HR -->|routerPath| R["Artifactory Service<br/>port 8082 (Router)"]
HR -->|artifactoryPath| A["Artifactory Service<br/>port 8081 direct"]
| Resource | Responsibility |
|---|---|
Gateway | Entry point. Declares listeners and terminates TLS. |
HTTPRoute | Path-based routing to Artifactory service ports. |
| Extension filter | Controller-specific proxy behavior such as rewrites, timeouts, and body size limits. |
The Gateway controller, not the chart, provisions the external load balancer and reconciles these resources into proxy configuration.
Prerequisites
-
Install the Gateway API CRDs. They are not built into Kubernetes.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yamlJFrog validated
v1.4.1. The chart checks whether your cluster servesgateway.networking.k8s.io/v1orv1beta1, then emits resources with the matching version. Later standard-channel releases work too. The experimental channel is not required. -
Run a Gateway controller. Without one the
Gatewayis created but never programmed, and no traffic reaches Artifactory.Controller Support status NGINX Gateway Fabric Validated by JFrog. Extension filter examples target it. Envoy Gateway Not validated. Expected to handle core routing, since the chart emits standard fields. Istio Gateway controller Not validated. Expected to handle core routing. -
Create a matching GatewayClass. Its name must equal your
gatewayApi.gatewayClassName, otherwise the install fails withgatewayApi.gatewayClassName is required when gatewayApi.enabled is true.kubectl get gatewayclass -
Set
nginx.enabled: false. The bundled Nginx pod defaults to enabled and is exposed as aLoadBalancerservice. Leaving it on alongside a Gateway gives you two entry points and two external load balancers. Nothing routes to the second one, and your cloud provider still bills you for it.
Missing CRDs Fail the Install
With
gatewayApi.enabled: trueand no CRDs, the chart aborts before creating anything:gatewayApi.enabled is true but Gateway API CRDs (gateway.networking.k8s.io) are not installed in the cluster.This is fail-fast rather than a silent skip, so a successful install confirms the CRDs were detected.
Configure and Deploy
This exposes Artifactory over HTTP and HTTPS with default path routing. Save it as values.yaml.
nginx:
enabled: false
gatewayApi:
enabled: true
gatewayClassName: "nginx"
hosts:
- artifactory.example.com
routerPath: /
artifactoryPath: /artifactory/
pathType: PathPrefix
disableRouterBypass: false
httpRoute:
enabled: true
tls:
- secretName: artifactory-tls
hosts:
- artifactory.example.comhelm upgrade --install artifactory jfrog/artifactory \
--namespace artifactory --create-namespace \
--set global.masterKey=${MASTER_KEY} \
--set global.joinKey=${JOIN_KEY} \
--values values.yamlFor the jfrog-platform chart, nest the same keys under artifactory.
artifactory:
nginx:
enabled: false
gatewayApi:
enabled: true
gatewayClassName: "nginx"
hosts:
- artifactory.example.com
tls:
- secretName: artifactory-tls
hosts:
- artifactory.example.comLater Changes Need databaseUpgradeReady
Changing any
gatewayApivalue on a deployed release means runninghelm upgrade. With the chart's bundled PostgreSQL enabled, the chart blocks every upgrade unless you also pass--set databaseUpgradeReady=true.The failure message talks about database upgrades, not Gateway settings, which makes the cause easy to miss. It applies even to routing changes that touch no database.
Default Routing
With hosts set and no other path configuration, the chart creates two rules.
| Path | Destination | Port | Source of port |
|---|---|---|---|
/ | Artifactory service, JFrog Router | 8082 | artifactory.externalPort |
/artifactory/ | Artifactory service, direct | 8081 | artifactory.externalArtifactoryPort |
Ports come from your Artifactory service values rather than being hardcoded, so overriding artifactory.externalPort changes the backend port to match.
The / rule targets the JFrog Router, which fronts every platform microservice including the UI, Xray, and Distribution. The /artifactory/ rule bypasses the Router for direct artifact traffic. Set disableRouterBypass: true to remove the second rule so everything flows through the Router.
TLS Configuration
An HTTP listener on httpExternalPort is always created. HTTPS listeners appear only when you populate tls.
The chart creates one HTTPS listener per hostname, so the controller can match certificates by SNI. Listeners are named https, https-1, https-2, and so on.
To resolve the hostnames for a tls entry, the chart uses the first of these that is not empty:
- The
hostslist on thattlsentry. - The top-level
gatewayApi.hostslist. - A wildcard listener with no hostname, matching any host.
Every entry requires secretName, and the secret must already exist in the release namespace. TLS mode is always Terminate, so traffic from the Gateway to Artifactory is plain HTTP inside the cluster.
This creates an http listener plus https and https-1, both referencing artifactory-tls.
gatewayApi:
hosts:
- artifactory.example.com
- docker.example.com
tls:
- secretName: artifactory-tlsDocker Registry Support
Docker clients need URL rewrites the Gateway API cannot express natively. Supply them through an extension filter. Add this block to your gatewayApi configuration, targeting NGINX Gateway Fabric's SnippetsFilter.
gatewayApi:
extensionFilter:
enabled: true
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
spec:
snippets:
- context: http.server
value: |
client_max_body_size 0;
proxy_read_timeout 600;
proxy_send_timeout 600;
- context: http.server.location
value: |
rewrite ^/(v2)/token /artifactory/api/docker/null/v2/token;
rewrite ^/(v2)/([^\/]*)(/.*) /artifactory/api/docker/$2/$1$3;The chart creates the SnippetsFilter and adds an ExtensionRef filter to the HTTPRoute referencing it. It also derives the group from the portion of apiVersion before the slash, so you never set it yourself.
http.server applies server-wide settings such as body size and timeouts. http.server.location applies rewrites per matched route. Setting client_max_body_size to 0 removes the upload limit, which artifact uploads need because they have no predictable upper bound.
Clients address repositories by path, as in artifactory.example.com/docker-local/myimage:tag. The rewrite reads the repository from the first path segment after /v2/.
Artifactory uses that same form in the Location headers it returns during a blob upload. Re-applying the rewrite to those redirected requests therefore produces the same path. Pushes work with Artifactory's default Docker access method, and no reverse proxy setting needs changing.
NGINX Gateway Fabric Disables Snippets by Default
The controller ignores
SnippetsFilterresources unless you installed it with--set nginxGateway.snippetsFilters.enable=true.Without that flag, the chart still creates the filter and the HTTPRoute still references it. The controller simply generates no NGINX configuration from it. Docker operations then fail with a 404 while the UI keeps working, which makes the cause easy to miss.
To reference a filter you create separately, leave extensionFilter.enabled as false and point httpRoute.filters at it. Supply group explicitly here, because the chart derives it only for filters it creates.
gatewayApi:
httpRoute:
filters:
- type: ExtensionRef
extensionRef:
group: gateway.nginx.org
kind: SnippetsFilter
name: artifactory-docker-rewritesAdditional Routes
Use additionalDirectRoutes to append complete HTTPRoute rules. Both it and additionalRules render through Helm's tpl, so template expressions resolve against the release. Each entry needs both matches and backendRefs.
gatewayApi:
additionalDirectRoutes: |
- matches:
- path:
type: PathPrefix
value: /xray/
backendRefs:
- name: {{ include "artifactory.fullname" . }}
port: 8082Use artifactory.fullname with the artifactory chart and artifactory-ha.fullname with the artifactory-ha chart.
Parameter Reference
All parameters nest under gatewayApi in values.yaml, or under artifactory.gatewayApi in the jfrog-platform chart. The same values apply to both the artifactory and artifactory-ha charts.
| Parameter | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Create Gateway API resources. Requires the CRDs. |
gatewayClassName | string | "" | Name of an existing GatewayClass, such as nginx. Required when enabled is true. |
name | string | "" | Override the Gateway name. Defaults to the chart fullname plus -gateway. |
hosts | list | [] | Hostnames for the HTTPRoute. Same semantics as ingress.hosts. Empty matches any hostname. |
httpExternalPort | int | 80 | Port for the HTTP listener. |
httpsExternalPort | int | 443 | Port for each HTTPS listener. |
routerPath | string | / | Path routed to the JFrog Router on artifactory.externalPort. |
artifactoryPath | string | /artifactory/ | Path routed directly to Artifactory on artifactory.externalArtifactoryPort. |
pathType | string | PathPrefix | PathPrefix, Exact, or RegularExpression. Gateway API rejects ImplementationSpecific. |
disableRouterBypass | bool | false | true removes the artifactoryPath rule so all traffic passes through the Router. |
additionalDirectRoutes | string or list | "" | Extra HTTPRoute rules appended to the route. Rendered through tpl. |
additionalRules | string | "" | Extra HTTPRoute rules as a YAML string. Rendered through tpl. |
tls | list | [] | TLS entries. Each requires secretName and creates one HTTPS listener per host. |
labels | map | {} | Additional labels on the Gateway. |
annotations | map | {} | Additional annotations on the Gateway. |
httpRoute.enabled | bool | true | Create the HTTPRoute. false creates only the Gateway, and also suppresses the extension filter. |
httpRoute.name | string | "" | Override the HTTPRoute name. Defaults to the chart fullname plus -httproute. |
httpRoute.labels | map | {} | Additional labels on the HTTPRoute. |
httpRoute.annotations | map | {} | Additional annotations on the HTTPRoute. |
httpRoute.filters | list | [] | Filters applied to the router path rule, such as an ExtensionRef to an externally managed filter. |
extensionFilter.enabled | bool | false | Create a controller-specific filter and wire it into the HTTPRoute automatically. |
extensionFilter.name | string | "" | Override the filter name. Defaults to the chart fullname plus -extension-filter. |
extensionFilter.apiVersion | string | "" | API version of the filter CRD, such as gateway.nginx.org/v1alpha1. Required when enabled. |
extensionFilter.kind | string | "" | Kind of the filter CRD, such as SnippetsFilter. Required when enabled. |
extensionFilter.spec | map | {} | Body of the filter resource. Controller-specific and passed through unchanged. |
For current defaults on the version you deploy, run helm show values jfrog/artifactory | grep -A 60 "^gatewayApi:".
Generated Resource Names
Names derive from the chart fullname, which is not always <release>-artifactory. Helm omits the chart name when your release name already contains it.
| Release name | Gateway | HTTPRoute | Extension filter |
|---|---|---|---|
artifactory | artifactory-gateway | artifactory-httproute | artifactory-extension-filter |
myrelease | myrelease-artifactory-gateway | myrelease-artifactory-httproute | myrelease-artifactory-extension-filter |
The artifactory-ha chart follows the same rule with artifactory-ha in place of artifactory. Run kubectl get gateway,httproute to see actual names rather than assuming them.
The -gateway suffix matters: NGINX Gateway Fabric provisions its own data-plane Service named <gateway-name>-nginx. Without the suffix that would collide with the Service the chart creates for its bundled Nginx pod. If you override gatewayApi.name, avoid values whose -nginx form clashes with an existing Service.
Migrate from Ingress
Most values map directly.
| Ingress value | Gateway API value |
|---|---|
ingress.enabled | gatewayApi.enabled |
ingress.className | gatewayApi.gatewayClassName |
ingress.hosts | gatewayApi.hosts |
ingress.routerPath | gatewayApi.routerPath |
ingress.artifactoryPath | gatewayApi.artifactoryPath |
ingress.pathType | gatewayApi.pathType |
ingress.disableRouterBypass | gatewayApi.disableRouterBypass |
ingress.tls | gatewayApi.tls |
ingress.additionalRules | gatewayApi.additionalRules |
ingress.additionalDirectRoutes | gatewayApi.additionalDirectRoutes |
Four differences need attention.
pathTypeis not interchangeable. Ingress defaults toImplementationSpecific, which the Gateway API rejects. UsePathPrefix,Exact, orRegularExpression. If you relied onImplementationSpecificregular expression behavior,RegularExpressionis closest, and support varies by controller.- Ingress annotations do not carry over. Annotations such as
nginx.org/client-max-body-sizehave no Gateway API equivalent. Reimplement them as extension filters or your controller's own policy resources.gatewayApi.annotationsapplies to the Gateway object itself and is not a substitute. - There is no
backendServiceequivalent.ingress.backendServicecan benginxto keep the bundled Nginx pod as a last-hop reverse proxy. The HTTPRoute always targets the Artifactory service directly. - Routes attach only from the Gateway's own namespace. The Gateway sets
allowedRoutes.namespaces.from: Same.
Enable ingress and gatewayApi together during migration. Both resource sets are created and both paths work, so you can shift DNS gradually and roll back by repointing DNS rather than redeploying.
Verify the Deployment
PROGRAMMED must be True before traffic flows. An unattached HTTPRoute reports a parentRef condition explaining why.
kubectl get gateway --namespace artifactory
kubectl describe gateway --namespace artifactory
kubectl describe httproute --namespace artifactoryPoint DNS at the Gateway address, then check platform health. A healthy platform returns HTTP 200 with {"code": "OK"}.
curl https://artifactory.example.com/router/api/v1/system/readinessTo inspect what the chart will create without deploying, render locally. The --api-versions flag simulates the CRDs being present.
helm template artifactory jfrog/artifactory \
--api-versions "gateway.networking.k8s.io/v1" \
--values values.yaml \
--show-only templates/gateway-api/gateway.yaml \
--show-only templates/gateway-api/httproute.yamlTroubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
Install fails on gatewayClassName is required | enabled is true with an empty gatewayClassName | Set it to a name from kubectl get gatewayclass |
gatewayApi.enabled: true produces no resources and no error | Chart predates the feature, commonly jfrog-platform 11.5.x | Upgrade to jfrog-platform 11.6.0 or standalone 107.161.15 or later |
Gateway created but PROGRAMMED is not True | No controller matches the GatewayClass, or it is unhealthy | Confirm the class name and that its controller pods are running |
| HTTPRoute not attached | Route is in a different namespace than the Gateway | Move it into the release namespace |
SnippetsFilter created but has no effect | NGINX Gateway Fabric installed without snippet support | Reinstall with --set nginxGateway.snippetsFilters.enable=true |
| Two external load balancers provisioned | Bundled Nginx pod still enabled | Set nginx.enabled: false |
| TLS handshake fails for one hostname only | No tls entry resolves to that hostname, so it has no HTTPS listener | Add the hostname to the entry's hosts, or to gatewayApi.hosts |
| Docker operations fail while the UI works | Rewrites missing, or filter on the wrong rule | Confirm the extension filter. Filters attach only to the routerPath rule |
Limitations
- JFrog validated only NGINX Gateway Fabric. Other conformant controllers should handle core routing and TLS, but are untested.
- No
backendService: nginxequivalent. The bundled Nginx pod cannot serve as a last-hop reverse proxy. - Filters apply only to the router path rule. The
artifactoryPathrule receives none, so rewrites and proxy settings do not apply to/artifactory/traffic. Ingress differs, applyinglocation-snippetsto every generated path. - Cross-namespace routes are not supported. The Gateway uses
allowedRoutes.namespaces.from: Same. - Extension filter contents are not validated by the chart.
extensionFilter.specpasses through unchanged, so errors surface as controller-side failures rather than Helm errors.
Frequently Asked Questions
This section provides answers to frequently asked questions.
Frequently Asked Questions
Q: Which chart version added Kubernetes Gateway API support?
A: Use Artifactory or Artifactory HA chart 107.161.15 or later, released July 27, 2026. For the jfrog-platform chart, use 11.6.0 or later, which bundles it. The changelog attributes the feature to 107.147.0, but that version was never published, so 107.161.15 is the earliest installable release. Every jfrog-platform release in the 11.5.x line bundles Artifactory 107.146.x and does not include it.
Q: Can I run Ingress and the Gateway API at the same time?
A: Yes, and this is the recommended migration path. Enable both so both resource sets exist and both routes work. Shift DNS to the Gateway address once verified, then disable ingress. Keeping both active means rollback is a DNS change rather than a redeploy.
Q: Which Gateway API version should I install?
A: JFrog validated v1.4.1. The chart detects whether your cluster serves gateway.networking.k8s.io/v1 or v1beta1 and emits matching resources, so later standard-channel releases also work. Use standard-install.yaml. The experimental channel is not required for anything on this page.
Q: Why do my Docker rewrites not apply to /artifactory/ requests?
/artifactory/ requests?A: Filters configured through extensionFilter or httpRoute.filters attach only to the routerPath rule. The artifactoryPath rule receives no filters. This differs from Ingress, where location-snippets annotations apply to every generated path, so rewrites that worked on Ingress stop applying to /artifactory/ traffic after migrating. Route that traffic through the Router with disableRouterBypass: true if it needs the same rewrites.
Related Topics
- JFrog Platform Installation with NGINX Ingress Controller
- Ingress Behind Another Load Balancer
- Configure Nginx Service and Load Balancer for Helm
- Nginx SSL Termination at the Load Balancer
- Load Balancer
- Establish TLS in Artifactory and JFrog Platform
Updated about 15 hours ago
