RealNAS runs on vanilla FreeBSD. The central rule is practical: FreeBSD owns the operating system, and RealNAS owns NAS-related orchestration. The daemon may expose host operations in a UI or API, but the underlying system remains FreeBSD with normal pkg, freebsd-update, bectl, sysrc, service, pw, zfs, and zpool behavior.

Client data access does not pass through RealNAS. OpenZFS stores the data, Samba serves SMB, and the FreeBSD NFS server serves NFS. RealNAS configures and monitors those systems.

Component Map

flowchart TB Browser(["Web browser / administrator"]) CLI(["Shell / midclt / automation"]) subgraph RealNAS [" RealNAS userspace packages "] direction TB WebUI["realnas_webui
HTTPS static server + reverse proxy
default 0.0.0.0:4200"] Daemon["realnasd / middlewared
Python plugins, jobs, events
SQLite + generated config"] SQLite["/var/db/realnas/realnas-v1.db
NAS orchestration state"] Bootstrap["realnas-bootstrap
idempotent first-boot mutation"] end subgraph FreeBSD [" FreeBSD host "] direction TB Bindings["native bindings
py-bsd / py-netif / py-libzfs / py-sysctl / TDB"] Adapters["tool adapters
service, sysrc, pkg, freebsd-update
bectl, zfs, zpool, pw, smartctl, geli"] Services["data services
OpenZFS, Samba, NFS, SMART, reporting"] Files["host state
rc.conf, users/groups, generated service config"] Kernel(["FreeBSD kernel"]) end Browser -- "HTTPS / WebSocket" --> WebUI WebUI -- "loopback /api + /websocket" --> Daemon CLI -- "Unix socket / loopback API" --> Daemon Bootstrap --> Daemon Bootstrap --> SQLite Daemon --> SQLite Daemon -- "library calls" --> Bindings Daemon -- "structured argv" --> Adapters Daemon -- "etc.generate" --> Files Bindings --> Kernel Adapters --> Services Services --> Kernel Files --> Services classDef edge fill:#ffffff,stroke:#d8d3c8,stroke-width:1px,color:#1a1a1a; classDef accent fill:#ffffff,stroke:#8a2424,stroke-width:1.5px,color:#1a1a1a; classDef kernel fill:#f4f0e6,stroke:#8a2424,stroke-width:1px,color:#1a1a1a,font-style:italic; class Browser,CLI edge; class WebUI,Daemon,Bootstrap accent; class SQLite,Bindings,Adapters,Services,Files edge; class Kernel kernel;
The browser reaches the WebUI proxy; the daemon reaches FreeBSD through bindings, generated config, and named command adapters.

Runtime Pieces

realnasd: Middleware Daemon

realnasd is the middlewared process started by /usr/local/etc/rc.d/realnasd. It runs as root because ZFS, users, services, generated configuration, sockets, and some hardware operations require host privileges.

Default rc.conf behavior:

realnasd_enable="NO"
realnasd_loglevel="INFO"
realnasd_tcp_listen="YES"
realnasd_bind_host="127.0.0.1"
realnasd_bind_port="6000"
realnasd_src="/usr/local/share/realnas/middleware/src/middlewared"
realnasd_logfile="/var/log/realnas/middlewared.log"

Set realnasd_tcp_listen="NO" to use only Unix sockets. A wildcard bind such as 0.0.0.0 is an explicit operator decision, not the packaged default.

realnas_webui: Browser Surface

realnas_webui is a small HTTPS static server and reverse proxy started by /usr/local/etc/rc.d/realnas_webui. It serves the Angular bundle from /usr/local/www/realnas, proxies /api/ to middleware, and bridges /websocket.

realnas_webui_enable="NO"
realnas_webui_host="0.0.0.0"
realnas_webui_port="4200"
realnas_webui_backend="http://127.0.0.1:6000"
realnas_webui_tls="YES"
realnas_webui_cert="/var/db/realnas/webui.crt"
realnas_webui_key="/var/db/realnas/webui.key"
realnas_webui_logfile="/var/log/realnas/webui.log"

The WebUI is the intended public management surface. The middleware backend remains loopback by default.

realnas-bootstrap: Mutable First-Boot Setup

Package installation lays down files. realnas-bootstrap performs runtime mutation: Python wheel installation from the offline wheelhouse, native binding verification, schema creation, singleton seed rows, pwenc_secret, WebUI TLS material, realnasd startup, builtin account import, system dataset setup, hosts generation, and reporting daemon enablement.

This split is deliberate. Reinstalling or upgrading a package should not silently rotate secrets, rewrite unrelated FreeBSD state, or hide bootstrap failures inside a package hook. Bootstrap is explicit and idempotent.

State Ownership

The RealNAS state model is hybrid. If FreeBSD already has a standard owner for a setting, RealNAS uses that owner or a narrow adapter. If the setting is NAS orchestration state with no FreeBSD-native home, RealNAS stores it in SQLite and renders service configuration from it.

Domain Owner Storage / Interface
Service enablement FreeBSD /etc/rc.conf, written with sysrc.
Base system updates FreeBSD freebsd-update, pkg, and bectl.
Boot environments FreeBSD/ZFS bectl and ZFS boot datasets.
ZFS pools, datasets, properties, snapshots OpenZFS Read through py-libzfs and zfs/zpool adapters where needed.
Local users and groups FreeBSD account database plus middleware convergence pw, /etc/master.passwd, /etc/group, and user.sync_builtin.
SMB shares and share options RealNAS orchestration rendered to Samba SQLite, Samba registry/share config, and generated Samba files.
NFS exports RealNAS orchestration rendered to FreeBSD NFS Middleware-generated exports; use UI/API rather than hand-editing generated output.
Snapshot and replication schedules RealNAS SQLite, with zettarepl execution.
Alerts and notification routes RealNAS SQLite and local logs/email integrations.
WebUI TLS first-boot material Operator-owned runtime files created by bootstrap /var/db/realnas/webui.crt and /var/db/realnas/webui.key.

Generated Configuration

Generated config is regenerated from middleware state. Operators should not edit generated files in place and expect edits to persist. Each generated surface should have one of four policies: a preserving include/drop-in path, a database-backed auxiliary/options field, a RealNAS-owned generated file with no safe include path, or a deferred optional-feature classification.

Preserving Drop-Ins

Service Generated File Preserving Path
nginx-based proxy contexts, where enabled /usr/local/etc/nginx/nginx.conf /usr/local/etc/nginx/conf.d/*.conf and /usr/local/etc/nginx/realnas-server.d/*.conf.
OpenSSH daemon /usr/local/etc/ssh/sshd_config /usr/local/etc/ssh/sshd_config.d/*.conf.
Apache WebDAV /usr/local/etc/apache24/httpd.conf /usr/local/etc/apache24/Includes/*.conf.
sudo /usr/local/etc/sudoers /usr/local/etc/sudoers.d.
Avahi mDNS /usr/local/etc/avahi/services/*.service RealNAS removes only its own generated service names; unrelated operator service files are preserved.
NUT /usr/local/etc/nut/* RealNAS-generated files are overwritten, but unrelated operator files in the directory are preserved.

Database-Backed Extensions

AFP, FTP/ProFTPD, rsync daemon, SMB, SNMP, and OpenVPN expose auxiliary/options fields where supported. Those fields are preferred because they can be validated and rendered with the service state. Arbitrary SMB include = ... lines are intentionally rejected from SMB auxiliary input.

RealNAS-Owned Generated Files

Some generated files have no safe include/merge contract or contain secrets. Examples include generated account databases, mail aliases, middleware cron entries, /etc/fstab, host/resolver files, DHCP client policy, directory-service client files, PAM/2FA files, Kerberos keytabs, iSCSI target config, NFS exports, SMART config, loader policy, WebUI/certificate key material, reporting config, dynamic DNS config, WebDAV auth files, Avahi daemon settings, UPS config, and SSH host keys. Use the owning UI/API for those surfaces.

Dependency Policy

RealNAS prefers FreeBSD ports and native bindings before local parsers or vendored wheels. The release package still stages an offline wheelhouse so installation does not need PyPI, but that wheelhouse is not the desired long-term owner for every dependency.

Surface Current Policy
Network interfaces Use packaged net/py-netif from the RealNAS ports overlay. The old command-backed ifconfig/netstat parser has been retired.
Resolver / DNS Use FreeBSD resolvconf(8) through middlewared.common.resolver.apply_resolvconf() with allowlisted argv, source-name validation, and NUL rejection on rendered content. Falls back to etc.generate('resolv') only when no resolvconf binary is present.
TDB / Samba databases Use native databases/tdb Python bindings with required Tdb and open symbols; missing native TDB is a packaging failure.
ZFS Prefer devel/py-libzfs for state reads. CLI fast paths require structured argv and explicit reasons.
sysctl Use packaged devel/py-sysctl for reads and middlewared.common.sysctl for active writes.
GEOM and disk topology Use py-bsd, kern.geom.confxml, and centralized CAM parsing. Destructive disk commands route through disk command adapters.
Python wheelhouse Every pinned runtime wheel is classified as port, vendor, optional-plugin, or remove.
SQLAlchemy Runtime uses SQLAlchemy 2.0-compatible query construction and package recipes require py311-sqlalchemy20.

Privileged Command Ownership

RealNAS avoids ad hoc privileged command execution. High-risk mutation classes have named owners and validate through a privileged command registry.

Owner Commands
boot environment beadm, bectl, zectl.
disk disk_resize, geli, gmirror, gmultipath, gpart, swapoff, swapon.
kernel module kldload, kldunload.
rc.conf sysrc.
service lifecycle service.
sysctl sysctl.
update freebsd-update, pkg.
user database chpass, pw, pwd_mkdb, vipw.
ZFS CLI zfs, zpool.

Read-only probes can still use structured argv when a library is not available. Mutation paths with state ownership or rollback implications need an owner adapter.

Plugin Surface Policy

Default plugin loading is filtered by plugin class:

core
The normal default runtime: storage, sharing, users, services, alerts, reporting, system, update, and related dependencies.
compatibility
Truthful compatibility surfaces that kept WebUI/API callers still probe, such as disabled or single-node answers.
optional
Graduateable future packages such as VM, jail/plugin, KMIP, VMware, failover, NIS, Fibre Channel, support, and hardware health.
disabled
Inherited, proprietary, Linux-only, enterprise-only, or otherwise non-default modules that should not import silently.

Default imports are core and compatibility. Optional stacks must be enabled deliberately, for example with REALNAS_PLUGIN_CLASSES=core,compatibility,optional, and should come with package dependencies plus feature-level smoke coverage.

Allowed Default Stubs

A default stub is acceptable only when the feature is absent from the default install, existing kept middleware or WebUI code still probes the namespace, the answer is truthful, and the namespace is documented. Core workflow namespaces such as pool import, dataset encryption, disk mutation, users/groups, shares, services, networking, alerts, and generated configuration must not be hidden behind canned success.

Current deferred/compatibility namespaces include failover, vm, kmip, vmware, support, jail, plugin, fcport, enclosure, nis, and the legacy identity alias truenas. system.health graduated to a real plugin that aggregates pool status, per-disk SMART results, and CPU temperature sensors; SES enclosure remains a separate stub. Future implementations should replace deferred namespaces rather than adding parallel APIs.

APIs

RealNAS exposes plugin methods over WebSocket and JSON-RPC-like HTTP paths through the WebUI proxy. The UI does not get a private backend. Supported UI operations should be scriptable through the same middleware methods.

midclt call system.info
midclt call pool.query
midclt call core.get_jobs
curl -k https://127.0.0.1:4200/api/docs/

Update Model

RealNAS does not use the inherited appliance image train updater. The middleware method update.update drives a six-step atomic transaction over FreeBSD-native tools, with paired boot-environment and database rollback. Either the upgrade is canonical (committed BE, upgraded packages, migrated schema, healthy services) or the system is back at the pre-upgrade state with one reboot and zero manual DB reconciliation.

  1. DB snapshot. Copy /var/db/realnas/realnas-v1.db to a sibling .pre-update file via tmp + atomic os.replace. Fatal if it cannot write.
  2. Boot environment. bectl create pre-update-<timestamp> with conflict-suffix retry. Fatal if it cannot create.
  3. Base patches. freebsd-update install (skipped on PkgBase systems where pkg owns /usr/bin/uname).
  4. Package upgrade. pkg upgrade -y with a 30-minute timeout.
  5. Schema migration. Sets the run_migration keyvalue flag and runs the python-coded migration modules against the upgraded codebase.
  6. Health check. Calls system.info, systemdataset.config, and a user.query count; collects every failure before raising.

On any failure in steps 3-6, both rollback channels fire in order: bectl activate on the pre-update BE (deferred — applies on next boot) and an immediate atomic restore of the DB snapshot (the next middleware request sees pre-update schema). The resulting CallError concatenates the failure cause with both rollback messages. On success the snapshot is deleted.

Destructive upgrade-matrix coverage against real failed pkg / freebsd-update runs (debt item STOR-003) remains open release-hardening work. The transaction shape and rollback paths are pinned by architecture-contract gates.

Observability and Diagnostics

Diagnostic capture is local and operator-driven. RealNAS does not phone home, and the inherited support-upload paths are removed.

system.debug_generate
Produces a local .tgz under the system dataset's ixdiagnose/. The bundle contains the legacy ixdiagnose tarball plus a RealNAS subtree: realnas-bootstrap.log, last-smoke-report.json when present, generated rc.conf.freenas/rc.conf.local drop-ins, the last 64 KiB of middlewared.log, a filtered pkg info listing for RealNAS / py311-* / samba / openzfs, and TDB diagnostics (module path, package status, file metadata — no TDB contents).
Adapter breadcrumbs
Common adapters that shell out (disk_commands, sysctl, kld, resolver) emit a structured JSON record on every subprocess invocation: adapter name, argv, return code, sanitized stderr with NUL escaping and 400-character truncation, and an optional rollback annotation. INFO on success, ERROR on non-zero return code. Grep /var/log/realnas/middlewared.log for adapter_breadcrumb to follow what the daemon ran.
System health
system.health aggregates pool status, per-disk SMART, and CPU temperature into {status, components[]} with OK / WARNING / CRITICAL precedence; each probe is exception-isolated so one failing source does not blank the report.

Smoke and Regression Gates

The current source tree treats smoke results as release evidence, not as website claims detached from code. Recent gates cover:

  • Package artifact freshness before VM teardown.
  • Cold install on FreeBSD 15.1 from rebuilt middleware and WebUI packages.
  • Explicit realnas-bootstrap idempotency and package reinstall/bootstrap behavior.
  • Native py-netif and TDB package validation.
  • Architecture-contract checks for state ownership, direct writers, privileged command owners, plugin surface, stub budget, and dependency policy.
  • UI render, API shape checks, storage import, native encrypted import, legacy GELI import, rollback convergence, network convergence, service lifecycle, SMB groupmap, and security surface checks.
Boundary

If a change needs to mutate OS-owned state, it should go through FreeBSD's tool, a native binding, a documented generated-config path, or a narrow adapter with rollback/convergence tests.