> ## Documentation Index
> Fetch the complete documentation index at: https://support.lilt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set Custom Domain And Certificates

LILT derives every hostname it serves from two variables in `install.env`. You set the domain once, and the installer resolves it into every values file, route, and service URL.

<Warning>
  Do not edit hostnames in `lilt/values.yaml`, `lilt/environments/*/values.yaml`, or the chart values files directly. Those files hold `__SUBDOMAIN__` and `__BASE_DOMAIN__` placeholders that the installer substitutes at install time. An edit there is either overwritten on the next run or leaves the deployment inconsistent with its routes.
</Warning>

## Set the Domain

Set both variables in `install.env`:

```bash theme={null}
SUBDOMAIN=prod
DNS_DOMAIN=example.com
```

`DNS_DOMAIN` is required on every install path. Together the two produce the hostnames in the following table.

## Hostnames LILT Serves

Every hostname is a sibling of the others, not a subdomain of the main one. This matters for both DNS and certificates.

| Hostname | Serves |
| - | - |
| `<SUBDOMAIN>.<DNS_DOMAIN>` | The web application and the public API |
| `core-api-<SUBDOMAIN>.<DNS_DOMAIN>` | The core API |
| `connectors-api-<SUBDOMAIN>.<DNS_DOMAIN>` | The connectors API |
| `wso2-<SUBDOMAIN>.<DNS_DOMAIN>` | The WSO2 identity server. Routed by default on EKS. On-premises it is opt-in: the routes ship disabled and `install-wso2.sh` is not called by `install-lilt.sh`. |
| `minio-<SUBDOMAIN>.<DNS_DOMAIN>` | MinIO object storage, on-premises installs only |
| `minio-<SUBDOMAIN>-ui.<DNS_DOMAIN>` | The MinIO console, on-premises installs only |
| `vault.<SUBDOMAIN>.<DNS_DOMAIN>` | The Vault UI, only when `ENABLE_VAULT_UI` is true. Override with `VAULT_UI_HOST`. |

## Point DNS at the Ingress

All hostnames resolve to the same address: the load balancer in front of the Istio ingress gateway.

### On-premises

MetalLB hands the ingress gateway the address you set in `LB_IP`:

```bash theme={null}
# install.env
LB_IP=10.124.0.234
```

`LB_IP` is required when `ENABLE_METALLB` is true, which is the default. The install stops with an error if it is unset. Create an A record for each hostname in the preceding table pointing at that address.

### On AWS EKS

The ingress gateway provisions a Network Load Balancer. Read its address after the install:

```bash theme={null}
kubectl get svc -n istio-ingressgateway
```

Create a Route53 alias record for each hostname pointing at that load balancer. To have the installer create the records for you, set:

```bash theme={null}
# install.env
ENABLE_DNS=true
HOSTED_ZONE_ID=Z0EXAMPLE1234567890
```

