Naust
Live 25 May 2026 · Go · Next.js · TypeScript
Editor’s note: Since the writing of this article, the mailinabox-fork has been renamed to Naust, and has changed substantially from upstream mailinabox project.
Overview
Naust (repo) began as a redesign of the administration interface for Mail-in-a-Box but evolved into a broader rearchitecture of the entire stack. While it retains the project’s goal of providing a self-hosted mail server, its architecture, management stack, authentication model and deployment options have changed substantially from the upstream project.

Architecture
The naust daemon was designed around least-privilege principles. Installation currently requires root access to install, but normal operation runs under an unprivileged user, with privileged actions delegated via socket calls to a helper daemon. This means that the blast radius of a compromised management daemon is limited to the operations which the helper daemon provides, rather than the entire filesystem.
Naust has also added Passkey support to the management panel, as well as relay support for users operating behind residential IPs or other restricted environments. For newer versions of Ubuntu, encryption at rest is also an available option during install time which users can opt-in to by accessing the admin panel.
Additionally, the components installed during the installation process are designed to be plug-and-play - users may choose to have Roundcube, Cypht, or Rav (Naust’s flagship webmail project, written in Rust), with the option of switching between each during install.
As with the mail daemon, every service written by the installer is sandboxed so that it has restricted access to the filesystem and system resources, limiting the impact of a compromised service.
Design Decisions
The naust management daemon was designed to run without root, but it requires the helperd daemon to execute privileged actions. helperd exposes a fixed set of allowlisted privileged operations rather than allowing arbitrary command execution. Thus, the management daemon cannot run arbitrary commands, or invent commands which helperd does not have explicitly defined.
Naust has also been designed to run in docker, as well as bare metal. This means developers can test and build using docker without deploying a new VPS server, and users can test Naust without committing to a server.
Challenges
Multi-ubuntu-major compatibility: The original mailinabox stack was designed for Ubuntu 22.04, and many pieces of software have changes since.
- Dovecot 2.3 -> 2.4 redesigned the configuration syntax, replaced one-letter %variables with settings variables, and renamed, deprecated or changed many settings.
- Mailcrypt (Dovecot’s encryption-at-rest plugin): older versions of dovecot do not support %passdb, which required backporting 2.4 functionality to 2.3 to keep versions consistent
Modular/composable architecture:
- Each piece of the stack has been designed to be swappable, right down to Dovecot and Postfix, this means that component logic is isolated to its own configuration files and runtime - which makes testing take longer for slow TTL bugs.
- Some pieces of software do not like being run as a subcomponent: Beszel is not designed to delegate admin authentication to another platform, and token workarounds were created to mitigate this problem.
- The original mailinabox implementation assumed root access for the daemon, Naust does not - this means file system permissions, as well as sources of truth (DB reads) have to be properly tuned, and tested on live boxes.
Lessons Learned
As part of Naust’s redesign, I learned the importance of proper regression testing, especially when working on components which can appear to be working while still being subtly broken.
Mail is an especially brutal environment, where small configuration changes can have delayed or non-obvious failures. A service starting successfully does not necessarily mean the system is functioning correctly, and many bugs only become visible after waiting for queues, TTLs, synchronisation, or other slow-moving processes.
This has made live box testing and understanding delayed failure modes an important part of development.
What’s Next
-
More component support, and greater flexibility in the stack
-
Continuing the Go rewrites for the setup installer
-
Moving the installer steps from using stamps to using state reconciliation