# Custom domains

> Serve an app from a hostname you own on an own-cloud Starter cluster. Register the parent domain once, link each host, and Ownkube issues and renews the certificate.

Every public web app gets a hostname under `ownkube.app` with a certificate Ownkube manages. On an own-cloud **Starter** cluster you can also serve the same app from a hostname you own, such as `app.acme.io` or the bare `acme.io`, with HTTPS handled for you. You register the parent domain once per cluster, then link as many hosts under it as you need.

**Own-cloud Starter feature.** Custom domains run on a Starter cluster in your own AWS account, the [Advanced](/docs/own-cloud) path. Apps on Ownkube Compute are reachable at their `ownkube.app` address today; if you want your own domain there, email [support@ownkube.io](mailto:support@ownkube.io?subject=Custom%20domains%20on%20Compute) and tell us.

## How it works

A custom domain has two parts, and the dashboard walks you through both.

1. **Certificate verification**, once per parent domain. You add a single CNAME at your DNS provider that delegates the `_acme-challenge` name to Ownkube. That proves you own the domain, and Ownkube issues one certificate covering `acme.io` and `*.acme.io`. It renews on its own.
2. **Traffic routing**, once per host. You link a host to an app on the deployment's **Settings** tab, add the routing record the dashboard shows you, and the host comes online as soon as DNS resolves.

Certificates come from [Let's Encrypt](https://letsencrypt.org/) through the [DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge), which is why the apex domain works too: no `ALIAS` record tricks and no proxy in front.

## Before you start

- A [Starter cluster](/docs/own-cloud/create-cluster) on cluster platform 0.22.0 or newer.
- A public web app on that cluster. See [Public hostnames](/docs/networking/public-hostnames).
- Access to the DNS zone for the domain you want to use ([Cloudflare](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/), Route 53, Namecheap, or any provider that can create CNAME and A records).

## Register the parent domain

1. **Open the cluster's settings**

   Go to your [dashboard](https://app.ownkube.io/dashboard), open the cluster, and find the **Certificate verification** card under **Settings**.

2. **Enter the domain**

   Type the parent domain, for example `acme.io`. Every host under it, and the bare domain itself, will be covered by one certificate. Click **Add**.

3. **Add the verification record**

   The card shows one CNAME to create at your DNS provider:

   | Type | Name | Target |
   |---|---|---|
   | `CNAME` | `_acme-challenge.acme.io` | the Ownkube-owned name shown in the card |

   This record is the only thing Ownkube ever needs in your zone. It does not change where `acme.io` itself points.

4. **Wait for the certificate**

   Once the record resolves, the card reads **DNS verified, issuing your certificate**, then **Active**. Issuance usually finishes within a couple of minutes of the DNS change going live. Click **Verify** to recheck early.

## Link a host to an app

1. **Open the app's Settings tab**

   Open the deployment and go to **Settings**. The **Traffic routing** card lists linked hosts and the parent domains you have registered.

2. **Enter the hostname**

   Type the full host, such as `app.acme.io`, `api.acme.io`, or the bare `acme.io`, and click **Link**. A host can be linked to one app at a time.

3. **Add the routing record**

   The dashboard shows the record for that host:

   | Host | Type | Target |
   |---|---|---|
   | A subdomain such as `app.acme.io` | `CNAME` | an Ownkube name for your cluster |
   | The apex `acme.io` | `A` | your cluster's public IP |

   Add it at your DNS provider. The host moves from **pending** to **verifying** to **active** on its own. If the certificate for the parent domain is still being issued, the card says so and the host goes live once it is ready.

4. **Check it**

   Open `https://app.acme.io`. Your app answers over HTTPS with a certificate issued for your domain, and plain `http://` redirects to `https://`.

Your app sees the custom hostname in the `Host` header, so framework redirects and absolute URLs work without any forwarded-header configuration.

To stop serving a host, click **Unlink** next to it. The parent domain and its certificate stay registered so you can link the host again later or point it at a different app.

## From the CLI or an agent

The same flow is available in [`okctl`](/docs/cli) and through the [MCP server](/docs/mcp).

```bash title="Terminal"
# Linked hosts on an app, plus the parent domains available on its cluster
okctl domains list <deployment-id>

# Link a host; prints the routing record and the certificate challenge record
okctl domains add <deployment-id> app.acme.io

# Recheck DNS now instead of waiting for the next pass
okctl domains verify <domain-id>

# Unlink a host
okctl domains delete <domain-id>
```

## Limits

- One parent domain registration per domain per cluster. Two apps on the same cluster can share a parent domain with different hosts.
- A host links to one app at a time. Unlink it first to move it.
- Public web apps only. Workers, jobs, databases and caches have no public hostname to attach a domain to.
- Starter clusters only. On Production (EKS) clusters and Ownkube Compute, apps keep their `ownkube.app` hostname.

## Troubleshooting

<details>
<summary>"Couldn't find the DNS record yet"</summary>

The record has not propagated or was created on a different name. Check it from outside your network:

```bash title="Terminal"
dig +short _acme-challenge.acme.io CNAME
dig +short app.acme.io CNAME
```

Both should return the targets the dashboard shows. On Cloudflare, make sure the `_acme-challenge` record is **DNS only** (grey cloud), not proxied.

</details>

<details>
<summary>The host is active but the browser warns about the certificate</summary>

The parent domain's certificate is still being issued. Open the cluster's **Certificate verification** card and wait for **Active**, or click **Verify**. If it has been more than ten minutes, check that the `_acme-challenge` CNAME still exists.

</details>

<details>
<summary>I use Cloudflare's proxy in front of my site</summary>

You can keep the orange cloud on the routing record for the host. Set Cloudflare's SSL mode to **Full (strict)** so it validates the certificate Ownkube issued. Leave the `_acme-challenge` record unproxied.

</details>

- [Public hostnames](/docs/networking/public-hostnames)
- [Access protection](/docs/own-cloud/access-protection)
- [Clusters](/docs/own-cloud/clusters)

---

**Don't see a feature you need?** Email [support@ownkube.io](mailto:support@ownkube.io?subject=Feature%20request). Ownkube is shaped by the teams using it and we ship what our users ask for.

## Search

This site has a search endpoint: https://context7.com/api/v2/ask?siteKey=ask_c64dfe8ffaa8d3ad19576838&query=<URL-encoded question>. It returns documentation that answers the question, with a source link for each part. No API key is needed. If nothing matches, it says so.
