## Set it and confirm it arrived

```bash
bin/kamal app exec 'bin/rails runner "puts Rails.application.config.x.app_host"'
```

If that prints nothing, the variable is not reaching the container. `APP_HOST` goes in the `env`
section of `config/deploy.yml` rather than in `.kamal/secrets`, because it is not a secret: it is
your public domain name.

## Why this only breaks in production

In development every link you click carries a request, and Rails builds URLs from it. A mailer is
different: `deliver_later` enqueues a job, Solid Queue runs it minutes later in a different process,
and there is no request anywhere in that path. The host has to come from configuration.

That is also why the failure is invisible in the test suite. Request specs supply a host, so links
look right, and the first broken link is the first real sign-in email a user receives.

## The blast radius is wider than email

The same setting feeds several things that all need an absolute URL and none of which have a
request:

- Magic-code sign-in links, which is the one users hit immediately.
- `<link rel="canonical">` on every public page.
- `/sitemap.xml`, `/feed.xml` and `/llms.txt`, which are built by `app/controllers/seo_controller.rb`.

A sitemap full of `localhost` URLs is submitted successfully and indexes nothing, with no error
anywhere.

## Verify from outside

```bash
curl -s https://yourdomain.com/sitemap.xml | head -5
```

The first `<loc>` should be your domain. That single check covers the mailer case too, because both
read the same configured host.

## Do not hardcode it in a mailer

It is tempting to pass `host:` into one `*_url` call and move on. That fixes one link and leaves the
other dozen, and it puts the domain in application code where a rebrand has to find it. One setting,
read everywhere, is the shape that stays correct.
