Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Runtime Boundaries

AtomicNix separates immutable platform code from operator-provisioned runtime behavior.

Immutable Platform

The image owns boot, kernel, initrd, RAUC, firewall defaults, SSH policy, local provisioning, LAN gateway services, OpenVPN recovery plumbing, and update confirmation logic. These live in the active squashfs slot and are replaced only by RAUC updates.

Persistent Operator State

/data/config/ owns runtime configuration imported during provisioning. RAUC slot writes do not modify /data.

The bootstrap API is LAN-local and exposes POST /api/config for complete config.toml files or supported config bundles. It uses the same validation and persistence path as the web console and returns JSON success or validation errors for programmatic clients.

The accepted config.toml schema is:

version = 1

[admin]
ssh_keys = ["ssh-ed25519 ..."]

[firewall.inbound]
tcp = [443]
udp = [1194]

[lan]
gateway_cidr = "172.20.30.1/24"
dhcp_start = "172.20.30.10"
dhcp_end = "172.20.30.254"
domain = "local"
gateway_aliases = ["atomicnix"]
hostname_pattern = "atomicnix-{mac}"

[health]
required = ["myapp"]

[container.myapp]
privileged = false

[container.myapp.Container]
Image = "ghcr.io/example/myapp:latest"
PublishPort = ["10080:8080"]

[firewall.inbound] is required and must contain at least one TCP or UDP port. [lan] is optional; omitted fields use the fallback LAN gateway contract.

Firewall JSON

/data/config/firewall-inbound.json is a JSON object with optional tcp and udp arrays of integer ports in 1..65535.

{
  "tcp": [443],
  "udp": [1194]
}

Provisioned rules are added only on WAN eth0. The baseline firewall remains deny-by-default for new eth0 inbound traffic and drops all forwarding.

LAN JSON

/data/config/lan-settings.json is generated from config.toml and includes the validated runtime fields consumed by lan-gateway-apply.py.

{
  "gateway_cidr": "172.20.30.1/24",
  "gateway_ip": "172.20.30.1",
  "subnet_cidr": "172.20.30.0/24",
  "netmask": "255.255.255.0",
  "dhcp_start": "172.20.30.10",
  "dhcp_end": "172.20.30.254",
  "domain": "local",
  "hostname_pattern": "atomicnix-{mac}",
  "gateway_aliases": ["atomicnix"]
}

The DHCP range must stay inside the /24 gateway subnet, must be ordered, and must not include the gateway IP.

Quadlet Safety Boundary

Provisioned containers are rendered into canonical Quadlet files under /data/config/quadlet/ before being synced into Podman systemd search paths.

Rootful containers require privileged = true and are forced onto Network=host. Rootless containers use the appsvc user, are forced onto Network=pasta, and non-loopback PublishPort binds are rewritten to 127.0.0.1.

Bundle imports may include files/; Quadlet values may reference ${CONFIG_DIR} and ${FILES_DIR} to bind files from /data/config/ without embedding host-specific absolute paths in the seed.