---
title: Custom domains
description: Point your own domain at a CoDuck project, with TLS handled automatically.
category: domains
order: 1
agent: "Custom domains attach to a project via 'coduck domains add <domain>'. Two-phase verify (ownership + routing). TLS auto-issued via Let's Encrypt once verified."
---

# Custom domains

Every project gets a `<project>.coduck.app` subdomain with HTTPS automatic. You can also attach one or more domains you own. CoDuck handles DNS verification, SSL via Let's Encrypt, and routing.

## The lifecycle

```
add  →  verify ownership  →  verify routing  →  active
```

1. **Add** registers the domain against the project and returns the DNS records you need to set on your registrar.
2. **Verify ownership** confirms you control the domain by looking up a TXT record CoDuck issued on `_coduck.<domain>`.
3. **Verify routing** confirms traffic for the domain reaches CoDuck by checking the apex `A` record or subdomain `CNAME`.
4. **Active** means both checks passed, Let's Encrypt has issued a certificate, and the per-project nginx vhost is serving traffic.

## Add a domain

1. Open your project at `https://app.coduck.ai/project/<projectId>`.
2. Switch to the **Cloud** panel, open the **Hosting** tab, then the **Domains** sub-tab.
3. Click **Add domain** and enter the domain you want to attach.
4. CoDuck displays the DNS records you need to set on your registrar (Namecheap, Cloudflare, GoDaddy, etc.).
5. Set the records, then click **Verify** once DNS has propagated.

> [!NOTE]
> DNS changes can take anywhere from seconds to an hour to propagate, depending on your registrar's TTL. If verification fails immediately, wait a minute and try again.

## DNS records you need to set

The exact values are shown when you add the domain. They follow this shape:

| Phase | Type | Name | Value |
|---|---|---|---|
| Ownership | TXT | `_coduck.<domain>` | `coduck-verify=<token>` (shown when you add the domain) |
| Routing (subdomain) | CNAME | `app` (or whatever subdomain) | `<project>.coduck.app` |
| Routing (apex) | A | `@` | CoDuck's published IP (shown when you add) |

The TXT record stays in place — CoDuck re-checks it periodically to keep the domain associated with your account.

## Apex vs subdomain

- **Apex** (`example.com`) needs an `A` record pointing at CoDuck's IP. Some registrars (Cloudflare, DNSimple) also support `ALIAS` or `ANAME` records that emulate CNAMEs at the apex — those work too.
- **Subdomain** (`app.example.com`) can use a `CNAME` pointing at `<project>.coduck.app`. This is preferred because IP changes on CoDuck's side propagate automatically without you touching DNS.

## SSL

Let's Encrypt issues a certificate automatically after routing is verified. There's no extra step on your end, and renewals are handled automatically before expiry.

## Managing existing domains

Under **Hosting → Domains** you can see every domain attached to the project, along with its current status — `pending`, `verifying`, `active`, or `failed`. From the same view you can:

- Set one domain as **primary** (the canonical redirect target for the project).
- **Remove** a domain, which detaches it and tears down the nginx vhost.
- **Transfer** a domain to a different project you own.

## Common failures

- **DNS not propagated** — verification ran before your registrar published the record. Check with `dig TXT _coduck.example.com` or `dig example.com`, then retry once the record is live.
- **Wrong record type at apex** — `CNAME` at an apex will be rejected by most registrars; use `A` (or `ALIAS`/`ANAME` if supported) instead.
- **Record at the wrong host** — the TXT goes on `_coduck.<domain>`, not the apex. Some registrar UIs auto-append the domain; double-check the final hostname.
- **Let's Encrypt rate limit** — if both verifications pass but SSL doesn't issue, you've likely hit Let's Encrypt's per-domain issuance limit. Wait an hour and re-verify.

## For developers

If you'd rather script domain operations than click through the dashboard, the CoDuck CLI exposes the same flow. See [CLI commands](/docs/cli/commands) for the full reference.

## Next

- [Deploying](/docs/projects/deploy) — what triggers a re-build vs. a config reload after a domain change.
- [Sending email](/docs/email/sending) — email uses domain verification too, but with DKIM CNAMEs only and a separate flow from custom domains.