The installer then needs Route53 write access. See [Install from a bastion](/self-managed/v6.1/install-from-a-bastion#iam-permissions).

### Testing Before DNS Exists

To reach the application before the records are live, map the ingress address in your own `/etc/hosts`. Add every hostname, not only the main one, or sign-in fails when the browser is redirected to the identity server.

```
203.0.113.24  prod.example.com core-api-prod.example.com connectors-api-prod.example.com wso2-prod.example.com
```

## Certificates

Every listener on the ingress gateway reads one Kubernetes secret, `lilt-com-tls`, in the `istio-ingressgateway` namespace. `install-lilt-networking.sh` is what writes it, and it picks a source in this order: cert-manager if `ENABLE_CERT_MANAGER=true`, then `TLS_CRT_FILE` and `TLS_KEY_FILE`, then an existing secret it leaves alone. If none of those apply it warns and continues, and the gateway serves no HTTPS.

Choose one source of certificates.

| Path | How | Use it when |
| - | - | - |
| Your own certificate | Set `TLS_CRT_FILE` and `TLS_KEY_FILE` in `install.env`. | On-premises, air-gapped, or any deployment with its own certificate authority. This is the default path. |
| cert-manager | Set `ENABLE_CERT_MANAGER=true`. cert-manager issues the certificate through a Route53 DNS-01 challenge and then owns the secret. | EKS deployments with Route53 access and the cert-manager IAM role. Needs `ENABLE_DNS=true`, or DNS records that already exist for the challenge to resolve against. |
| The Terraform module, indirectly | Set `enable_tls_certificate = true` in `lilt-aws-env`. The module runs the ACME challenge and stores the certificate in Vault. It does not write the Kubernetes secret. | EKS deployments that want the certificate provisioned alongside the infrastructure. You still deliver it to the cluster, either by writing the files out and setting `TLS_CRT_FILE` and `TLS_KEY_FILE`, or by creating `lilt-com-tls` yourself. |

<Warning>
  Do not enable two of these. cert-manager and the Terraform module both run ACME challenges against the same Let's Encrypt identifier set, which is limited to five certificates per 168 hours. Two owners exhaust the quota and neither gets a certificate.

  Setting `TLS_CRT_FILE` together with `ENABLE_CERT_MANAGER=true` is safe but pointless: the installer skips the file seed when cert-manager is on, so your file is ignored.
</Warning>

### Required Subject Alternative Names

A certificate you supply yourself must cover every hostname LILT serves. Because the hostnames are siblings rather than subdomains, a single `*.<SUBDOMAIN>.<DNS_DOMAIN>` wildcard does not cover them.

```
*.<SUBDOMAIN>.<DNS_DOMAIN>
<SUBDOMAIN>.<DNS_DOMAIN>
core-api-<SUBDOMAIN>.<DNS_DOMAIN>
connectors-api-<SUBDOMAIN>.<DNS_DOMAIN>
wso2-<SUBDOMAIN>.<DNS_DOMAIN>
```

Add the MinIO hostnames on an on-premises install, and the Vault UI host if `ENABLE_VAULT_UI` is true.

<Warning>
  A missing name is easy to miss and slow to diagnose. Browsers that already trust the main hostname keep working, while API clients fail hostname verification against the API hosts. The symptom reads as a TLS bug rather than as an incomplete certificate.
</Warning>

Check what a certificate actually covers before you install:

```bash theme={null}
openssl x509 -in tls.crt -noout -text | grep -A1 'Subject Alternative Name'
```

### Supplying the Certificate

Point the two variables at PEM files on the machine running the installer:

```bash theme={null}
# install.env
TLS_CRT_FILE=/opt/lilt/certs/tls.crt
TLS_KEY_FILE=/opt/lilt/certs/tls.key
```

`TLS_CRT_FILE` must be the full chain: your certificate followed by any intermediates. A chain missing its intermediate still satisfies a browser that has cached it, and fails later in a stricter client.

## Apply a Change

Both the domain and the certificate take effect when you re-run the installer. Every component uses `helm upgrade --install`, so a re-run only changes what differs.

```bash theme={null}
sh install-lilt.sh        # on-premises
sh install-lilt-eks.sh    # EKS
```

## Rotate a Certificate

When cert-manager or the Terraform module owns the secret, rotation is automatic and you do nothing.

For a certificate you supply, replace the files and re-run the installer. It overwrites `lilt-com-tls` with the new material:

```bash theme={null}
cp new-tls.crt /opt/lilt/certs/tls.crt
cp new-tls.key /opt/lilt/certs/tls.key
sh install-lilt-eks.sh
```

The ingress gateway picks the new certificate up without a restart. Confirm the expiry date changed:

```bash theme={null}
kubectl get secret lilt-com-tls -n istio-ingressgateway \
  -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates
```

## Verify

```bash theme={null}
# The certificate the gateway serves, and its names.
openssl s_client -connect <SUBDOMAIN>.<DNS_DOMAIN>:443 -servername <SUBDOMAIN>.<DNS_DOMAIN> </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates -ext subjectAltName

# Every hostname should answer, and verify cleanly. Drop wso2- if you do not
# run the identity server.
for h in "" core-api- connectors-api- wso2-; do
  printf '%s: ' "$h<SUBDOMAIN>.<DNS_DOMAIN>"
  curl -sS -o /dev/null -w '%{http_code} verify=%{ssl_verify_result}\n' \
    "https://$h<SUBDOMAIN>.<DNS_DOMAIN>/"
done
```

`verify=0` on every hostname means DNS, the load balancer, and the certificate are all correct. The status code itself varies by host, and a 401, 404, or redirect is still proof that the request reached the gateway over a valid certificate. A connection or TLS error is not.

## Troubleshooting

**The gateway serves no HTTPS at all.** The installer warns and continues when no certificate is available. Check whether the secret exists:

```bash theme={null}
kubectl get secret lilt-com-tls -n istio-ingressgateway
```

If it is missing, neither `TLS_CRT_FILE` nor cert-manager was configured. Create it directly, then re-run the installer:

```bash theme={null}
kubectl create secret tls lilt-com-tls -n istio-ingressgateway --cert=tls.crt --key=tls.key
```

**Sign-in redirects to a hostname that does not resolve.** The identity server runs on its own hostname. Confirm that `wso2-<SUBDOMAIN>.<DNS_DOMAIN>` resolves and is covered by the certificate.

**A hostname returns 404 through the gateway.** The route for it did not install. Check the HTTPRoutes:

```bash theme={null}
kubectl get httproute -n istio-ingressgateway
```

## Related Articles

* [Install System (AWS EKS)](/self-managed/v6.1/install-system-aws-eks)
* [Install on an existing EKS cluster](/self-managed/v6.1/install-aws-existing-cluster)
* [Provision AWS infrastructure with Terraform](/self-managed/v6.1/install-aws-terraform-module)
* [Single sign-on (SSO)](/self-managed/v6.1/single-sign-on-sso)
