Domains
This page tells how to choose where the DNS zones of the deployments are, and how to assign a domain to a deployment. On this page, <domain> is the domain of your hosting provider deployment.
Choose where the DNS zones are
A hosting provider deployment uses DNS at three levels:
| Level | What it holds | Where |
|---|---|---|
| The registrar | The registration of a domain, its nameservers, the glue records and the DNSSEC key | OpenProvider, Porkbun or INWX |
| The zone of your domain | The names of your nodes, of your services and of the apps of the deployments | The bind nodes of your deployment, or an external service: PowerDNS or deSEC |
| The zone of a domain that a deployment registers | The names of the apps of that deployment | The bind nodes of your deployment (the default), or deSEC |
DNS_SERVER_URL and DNS_PROVIDER select the external service for the zone of your domain. OPERATOR_ZONES_PROVIDER set to desec puts the zones of the domains that the deployments register at deSEC.
Why the registrar holds no records
The registrar effects write only the delegation of a domain. They write no records into a zone, also when the registrar has a DNS service. Thus:
- Only the delegation depends on the registrar. When you change the registrar, the zones do not change. When you move the zones, you change only the nameservers and the DNSSEC key at the registrar.
- Each registrar has a different API. A zone at the registrar would make each part that writes records depend on the registrar.
- The zones use standard protocols: dynamic updates (RFC 2136) with TSIG keys that may write only some names, zone transfers, catalog zones (RFC 9432), and DNSSEC signing that your deployment controls. The DNS service of a registrar does not always give these.
BIND or an external service
With bind, your deployment is the authority for its zones. It depends on no DNS service of a third party. You then operate the nameservers: their availability, their DNSSEC keys and their security.
With an external service, that service is the authority for the zone, and your deployment depends on the service and its API. Your deployment gives the service one API token that can write the full zone. For each deployment that registers a domain, the api makes a deSEC token that can write only the zone of that deployment. When the zone of your domain is at deSEC, a deployment with a domain under your domain writes its names into the zone of your domain: the api makes a deSEC token that can write only the A and AAAA records of the names of that deployment. Thus your zone must be in the deSEC account of OPERATOR_ZONES_MANAGER_TOKEN. A deployment with your domain itself gets no token and writes no records.
These tokens work from each network. To let them work only from the deployers of the deployments, set OPERATOR_ZONES_ALLOWED_SUBNETS to their networks, in CIDR notation and separated by spaces. The api sets these networks on each token of your deployment at each deploy and each hour. When the destroy of a deployment succeeds, the api deletes its tokens. Each hour, the api also deletes the tokens of your deployment that are older than one hour and that no deployment uses: the deployment does not exist, has no build, or its last build is a destroy that succeeded, and no zone follows it after a migration. Thus a token that a failed delete left at deSEC goes away. The api does not delete the zones of the deployments at deSEC.
An operator can give a token of a deSEC account of their own, which holds the zone of their domain, with the deploy. Then the api does not make a token or a zone at deSEC for that deployment, and deletes the tokens that it made for it. The zone and the token are then in the account of the operator, not in your account. OPERATOR_ZONES_ALLOWED_SUBNETS and the hourly delete do not apply to that token, and a destroy does not delete it at deSEC. The api refuses the token for a domain under your domain.
The ACME challenge zones
A wildcard certificate, for example the certificate of the S3 names of Garage, needs an ACME DNS-01 challenge. The node writes the challenge record with a TSIG key that may write only one record type at one name. The token of an external service can write the full zone, thus your deployment does not give it to a node. When the zone of your domain is at an external service, your deployment keeps one bind primary, which serves only one zone for each challenge name. The zone of your domain delegates each challenge name to this primary with an NS record. These challenge zones are not signed.
NIS2
NIS2 (Directive (EU) 2022/2555) makes each "DNS service provider" an essential entity, regardless of its size (Art. 2(2)(a)(iii) and Art. 3(1)(b)). A DNS service provider gives "authoritative domain name resolution services for third-party use" (Art. 6(20)(b)). An essential entity must register, take security measures and report incidents. NIS2 applies to cloud computing service providers only from the medium size. Thus the DNS can make NIS2 apply to a small hosting provider that NIS2 does not otherwise include.
- The names of your nodes and of your services, and the challenge zones, are for your own use.
- The names of the apps of the deployments are for the use of third parties, the operators, also in the zone of your domain. You offer these apps to the operators as a service. The zones of the domains that the deployments register are also for the use of the operators. When your
bindnodes serve one of these zones, a strict reading makes you a DNS service provider. - Lawyers do not agree whether DNS that comes with hosting is in scope, and national laws can differ. For example, the German law ignores activities that are negligible for the business. Fediversity does not give legal advice.
- When an external service serves a zone, that service is the DNS service provider for it, not you. Thus to keep all third-party names off your
bindnodes, put the zone of your domain at PowerDNS or deSEC, and the zones of the domains that the deployments register at deSEC. The challenge zones then stay onbind. The DNS service of a registrar would have the same result, but your deployment does not use it (see above).
Assign a domain to a deployment
When a hosting provider has a registrar account, a deployment can register its
domain through it. OpenBao records which deployment has each domain. These
records are the domain claims, at vars/public/domain-claims/<domain> in the
secret store of OpenBao, at https://secrets.<domain>/. The deployment field
of a claim is the namespace of the deployment.
Each domain has an owner contact, and where the TLD has them, also an administrative, a technical and a billing contact. Each of these is a domain contact of the operator, which the operator saves in the panel (see Let an operator save their domain contacts). The operator picks a contact for each role of each deployment. A role without a pick takes the owner contact. Thus the operator is the registrant of record of each domain that a deployment registers or transfers, and complaints about the domain go to the operator. You are never the owner of the domain of an operator: your deployment has no fallback owner. The api and the plan refuse a deploy that registers or transfers a domain without an owner contact.
The operator accepts the terms of the registrar, of the registry and of the TLD, for example the rules on transfers and on the correct contact data. Tell the operators which of these terms apply to them, for example in your contract, see Contract templates.
The panel keeps the handle of each contact, and shows the contacts in Domain contacts in the admin of the panel. A superuser cannot change a handle there, because the handles of all operators are in one registrar account. Each deploy gives the handles of the roles of the deployment to the deployment.
Before a registration or a transfer, the plan reads the contacts at the
registrar, and stops when the account does not have a handle, or when the
registry does not accept the owner contact as the owner. The file
core/effects/tf/incus/operator/registry-owners.json has the countries of an
extension: the country of the address must be one of them, or the citizenship
that the contact states for the extension, for example for the rule of .eu. An
extension that is not in this file has no check, so check the rules of its TLD
yourself. The plan does not stop a deployment whose name the account already
holds.
A change of the administrative, the technical or the billing contact of an
existing domain applies at the next deploy of its deployment. A deploy does not
change the owner: see Change the owner of a domain.
Another front-end gives the IDs of the contacts as domain_contacts, a map of
owner, admin, tech and billing to contact IDs, in the answer of its
authorization callback, and the api gives the handles of these IDs to the
deployment.
Each extension has the terms of its registry, and a country-code extension can
have terms that are different from those of the registrar. Set
OPERATOR_REGISTRAR_EXTENSIONS to the extensions whose terms you accept, in
lower case without a leading dot, separated by spaces, for example com nl eu.
Some registries also need the acceptance of their terms by the registrant at
each registration, for example dk and se. The table
core/effects/tf/incus/operator/registry-terms.json has, for each such
extension, the documents of these terms, their version, and the flags that
tell the registrar about the acceptance. The API shows these documents to the
operator next to your domain terms, and refuses a deploy that registers or
transfers such a domain for a deployment until the operator accepts the
current version of both. The deploy then sends the flags to the registrar. An extension of the
table without documents has terms that the table does not have: the eval
refuses it in OPERATOR_REGISTRAR_EXTENSIONS, and the API and the deploy
refuse its domains. Each document has a sha256: for an HTML page, that of
the text of the page, else that of the file. Every month,
.forgejo/workflows/registry-terms.yaml gets each document. When a document
changed, it opens a pull request that sets the new sha256, and sets the
version of the entry to the date. Read the new document before the merge: a
new version applies to each domain with that extension. The
run fails when it does not get a document, for example when the registry moved
its terms to a new URL. An entry can have noticeDays, the days of notice that
the registry gives. For such an entry, the pull request also sets effective,
the date from which the new terms apply, to the version plus these days.
Correct this date in the pull request to the date that the registry gives. For
an entry without noticeDays, the pull request removes effective, and the
panel shows no date. The registry can send its notice only to the registrant of
record, which is you unless the owner is a contact of an operator: Punktum dk
does this for dk. The panel tells the
operators of a domain after the merge, thus merge before the date.
The extension of a domain is all its labels after the first, thus co.uk of
example.co.uk, and uk does not include co.uk. Then the API refuses a
deploy that registers a domain with a different extension, and the deploy does
not register or transfer it. The domain that the stored configuration of a
deployment has stays in use, and a deploy adopts a domain that your registrar
account holds, whatever its extension. Thus a removal from the list does not
stop a deployment. The API reads your registrar account to accept such a
domain, thus without a registrar command it refuses the domain. Unset, a
deploy can register each extension that the registrar sells.
To make each operator accept your domain terms, publish them at a URL, and set
OPERATOR_REGISTRAR_TERMS_URL to that URL and
OPERATOR_REGISTRAR_TERMS_VERSION to their version, for example 2026-01. Set
both or neither. Then the panel shows a link to the terms and a checkbox at a
deploy and at a migration import, and the API refuses a deploy that registers a
domain for a deployment without the acceptance of the current version. The API
records the accepted version in the audit event, and the version and its time
on the deployment. When you change the terms, set a new version. A new version
does not stop a deployment that has its domain already: its deploys go on, and
the deployment keeps the version that the operator accepted. An automatic
update does not check the terms. Unset, a deploy needs no acceptance. Set
OPERATOR_REGISTRAR_TERMS_EFFECTIVE to the date from which the terms apply, for
example 2026-02-01, to show it to the operator. This date needs the URL and
the version.
The domain card of a deployment page shows the operator the terms of the domain of that deployment, with their date if the date is known, and the version that the deployment accepted. When the current version is a different version, the card tells the operator that the acceptance is pending, and has a button that accepts the current version. The API records this acceptance in the audit chain and on the deployment. The acceptance starts no deploy. A deployment with no recorded version is also pending.
Each day, the panel sends an email to the operator of each pending domain, once
for each version. The email has the documents of the terms, their dates, and a
link to the deployment page. The API records the notice in the audit chain and
on the deployment, without the address of the operator. An operator with no
email address gets no notice, and the panel tries again on the next day. The
panel sends this email through the panel-smtp resource, thus a deployment
without a relay node sends no notice. Before you change the terms, make sure
that a relay node is in the deployment.
The handle of the operator is the owner of each domain that a deployment registers or transfers. Thus the operator is the registrant of record of it. The registrant of record is liable for the wrongful use of a domain that a different party uses, unless the registrant discloses the identity and the contact data of that party. For a generic TLD, section 3.7.7.3 of the ICANN Registrar Accreditation Agreement gives 7 days to disclose them to a party that gives reasonable evidence of actionable harm. When the account holds a domain with your handle as the owner, and an operator uses it, this liability is yours. The domain claims name the deployment of each domain, and the panel names the operator of each deployment. Keep the contact data of each operator in your contract: the software keeps only the handle of a contact, and not its data.
The agreement of your registrar can keep you responsible for the compliance of
your operators: the agreement of openprovider does this for a reseller. Thus
put an indemnity in your contract,
see Contract templates.
When a deployment deploys a domain:
- If the domain is free, the deployment writes its claim, and then registers the domain. The registration costs at most the configured maximum price. The deployment does not register a premium domain.
- If the registrar account has the domain, and the claim names this deployment, the deployment uses the domain.
- If the registrar account has the domain and there is no claim, the target of a migration writes its claim and uses the domain. The api does this for the builds of a migration import, until one of them succeeds.
- If a different registrar account has the domain, and the deployment has its
auth code from a migration string or from
domain_transfer_in, the deployment writes its claim, and then transfers the domain to the registrar account. The transfer costs at most the configured maximum price, also for a premium domain. Until the transfer is complete, the deploy fails. Deploy again when it is complete. If the registry refuses the transfer, the deploy fails, and does not send the transfer again: the api does that when it gets the code again. - If the registrar account has the domain, and the claim names a different deployment, or there is no claim and the deployment is not the target of a migration, the deploy fails.
When a deployment is destroyed, its claim is removed, but the registration stays in the registrar account until its expiry.
A deployment can also use a name below the domain of your zone, for example
demo.<domain>. This name has no registration, but it has a claim in the same
place, which the api writes. When a deployment deploys such a name:
- If a different deployment has a claim on the name, on a name above it, or on a name below it, the deploy fails. A suspended deployment keeps its claims. A destroy of the deployment removes them.
- If the zone of your domain has records at the name, below it, or at a name between the name and your domain, the deploy fails. Records that a deploy of the same deployment with the same name made do not count.
- When the deployment changes to a different name, it keeps the old name until a deploy of the new name succeeds.
The api finds the records of the zone through the API of the DNS provider of
the zone. For deSEC, the api lists the records of the zone. For each other DNS
provider, such as your own bind nodes, the api asks the primary nameserver of
the zone. DNS cannot list the names below a name, thus this check does not see
the records below the name. When the nameserver does not answer, the deploy
goes on. The panel does the same checks before it saves a configuration.
The api gives the domains of the deployments of the caller with GET /domains.
The status of a domain is:
held: a deployment that is not destroyed uses the domain. If the caller owns that deployment,holdernames it.assignable: the deployment that had the domain is destroyed, and the caller owns it.holdernames it.unavailable: the deployment that had the domain is destroyed, but the caller does not own it, or the claim names a deployment that the api does not know.unregistered: no deployment of the api registered the domain.transferred: a migration export or a take-out gave the auth code of the domain, and the domain left: a transfer moved it to a different registrar account, or the target of a migration with the same account locked it again. No deployment of this hosting provider can use the domain.
To give a domain to a different deployment, the owner of that deployment sends
PUT /deployments/domain_claim/<deployment> to the api. The configuration of
the deployment must register the domain. The api writes the claim, and the next
deploy of the deployment uses the domain. The api does not start the deploy. In
the panel, the page of the deployment shows the status of its domain. When the
status is assignable, the page has the button "Give the domain to this
deployment", which sends this call. If
a destroyed deployment had the domain, the caller must also own that deployment.
A superuser of the panel can give a domain to the deployment
of each operator, also from the destroyed deployment of a different operator.
The api refuses the claim with conflict when a deployment that is not
destroyed has the domain, when the claim names a deployment that the api does not
know, when no deployment of the api registered the domain, or when the status is
transferred.
A superuser of the panel can also give a domain with a claim
that names a deployment that the api does not know, or a domain that no
deployment of the api registered. The call is
PUT /deployments/domain_assign/<deployment>, and the page "Domains" of the panel
has a form for it. Without the query parameter domain, as with
domain_claim, the configuration of the deployment must register the domain.
With domain, for example ?domain=example.org, the deployment gets that
domain, also when its configuration has a different domain. Use this to give a
domain of the registrar account to a deployment that did not have it yet. The
form of the panel has a field "Domain" for this. Then the operator sets the
domain in the configuration of the deployment and deploys, and the deploy adopts
the domain from the registrar account.
The api also refuses domain_assign with conflict when a deployment that is
not destroyed has the domain, or when the status is transferred. A deployment
gives up its domain when it is destroyed, or when a deploy of a different domain
deletes its claim. Thus, to move a domain between two operators, the first
operator destroys the deployment or deploys it with a different domain, and then
a superuser gives the domain to the deployment of the second operator.
A superuser can also give a domain that a destroyed or never deployed deployment had, when the panel has no record of that deployment, for example after a reset of the database of the panel.
The registrar renews a registration at its expiry only if
OPERATOR_REGISTRAR_AUTORENEW is true. The renewal costs the renewal price of
the extension, which can be much more than the price of the registration. The
configured maximum price does not apply to a renewal. A change of the setting
applies to a domain at the next deploy of its deployment. An operator can turn
off the renewal of the domain of their deployment in the panel, which starts a
deploy. An operator cannot turn on a renewal that the setting turns off. The
renewal of a domain is:
- on or off as the setting says, after each deploy that uses the domain, unless the operator of the deployment turned it off;
- unchanged while its deployment is suspended;
- unchanged after the destroy of its deployment. Thus a domain that no deployment uses still renews, until the operator of the destroyed deployment turns the renewal off on the page "Domains" of the panel. In a migration, the next deploy of the target sets the renewal.
Every 6 hours, the api node reads the domains of the registrar account and the
domain claims. The panel "Registered domains" of the dashboard "Infrastructure
overview" shows the time until the expiry of each domain, its renewal, and its
holder: deployment (the claim names it in claimed_by), provider (the
domain of the hosting provider, or a name below it), or none. Grafana sends
these alerts:
- A deployment holds the domain, but its renewal is off: a warning 30 days before the expiry, and a critical alert 7 days before it. If the operator turned the renewal off, this is what they want, and the registration ends at the expiry. Else turn the renewal on in the registrar account, or remove the domain from the deployment.
- The renewal is on, but the domain expires in less than 7 days: a critical alert. The registrar did not renew the domain, for example because the account has no funds.
A domain that no deployment holds gives no alert. If
the api node cannot read a domain, its unit registrar-domains fails, and the
alert of a failed unit occurs.
The domain card of a deployment in the panel shows the operator when the
registration of its domain expires, and if the registrar renews it then. The
values come from the same read of the registrar account, or from the last
deploy when that read is older. When the deployment holds the domain and the
renewal is off, the card shows a warning. These values describe the
registration in your registrar account. They do not tell the operator who pays
for the renewal, or who decides it: OPERATOR_REGISTRAR_AUTORENEW decides it.
Tell the operators what your contract says about this.
Some cases are different from a domain that an operator registers for their own use:
- A name below a domain of your registrar account, for example a demo at
demo.example.eu, has no registration of its own. Its deploy does not register, renew or releaseexample.eu, and its destroy does not turn the renewal ofexample.euoff. The card shows the registration ofexample.eu, and tells this. Whenexample.euexpires, the name stops working, thus keep the renewal ofexample.euon while a deployment uses a name below it. - When the renewal of
example.euis off, for example because the operator of a destroyed deployment turned it off, the card of a deployment of a name below it shows the warning. - A name below a domain that your registrar account does not hold has no values on the card. The holder of that domain decides its renewal.
- A demo or a test that registers a domain of its own is a deployment like the others: you pay for the registration, and the destroy keeps the renewal. Thus the domain still renews after the destroy, unless the page "Domains" of the panel or the registrar account turns its renewal off.
- A domain that the operator holds at their own registrar, and a name below the domain of the hosting provider, have no domain card.
- Without the read of the registrar account, a name below a domain has no values. A domain then has the values of the last deploy, when the deploy recorded a domain event of it.