Migrate to Envoy Gateway
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
Starting with GitLab 19.0, GitLab chart disables the bundled NGINX Ingress and defaults to Gateway API and the bundled Envoy Gateway. All bundled Ingress controllers, including HAProxy and Traefik, are deprecated and will be removed in 20.0. Ingresses are not deprecated and will remain available after 20.0, but will need an external Ingress controller.
You can migrate from the bundled NGINX Ingress to Gateway API with either:
- A one step migration
- A multi-step migration with zero downtime.
If you do not want to migrate, you can continue using an Ingress controller.
Migrate in one step
Expect approximately 5 minutes of downtime during migration. The actual time may differ depending on your deployment, infrastructure, and configuration. For a zero-downtime approach, see zero downtime migration.
To migrate from (NGINX) Ingress to Gateway API and Envoy Gateway:
-
Install Envoy and Gateway API CRDs:
helm template eg-crds oci://docker.io/envoyproxy/gateway-crds-helm \ --version v1.9.0 \ --set crds.gatewayAPI.enabled=true \ --set crds.envoyGateway.enabled=true \ | kubectl apply --server-side -f - -
If not, install the Gateway API CRDs through your cloud provider or manually apply to your cluster.
-
Disable NGINX Ingress and Ingress resources:
# Disable bundled NGINX Ingress controller. nginx-ingress: enabled: false global: # Disable rendering of Ingress resources. ingress: enabled: false -
Configure Certmanager for Gateway API:
# Configure bundled certmanager for Gateway API support. certmanager: config: apiVersion: controller.config.cert-manager.io/v1alpha1 kind: ControllerConfiguration enableGatewayAPI: true global: gatewayApi: configureCertmanager: true -
Enable Envoy and Gateway API resources:
global: # Disable rendering of Ingress resources. gatewayApi: # Install a Gateway and Routes for each component. enabled: true # Install the bundled Envoy Gateway chart, a GatewayClass, a EnvoyPatchPolicy, and the EnvoyProxy resources. installEnvoy: true -
Configure the Gateway to bind a static IP address. By default the IP configured via
global.hosts.externalIPis reused.# Depending on your cloud provider you might to migrate additional annotations. global: hosts: # Only used by Envoy if bundled NGINX Ingress is disabled and no custom # gateway addresses are defined. externalIP: "10.10.0.1" gatewayApiResources: gateway: addresses: - type: IPAddress value: "10.10.0.2" infrastructure: annotations: {}Instead of using
global.hosts.externalIPorgatewayApiResources.gateway.addresses, configure the annotations for the provisioned LoadBalancer:gatewayApiResources: gateway: infrastructure: annotations: networking.gke.io/load-balancer-type: External networking.gke.io/load-balancer-ip-addresses: gitlab-ip-address cloud.google.com/l4-rbs: enabledTo migrate a EKS LoadBalancer, migrate your annotations from the NGINX controller service to the Envoy Gateway configuration:
gatewayApiResources: gateway: infrastructure: annotations: service.beta.kubernetes.io/aws-load-balancer-type: nlb service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "gitlab-allocation-id" service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" -
Upgrade your GitLab chart release with the updated values.
Migrate with zero downtime
To perform a zero-downtime migration, you can run NGINX Ingress and Envoy Gateway side by side, allowing two LoadBalancers to operate simultaneously. Once Envoy Gateway is fully configured to handle GitLab traffic, update the GitLab DNS records to point to the Envoy Gateway-managed LoadBalancer.
-
Enable Envoy Gateway and Gateway API resources without disabling NGINX Ingress:
nginx-ingress: enabled: true global: hosts: # External LoadBalancer IP bound by NGINX Ingress externalIp: "10.10.0.1" # Enable Gateway API and configure another gatewayApi: enabled: true installEnvoy: true gatewayApiResources: gateway: addresses: - type: IPAddress value: "10.10.0.2" infrastructure: annotations: {} -
Configure your TLS certificates or a certmanager issuer for the managed Gateway:
You can’t use the Issuer provided by GitLab chart for this purpose. The issuer uses HTTP01 which won’t be able to retrieve certificates until your DNS records have been updated.
-
Configure a DNS01 Issuer or customize the listeners to use already existing certificates.
-
If you created a custom Issuer, enable certmanager’s Gateway API support and annotate the managed Gateway:
# Enable Gateway API support for bundled certmanager. certmanager: config: apiVersion: controller.config.cert-manager.io/v1alpha1 kind: ControllerConfiguration enableGatewayAPI: true global: gatewayApi: # Do not configure HTTP01 issues. configureCertmanager: false gatewayApiResources: gateway: # Annotate Gateway to use custom DNS01 issuer. annotations: cert-manager.io/issuer: gitlab-dns01
-
-
Ensure GitLab is reachable if the domain would resolve to the IP of the Envoy Gateway LoadBalancer:
$ curl -Lso /dev/null \ --write-out 'Status: %{http_code} TLS: %{ssl_verify_result} (0=OK)' \ --resolve gitlab.example.com:443:10.10.0.2 \ "https://gitlab.example.com" Status: 200 TLS: 0 (0=OK) -
Update your DNS entries to resolve to the Envoy Gateway LoadBalancer.
-
Wait for the DNS entries to propagate to all clients.
-
Disable NGINX Ingress and Ingress objects:
nginx-ingress: enabled: false global: ingress: enabled: false
Continue using an Ingress controller
If you have not yet migrated to Gateway API and Envoy Gateway, you can disable all Gateway API components and continue using an Ingress controller.
If you have already migrated to Gateway API and are reverting to an Ingress controller, expect approximately 5 minutes of downtime during the switch, similar to the one step migration. The actual time may differ depending on your deployment, infrastructure, and configuration.
-
Disable all Gateway API components in your values file:
global: gatewayApi: enabled: false installEnvoy: false -
Enable Ingress resources and configure your Ingress controller:
If you manage your own Ingress controller outside of the GitLab chart, you only need to ensure Ingress resources are rendered. Do not enable the bundled NGINX Ingress controller.
nginx-ingress: enabled: false global: ingress: enabled: true configureCertmanager: trueFor more details, see external Ingress controller.
Re-enabling the bundled NGINX Ingress controller is the least preferred option. It is deprecated and will be removed in 20.0. The upstream NGINX Ingress project is also archived. Consider using an external Ingress controller or migrating to Envoy Gateway instead.
Enable the bundled NGINX Ingress controller and Ingress resources:
nginx-ingress: enabled: true global: ingress: enabled: true configureCertmanager: true -
Upgrade your GitLab chart release with the updated values.
Timeout settings
NGINX Ingress and Envoy Gateway express proxy timeouts differently. If you customized the NGINX Ingress timeout settings, the following table shows how they map to the Gateway API configuration for Webservice traffic and the defaults the chart ships with Envoy Gateway:
| NGINX Ingress setting | Previous chart default | Gateway API equivalent | Chart default |
|---|---|---|---|
ingress.proxyConnectTimeout (proxy-connect-timeout) |
15 |
gitlab.webservice.backendTrafficPolicy.spec.timeout.tcp.connectTimeout |
300s |
ingress.proxyReadTimeout (proxy-read-timeout) |
600 |
gitlab.webservice.backendTrafficPolicy.spec.timeout.http.streamIdleTimeout |
3600s |
ingress.proxyBodySize (proxy-body-size) |
512m |
None — Envoy streams request bodies without a size cap; limits are enforced by GitLab itself | Not applicable |
| None — NGINX applied no absolute request deadline | Not applicable | gitlab.webservice.deployments.<name>.gatewayRoute.rules[].timeouts |
0s (disabled) |
Like proxy-read-timeout, streamIdleTimeout is an inactivity timeout: it resets while data
flows in either direction, so long-running transfers are not interrupted. The absolute
HTTPRoute rule timeouts have no NGINX equivalent and stay disabled by default. For
configuration examples, see the
Webservice Gateway timeouts documentation.
Host headers with a trailing dot
NGINX removes a trailing dot from the Host header before matching a server name.
Envoy Gateway matches the Host or :authority header against route hostnames
literally, and Gateway API hostnames cannot contain a trailing dot.
As a result, clients that request an absolute fully qualified domain name with a
trailing dot, for example https://gitlab.example.com./, receive 404 Not Found
responses after the migration. The Envoy access log records these requests with
response_code_details: route_not_found. A common example is a GitLab Runner
configured with a trailing dot in the url setting to avoid DNS search domain
lookups. Affected runners fail to verify or register with status=404.
The chart enables the stripTrailingHostDot setting on the Webservice listeners
by default, which restores the NGINX behavior. This setting requires the bundled
Envoy Gateway 1.9 or later.
The setting also requires the ClientTrafficPolicy CRD from Envoy Gateway 1.9.
The Envoy Gateway 1.8 CRD does not define this field, so the API server prunes
it without an error and trailing-dot requests keep returning 404. Apply the
current CRDs before you upgrade the chart. For more information, see
Upgrade the Envoy Gateway CRDs.
On chart versions that bundle Envoy Gateway 1.8 or earlier, the setting is not
available. Remove the trailing dot from the client configuration instead. For
example, update the url setting in the GitLab Runner config.toml, or remove
the trailing dot from /etc/hosts entries on the client host.