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

ChartMinimum versionReleased
artifactory107.161.15July 27, 2026
artifactory-ha107.161.15July 27, 2026
jfrog-platform11.6.0Bundles 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"]
ResourceResponsibility
GatewayEntry point. Declares listeners and terminates TLS.
HTTPRoutePath-based routing to Artifactory service ports.
Extension filterController-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

  1. 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.yaml

    JFrog validated v1.4.1. The chart checks whether your cluster serves gateway.networking.k8s.io/v1 or v1beta1, then emits resources with the matching version. Later standard-channel releases work too. The experimental channel is not required.

  2. Run a Gateway controller. Without one the Gateway is created but never programmed, and no traffic reaches Artifactory.

    ControllerSupport status
    NGINX Gateway FabricValidated by JFrog. Extension filter examples target it.
    Envoy GatewayNot validated. Expected to handle core routing, since the chart emits standard fields.
    Istio Gateway controllerNot validated. Expected to handle core routing.
  3. Create a matching GatewayClass. Its name must equal your gatewayApi.gatewayClassName, otherwise the install fails with gatewayApi.gatewayClassName is required when gatewayApi.enabled is true.

    kubectl get gatewayclass
  4. Set nginx.enabled: false. The bundled Nginx pod defaults to enabled and is exposed as a LoadBalancer service. 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: true and 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.com
helm upgrade --install artifactory jfrog/artifactory \
  --namespace artifactory --create-namespace \
  --set global.masterKey=${MASTER_KEY} \
  --set global.joinKey=${JOIN_KEY} \
  --values values.yaml

For 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.com
⚠️

Later Changes Need databaseUpgradeReady

Changing any gatewayApi value on a deployed release means running helm 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.

PathDestinationPortSource of port
/Artifactory service, JFrog Router8082artifactory.externalPort
/artifactory/Artifactory service, direct8081artifactory.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:

  1. The hosts list on that tls entry.
  2. The top-level gatewayApi.hosts list.
  3. 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-tls

Docker 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 SnippetsFilter resources 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-rewrites

Additional 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: 8082

Use 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.

ParameterTypeDefaultDescription
enabledboolfalseCreate Gateway API resources. Requires the CRDs.
gatewayClassNamestring""Name of an existing GatewayClass, such as nginx. Required when enabled is true.
namestring""Override the Gateway name. Defaults to the chart fullname plus -gateway.
hostslist[]Hostnames for the HTTPRoute. Same semantics as ingress.hosts. Empty matches any hostname.
httpExternalPortint80Port for the HTTP listener.
httpsExternalPortint443Port for each HTTPS listener.
routerPathstring/Path routed to the JFrog Router on artifactory.externalPort.
artifactoryPathstring/artifactory/Path routed directly to Artifactory on artifactory.externalArtifactoryPort.
pathTypestringPathPrefixPathPrefix, Exact, or RegularExpression. Gateway API rejects ImplementationSpecific.
disableRouterBypassboolfalsetrue removes the artifactoryPath rule so all traffic passes through the Router.
additionalDirectRoutesstring or list""Extra HTTPRoute rules appended to the route. Rendered through tpl.
additionalRulesstring""Extra HTTPRoute rules as a YAML string. Rendered through tpl.
tlslist[]TLS entries. Each requires secretName and creates one HTTPS listener per host.
labelsmap{}Additional labels on the Gateway.
annotationsmap{}Additional annotations on the Gateway.
httpRoute.enabledbooltrueCreate the HTTPRoute. false creates only the Gateway, and also suppresses the extension filter.
httpRoute.namestring""Override the HTTPRoute name. Defaults to the chart fullname plus -httproute.
httpRoute.labelsmap{}Additional labels on the HTTPRoute.
httpRoute.annotationsmap{}Additional annotations on the HTTPRoute.
httpRoute.filterslist[]Filters applied to the router path rule, such as an ExtensionRef to an externally managed filter.
extensionFilter.enabledboolfalseCreate a controller-specific filter and wire it into the HTTPRoute automatically.
extensionFilter.namestring""Override the filter name. Defaults to the chart fullname plus -extension-filter.
extensionFilter.apiVersionstring""API version of the filter CRD, such as gateway.nginx.org/v1alpha1. Required when enabled.
extensionFilter.kindstring""Kind of the filter CRD, such as SnippetsFilter. Required when enabled.
extensionFilter.specmap{}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 nameGatewayHTTPRouteExtension filter
artifactoryartifactory-gatewayartifactory-httprouteartifactory-extension-filter
myreleasemyrelease-artifactory-gatewaymyrelease-artifactory-httproutemyrelease-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 valueGateway API value
ingress.enabledgatewayApi.enabled
ingress.classNamegatewayApi.gatewayClassName
ingress.hostsgatewayApi.hosts
ingress.routerPathgatewayApi.routerPath
ingress.artifactoryPathgatewayApi.artifactoryPath
ingress.pathTypegatewayApi.pathType
ingress.disableRouterBypassgatewayApi.disableRouterBypass
ingress.tlsgatewayApi.tls
ingress.additionalRulesgatewayApi.additionalRules
ingress.additionalDirectRoutesgatewayApi.additionalDirectRoutes

Four differences need attention.

  1. pathType is not interchangeable. Ingress defaults to ImplementationSpecific, which the Gateway API rejects. Use PathPrefix, Exact, or RegularExpression. If you relied on ImplementationSpecific regular expression behavior, RegularExpression is closest, and support varies by controller.
  2. Ingress annotations do not carry over. Annotations such as nginx.org/client-max-body-size have no Gateway API equivalent. Reimplement them as extension filters or your controller's own policy resources. gatewayApi.annotations applies to the Gateway object itself and is not a substitute.
  3. There is no backendService equivalent. ingress.backendService can be nginx to keep the bundled Nginx pod as a last-hop reverse proxy. The HTTPRoute always targets the Artifactory service directly.
  4. 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 artifactory

Point 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/readiness

To 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.yaml

Troubleshooting

SymptomCauseResolution
Install fails on gatewayClassName is requiredenabled is true with an empty gatewayClassNameSet it to a name from kubectl get gatewayclass
gatewayApi.enabled: true produces no resources and no errorChart predates the feature, commonly jfrog-platform 11.5.xUpgrade to jfrog-platform 11.6.0 or standalone 107.161.15 or later
Gateway created but PROGRAMMED is not TrueNo controller matches the GatewayClass, or it is unhealthyConfirm the class name and that its controller pods are running
HTTPRoute not attachedRoute is in a different namespace than the GatewayMove it into the release namespace
SnippetsFilter created but has no effectNGINX Gateway Fabric installed without snippet supportReinstall with --set nginxGateway.snippetsFilters.enable=true
Two external load balancers provisionedBundled Nginx pod still enabledSet nginx.enabled: false
TLS handshake fails for one hostname onlyNo tls entry resolves to that hostname, so it has no HTTPS listenerAdd the hostname to the entry's hosts, or to gatewayApi.hosts
Docker operations fail while the UI worksRewrites missing, or filter on the wrong ruleConfirm 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: nginx equivalent. The bundled Nginx pod cannot serve as a last-hop reverse proxy.
  • Filters apply only to the router path rule. The artifactoryPath rule receives none, so rewrites and proxy settings do not apply to /artifactory/ traffic. Ingress differs, applying location-snippets to 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.spec passes through unchanged, so errors surface as controller-side failures rather than Helm errors.

Frequently Asked Questions

This section provides answers to frequently asked questions.

plusFrequently 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?

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



Did this page help you?