Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- [Building images](building/guidance.md)
- [Container runtime vs bootc runtime](building/bootc-runtime.md)
- [DNS and resolv.conf](building/dns.md)
- [Users, groups, SSH keys](building/users-and-groups.md)
- [Kernel arguments](building/kernel-arguments.md)
- [Secrets](building/secrets.md)
Expand Down
6 changes: 5 additions & 1 deletion docs/src/building/bootc-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ The second, and most important mode of operation is when a bootc container is in

Crucially, besides setting up some mounts, bootc itself does not act as any kind of "container runtime". It does not set up pid or other namespace, does not change cgroups, etc. That remains the role of other code (typically systemd). `bootc` is not a persistent daemon by default; it does not impose any runtime overhead.

This distinction also applies to DNS configuration. A container runtime may
inject `/etc/resolv.conf` while building or running a container, but it is not
present to do so after a bootc image boots. See
[DNS and `/etc/resolv.conf`](dns.md).

Another example of this: While one can add [Container configuration](https://github.com/opencontainers/image-spec/blob/main/config.md) metadata, `bootc` generally ignores that at runtime today.

## Labels
Expand Down Expand Up @@ -88,4 +93,3 @@ system is deployed.

For more on the intersection of SELinux and current bootc (OSTree container)
images, see [bootc images - SELinux](../bootc-images.md#SELinux).

192 changes: 192 additions & 0 deletions docs/src/building/dns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# DNS and `/etc/resolv.conf`

`bootc` does not configure networking or select a DNS resolver. The operating
system in the container image must provide a coherent networking and DNS
policy, just as it must provide a kernel and an init system. Usually that
policy is owned by the distribution or base image.

The recommended approach is to configure DNS through the network management
and resolver services selected by the base image. Do not treat
`/etc/resolv.conf` as a file that should be copied from the container build
environment into the booted system.

## Build-time and boot-time resolver configuration

When an image is built, the container runtime normally provides temporary
`/etc/hostname`, `/etc/hosts`, and `/etc/resolv.conf` files so that `RUN`
instructions have working networking. These files describe the build
environment. They are not the DNS configuration of the machine that will boot
the image.

When the image is installed and booted, there is no outer Podman or other
container runtime to recreate these files. The network stack included in the
image is responsible for generating the booted host's resolver configuration.
See [Container runtime vs bootc runtime](bootc-runtime.md) for more about this
distinction.

In particular:

- Do not copy the build host's `/etc/resolv.conf` into an image.
- Do not rely on the `/etc/resolv.conf` visible in a networked `RUN`
instruction. It may be a bind mount owned by Podman or Buildah.
- Do not use an empty regular `/etc/resolv.conf` as a placeholder. `bootc`
identifies this as a likely container-build artifact and may remove it when
importing the image.
- Do not add files below `/run` to an image. `/run` is runtime state and is
recreated on every boot.

If a Containerfile step must intentionally replace `/etc/resolv.conf`, run the
step with networking disabled, for example with `RUN --network=none`. This
avoids modifying a container-runtime bind mount. It should only be necessary
when deliberately changing the resolver policy inherited from the base image.

## Follow the base image's resolver policy

A derived image should normally retain its base image's DNS implementation.
Configure DNS servers, search domains, and per-interface behavior through that
implementation rather than replacing `/etc/resolv.conf`.

Common policies include:

- NetworkManager supplies per-link DNS configuration to `systemd-resolved`,
and `/etc/resolv.conf` points to the local resolver stub.
- NetworkManager writes a regular `/etc/resolv.conf` directly.
- Another network manager or resolver owns the file under a
distribution-specific policy.

NetworkManager and `systemd-resolved` are not alternatives with the same role.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine as is but if it got much more complex we should probably redirect to more in-depth guidance for a particular OS and/or upstream documentation (esp for NetworkManager)

NetworkManager configures network interfaces and may use `systemd-resolved` as
its DNS backend. The base image must ensure that its selected services, the
type and target of `/etc/resolv.conf`, and its service enablement agree.

Environment-specific DNS configuration should generally be supplied when a
machine is provisioned. If DNS configuration is intentionally lifecycle-bound
to an image, include NetworkManager connection profiles or resolver drop-ins
in the image rather than including a generated `/etc/resolv.conf`. This keeps
the configuration declarative and makes its owner clear.

## Base images using `systemd-resolved`

A base image using `systemd-resolved` should ensure that:

- `systemd-resolved` is installed and enabled.
- NetworkManager or the selected network manager is configured to supply DNS
information to it.
- `/etc/resolv.conf` is a symlink to a resolver file generated below
`/run/systemd/resolve`, normally
`../run/systemd/resolve/stub-resolv.conf`.
- The symlink can be created on boot if it was not materialized during the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is what we expect to happen.

What we should be calling out here is that because of the /etc/resolv.conf being injected by container runtimes problem bootc actually always masks it (though just with the ostree backend today) see #1167

container build.

The upstream `systemd-resolved` package supplies a
[`systemd-tmpfiles`](https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html)
rule for the last requirement:

```text
L! /etc/resolv.conf - - - - ../run/systemd/resolve/stub-resolv.conf
```

The rule establishes the symlink during boot. It does not contain DNS server
addresses; `systemd-resolved` creates the target under `/run` from runtime
network configuration. Base image authors should normally use the rule
provided by the `systemd-resolved` package. A custom distribution integration
can ship an equivalent vendor rule in `/usr/lib/tmpfiles.d`.

Do not disable `systemd-resolved` while retaining this rule and symlink. That
leaves `/etc/resolv.conf` pointing to a runtime file that no enabled service
creates.

### Fedora CoreOS example

Fedora CoreOS uses NetworkManager with `systemd-resolved`. It retains the
resolver stub symlink and configures DNS through NetworkManager connection
profiles. For example, its Butane configuration examples write NetworkManager
keyfiles under `/etc/NetworkManager/system-connections/` and set the `dns` and
`dns-search` properties there.

See the Fedora CoreOS documentation for
[host network configuration](https://docs.fedoraproject.org/en-US/fedora-coreos/sysconfig-network-configuration/)
and Fedora's description of its
[`systemd-resolved` integration](https://fedoraproject.org/wiki/Changes/systemd-resolved).
Fedora CoreOS also has an integration
[test for its resolver policy](https://github.com/coreos/fedora-coreos-config/blob/testing-devel/tests/kola/networking/resolv/systemd-resolved).

This is an example of a complete base-image policy, not a requirement that all
bootc images use `systemd-resolved`.

## Base images using a regular `/etc/resolv.conf`

A base image may instead have NetworkManager or another service write a
regular `/etc/resolv.conf`. In that case it should ensure that:
Comment on lines +119 to +120

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write at runtime not build time, probably worth calling out


- `systemd-resolved` is not enabled.
- No tmpfiles rule recreates the `systemd-resolved` symlink.
- `/etc/resolv.conf` is absent or is a regular file that the selected service
is configured to manage.

For Fedora-derived images, removing the `systemd-resolved` package is generally
clearer than only disabling its service: the package also owns the tmpfiles
rule that creates the symlink. If the package must remain installed, the base
image must explicitly override the vendor tmpfiles rule as well as disabling
the service.

Any Containerfile operation that removes the inherited resolver symlink should
use `RUN --network=none`, because a networked build step may see a bind mount at
`/etc/resolv.conf` instead of the image's file.

## Traditional `resolv.conf` options and caching

Selecting a resolver backend can change observable DNS behavior. Options from
traditional `resolv.conf`, including lookup retry and search behavior, do not
all have direct equivalents in `systemd-resolved`. Conversely,
`systemd-resolved` provides features such as caching and per-link split DNS
that are not provided by a regular file alone.

Base image authors should select and document a resolver policy appropriate
for their distribution. Workloads that depend on particular behavior such as
`ndots`, timeouts, attempts, caching, or split DNS should be tested against
that resolver. `bootc` does not translate options between resolver

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bootc has nothing to do with /etc/resolv.conf other than the special case that we remove it if present in the container image.

I would just include links to the other relevant docs, we don't need to explain resolved vs resolv.conf semantics here.

implementations.

See the
[`systemd-resolved` documentation](https://www.freedesktop.org/software/systemd/man/latest/systemd-resolved.html)
and NetworkManager's documentation for
[`NetworkManager.conf`](https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/NetworkManager.conf.html)
and
[connection settings](https://networkmanager.dev/docs/api/latest/ref-settings.html)
for the behavior and configuration supported by each component.

## Static resolver configuration

A static `/etc/resolv.conf` can be appropriate for a deliberately static
system, but it is not the default recommendation. It prevents normal dynamic
Comment on lines +161 to +162

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkManager definitely supports configuring the DNS through it statically e.g. for a static IP address, and I think should be the preferred mechanism for systems using NM.

updates from DHCP, VPNs, and per-link configuration unless the network stack
is also configured not to manage the file.

If a static file is required:

1. Explicitly configure the network stack not to own `/etc/resolv.conf`.
2. Remove any inherited resolver symlink in a build step using
`RUN --network=none`.
3. Add the intended regular file from build context; do not copy the build

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work because of the above.

If someone wants to ship a static /etc/resolv.conf in their image, at the current time we probably need to recommend a systemd tmpfiles.d unit that symlinks it, to /usr/lib/resolv.conf or something else.

But again I think this is overall a bad idea, and it's better to configure DNS through the networking system (NM or resolved).

host's generated file.
4. Treat its contents as machine-local configuration when deciding whether it
belongs in the reusable image or in provisioning.

Remember that `/etc` is persistent and uses a three-way merge across bootc

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right this is why I think it's better to have it be a link to a file in /run (as systemd-resolved encourages) or a symlink to a file in /usr if it's truly static.

upgrades. A locally modified `/etc/resolv.conf` remains a local override and
may prevent a later image default from taking effect. See
[Filesystem: `/etc`](../filesystem.md#etc) for details.

## Application containers

Application containers and bootc hosts have different runtime behavior. By
default, Podman creates `/etc/hosts`, `/etc/hostname`, and `/etc/resolv.conf`
for each application container. Runtime options such as `podman run --dns`
control that generated file, and `--dns=none` requests use of the image's file.

The DNS options to `podman build` apply to networked `RUN` instructions; they
do not define the DNS policy of a host booted from the completed image. See the
Podman documentation for
[`podman run`](https://docs.podman.io/en/latest/markdown/podman-run.1.html) and
[`podman build`](https://docs.podman.io/en/latest/markdown/podman-build.1.html).
4 changes: 4 additions & 0 deletions docs/src/building/guidance.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ make in the container image to e.g. `/etc/postgresql.conf`
will be applied on update, assuming it is not modified
locally.

Resolver configuration has an additional interaction with the container
runtime used to build the image. See
[DNS and `/etc/resolv.conf`](dns.md) for the recommended approach.

### Prefer using drop-in directories

These "locally modified" files can be a source of state drift. The best
Expand Down
15 changes: 12 additions & 3 deletions docs/src/man/bootc-install-to-existing-root.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ cp /sysroot/etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scri
# Copy application configuration
cp -r /sysroot/etc/myapp /etc/

# Selectively merge configuration files
vi /etc/resolv.conf # Add nameservers from /sysroot/etc/resolv.conf
# Identify the old resolver policy, then migrate its authoritative
# network manager profiles or resolver configuration
stat -c '%F %N' /sysroot/etc/resolv.conf

# For user accounts, use proper tools
vipw # Carefully review and merge users from /sysroot/etc/passwd
Expand All @@ -120,6 +121,15 @@ This applies to network configurations, user accounts, application settings,
and other system configuration stored in `/etc`. Review files in `/sysroot/etc`
and manually copy or merge what you need into `/etc`.

**Note:** A generated `/etc/resolv.conf` should not normally be copied or
edited directly. It may be a dangling symlink after reboot or contain only a
local resolver address such as `127.0.0.53`, rather than the upstream DNS
servers. Migrate the authoritative configuration, such as NetworkManager
connection profiles or resolver drop-ins, into the configuration used by the
new system. If both systems intentionally use a hand-managed static file, it
may instead be selectively migrated. See
[DNS and `/etc/resolv.conf`](../building/dns.md) for details.

**Note:** For filesystem mounts from `/etc/fstab` in the old system, consider
using kernel arguments (via `systemd.mount-extra`) injected before reboot instead
of migrating the fstab entries. See the "Injecting kernel arguments" section above.
Expand Down Expand Up @@ -245,4 +255,3 @@ of migrating the fstab entries. See the "Injecting kernel arguments" section abo
# VERSION

<!-- VERSION PLACEHOLDER -->

Loading