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

# Add a Custom Domain

> Put your application on your own domain: create or import a Route 53 zone with fjall domain, deploy it, verify delegation, bind your app, and keep the zone clean.

## Prerequisites

Complete these before adding a domain:

* [**Authenticated with Fjall**](/cli/login) (run `fjall login`)
* [**Connected an AWS account**](/cli/connect) (run `fjall connect`)
* [**An application to put on the domain**](/deployment/create-application) — deployed, or ready to deploy

A domain is declared in `fjall/domains/<zone>/infrastructure.ts` with the typed `Domain` construct — the file you edit is the file that deploys. See [Domain](/resources/networking/domain) for the construct API and [fjall domain](/cli/domain) for every verb and flag.

***

## Create the domain

Scaffold a new domain in your project root:

```bash theme={null}
fjall domain create example.com
```

This writes `fjall/domains/example.com/infrastructure.ts` and registers the domain in `fjall-config.json`, so the organisation cascade can deploy it. Edit the declaration to add your records — MX, TXT, DKIM, whatever the zone needs.

### Or import an existing zone

Already hosting the domain in Route 53? Import it — the declaration is curated from the live zone, with records owned by other live stacks deliberately left undeclared:

```bash theme={null}
fjall domain import example.com
```

Or adopt the live zone by reference while writing the declaration yourself:

```bash theme={null}
fjall domain create example.com --hosted-zone-id Z1234567890ABC
```

If the domain is registered outside AWS with no Route 53 zone yet, plain `fjall domain create` is the right start — delegation to Route 53 happens after the first deploy.

***

## Deploy the zone

```bash theme={null}
fjall domain deploy example.com
```

The deploy echoes the resolved AWS account and target before anything else runs, then creates the hosted zone, the declared records, and DNS-validated ACM certificates. Pick the account explicitly with `-t, --target <name>` (and `-r, --region <region>`) — an unresolvable target fails closed with the available targets listed.

<Note>
  The first deploy of an **adopted** zone runs the record-takeover ceremony:
  the deploy stops with a ticket naming each record both the live zone and
  your declaration claim, and you consent per record with
  `--remediate '<name>/<TYPE>'=recreate` — there is no blanket flag. Consent
  withheld means exit code `4`: nothing released, nothing deployed. See
  [Destructive Changes](/deployment/destructive-changes).
</Note>

***

## Verify delegation

Check that the domain's nameservers point at Route 53:

```bash theme={null}
fjall domain verify example.com
```

For a domain registered outside AWS, print the nameservers to set at your registrar:

```bash theme={null}
fjall domain verify example.com --show-delegation
```

Update the NS records at your registrar, wait for propagation (typically a few hours), and re-run `fjall domain verify` until it reports the delegation live.

***

## Point your application at it

Set the application's `domain` — that is the whole mechanism. At deploy time the CLI injects a **managed domain binding** with literal values (hosted zone ID, certificate ARNs), so the app binds to the zone with no extra wiring, across accounts and regions.

For a [static site](/patterns/static-site-pattern), pass the domain at create time:

```bash theme={null}
fjall create app --name my-site --pattern staticsite \
  --source ../.. --build-command "npm run build" --output-dir out \
  --pattern-domain example.com
```

Or set `domain: "example.com"` in the pattern's `infrastructure.ts`. A compute application declares it the same way — `domain: "api.example.com"` on the `ComputeFactory` (see [Application Binding](/resources/networking/domain#application-binding)). Then deploy the app as normal:

```bash theme={null}
fjall deploy my-site
```

The pattern creates the alias record in the zone and attaches the domain's certificate; the deploy output includes your custom-domain URL.

***

## Keep the zone clean

List every live record with its ownership classification (`declared`, `satellite`, `residue`, or `unknown`):

```bash theme={null}
fjall domain records list example.com
```

Report the classification of the whole zone, then delete provably orphaned residue by named consent:

```bash theme={null}
fjall domain sync example.com
fjall domain sync example.com --fix
```

`--fix` runs the same ceremony as deploy — a ticket names each residue record, you consent per row, and withheld consent exits `4` with nothing deleted. Records classified `unknown` are never offered for deletion.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="fjall domain" icon="terminal" href="/cli/domain">
    Every domain verb, flag, and exit code.
  </Card>

  <Card title="Domain construct" icon="code" href="/resources/networking/domain">
    Declare zones, records, certificates, and delegation in infrastructure.ts.
  </Card>

  <Card title="Static Site Pattern" icon="globe" href="/patterns/static-site-pattern">
    Serve a static site on your domain via CloudFront.
  </Card>

  <Card title="Destructive Changes" icon="shield-halved" href="/deployment/destructive-changes">
    How the destruction ceremony and named consents work.
  </Card>
</CardGroup>
