> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.jambonz.org/self-hosting/kubernetes/exoscale-sks/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.jambonz.org/_mcp/server. # Exoscale (SKS) ## Prerequisites You will need: * An Exoscale account at [exoscale.com](https://www.exoscale.com) * API credentials (key and secret with full permissions) * Terraform >= 1.5 * kubectl installed * [helm](https://helm.sh/docs/intro/install/) installed * The [Exoscale CLI](https://community.exoscale.com/documentation/tools/exoscale-command-line-interface/) (`exo`), for verification and cleanup ## Provision the SKS Cluster The Terraform templates for provisioning an SKS cluster [are available here](https://github.com/jambonz-selfhosting/terraform/tree/main/exoscale/provision-sks-cluster). * Clone the [Terraform repository](https://github.com/jambonz-selfhosting/terraform) to your local machine. * Navigate to `exoscale/provision-sks-cluster`. * Export your API credentials: ```bash export EXOSCALE_API_KEY="your-api-key" export EXOSCALE_API_SECRET="your-api-secret" ``` * Copy `terraform.tfvars.example` to `terraform.tfvars` and edit it with your desired settings. * Run `terraform init && terraform plan && terraform apply` to provision the cluster. * Configure kubectl: `export KUBECONFIG=$(pwd)/kubeconfig && kubectl get nodes` > **Warning** > > Set `exoscale_api_key` and `exoscale_api_secret` in `terraform.tfvars` — the environment variables alone are **not** sufficient here. The Terraform provider falls back to `EXOSCALE_API_KEY` / `EXOSCALE_API_SECRET`, but the `exoscale-eip-creds` Kubernetes secret is built from the Terraform *variables*, which have no such fallback. Set only the environment variables and the cluster comes up fine with an empty credentials secret, and the EIP allocator then cannot authenticate. The Terraform also creates the `jambonz` namespace and the `exoscale-eip-creds` secret for you, so those steps are already done when you deploy the chart. > **Note** > > With multiple node pools, Exoscale requires a load balancer annotation naming the instance pool that should receive traffic. The exact command, with your cluster's instance pool ID filled in, is printed by `terraform output usage_instructions`. ## Deploy jambonz Install Traefik with the instance pool annotation (the namespace already exists): ```bash helm repo add traefik https://traefik.github.io/charts helm repo update helm install traefik traefik/traefik --namespace jambonz \ --set "service.annotations.service\.beta\.kubernetes\.io/exoscale-loadbalancer-service-instancepool-id=" ``` Then install the chart from a clone of the [Helm chart repository](https://github.com/jambonz-selfhosting/helm-chart): ```bash helm install jambonz . --namespace jambonz \ --set cloud=exoscale \ --set baseUrl=jambonz.example.com \ --set storageClassName=exoscale-sbs \ --set sbc.eipAllocator.enabled=true \ --set sbc.eipAllocator.rtpEnabled=false \ --set sbc.eipAllocator.exoscaleApiKeySecret=exoscale-eip-creds \ --set sbc.eipAllocator.exoscaleZone= ``` > **Note** > > `sbc.eipAllocator.rtpEnabled=false` is specific to Exoscale, and deliberate. The SIP nodes get a **managed** Elastic IP; the RTP nodes get none, because a managed Elastic IP requires a TCP healthcheck target and an RTP node has none that is safe to expose. RTP does not need one — Exoscale gives every SKS node a routable public IP directly, and that is what rtpengine advertises. Leave it at the default `true` and the RTP pod will fail to start, looking for a pool that intentionally does not exist. Databases are initialized by two Jobs, which must complete before the application pods can start: ```bash kubectl -n jambonz get jobs # db-create and db-seed must show Complete kubectl -n jambonz get pods ``` Confirm the SIP node claimed its Elastic IP, and that the address actually answers: ```bash terraform output -json sip_eip_addresses kubectl -n jambonz logs ds/jambonz-sbc-sip -c eip-allocator | tail -1 nc -z -w 5 5060 && echo reachable ``` ## Set up DNS Get the load balancer's address: ```bash kubectl -n jambonz get svc traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}' ``` On Exoscale this is an **IP address**, so create A records pointing at it for your portal hostname plus the `api.` and `grafana.` subdomains. ## Enable HTTPS Install cert-manager, then set `global.traefik.tls.enabled=true`, `global.traefik.clusterIssuer=letsencrypt-prod` and `global.traefik.email` in your values and run `helm upgrade`. ## Log In Browse to your portal hostname and log in as `admin` / `admin`. You will be prompted to change the password immediately. Then complete the [Post-Install Steps](/self-hosting/overview/post-install-steps) and generate a license key as described in [Software Licensing](/self-hosting/overview/licensing). ## Cleanup > **Note** > > You must delete any Kubernetes LoadBalancer services (which create Exoscale NLBs) before running `terraform destroy`, or the destroy will fail because the instance pools are locked by the NLBs. 1. Uninstall the releases, which removes the Traefik service and with it the NLB: ```bash helm -n jambonz uninstall jambonz helm -n jambonz uninstall traefik kubectl -n jambonz delete pvc --all kubectl delete namespace jambonz ``` 2. Confirm no NLB remains, and delete any that does: ```bash exo compute load-balancer list --zone exo compute load-balancer delete --zone ``` 3. Run `terraform destroy`. ## Upgrading an existing cluster If your cluster predates managed Elastic IPs, adding the healthcheck that converts a manual Elastic IP to a managed one **crashes the Exoscale Terraform provider** in place: ``` Error: The terraform-provider-exoscale plugin crashed! ``` Recreate the address instead: ```bash terraform apply -replace='exoscale_elastic_ip.sip[0]' ``` ## Resources * [SKS Terraform templates](https://github.com/jambonz-selfhosting/terraform/tree/main/exoscale/provision-sks-cluster) * [jambonz Helm chart](https://github.com/jambonz-selfhosting/helm-chart) > Deploy jambonz on Exoscale Scalable Kubernetes Service