Install it · 4 of 8
A new server
A new inbuxa install from nothing: the server, the console and the webmail in order, ending with a working domain and a first mailbox.
On this page
- Before you start
- The order, and why
- Afterwards
- Trying it before you commit a domain
- With the installer
- Behind your own web server
- Your own web server, by hand
- The server on the host
- Moving the server
- The console and the webmail on the host
- Front ends on another machine
- The mail server's certificate
- Updating
- With the installer
- By hand
- Removing
This is the path when there is nothing to preserve. It ends with a working mail domain, its certificates and a first mailbox.
It is the three installs run in order, so this page is the thread and the component pages are the detail:
- The mail server — comes up in bootstrap mode.
- The console — completes first boot, adds the domain, gets certificates, makes the first mailbox.
- The webmail — where that mailbox is read.
Or let the installer do all three
The inbuxa installer runs the three installs in one go, as containers, and ends in the same place. The pages above are the by-hand path, and the only path for installing straight onto the host until the installer can do that too.
Before you start
You need a Linux machine with Docker, a domain whose DNS you control, and a provider that lets the machine send on port 25.
Point the hostnames at the machine first. They do not have to resolve before you install anything, but certificates cannot be issued until they do.
mail.example.com. A 198.51.100.10
webmail.example.com. A 198.51.100.10
console.example.com. A 198.51.100.10
Decide those names now. The server registers the console's and the webmail's addresses for sign-in, and changing one later means telling the server again.
The order, and why
Each step needs the one before it:
| Step | You end with | Why it has to be here |
|---|---|---|
| Server | Bootstrap mode on port 8080, a one-time admin password |
Nothing else can be set up until the store exists |
| Console | A permanent administrator, your domain, DKIM, certificates, a first mailbox | First boot is completed over JMAP, and only the console does that |
| Webmail | People signing in and reading mail | It needs an account to sign in with |
Afterwards
Keep the permanent administrator's credentials. The temporary bootstrap
admin stops working the moment the wizard finishes.
Publish the DNS records the console gives you. Mail will not be delivered to you, and what you send will be treated as suspicious, until the MX, SPF, DKIM and DMARC records are live.
Put a proxy in front, and tell the server to trust it. Otherwise the automatic IP ban eventually bans your own front door, because every connection appears to come from it.
Then sign in to the webmail at https://webmail.example.com, and to the
console at https://console.example.com.
A new server keeps less than older ones did. Log files, bans, spam samples and delivery history expire, and Pyzor and one address blocklist start off. See Defaults on a new server.
Trying it before you commit a domain
You can run all three bound to loopback, against a domain you never publish, and look at the whole thing before deciding anything. Nothing is exposed and no certificate authority is contacted. The installer does it in one command:
inbuxa install --local --domain example.test --install-deps --yes
By hand, it is the server on 8080, the console pointed at it, and the webmail pointed at both.
The webmail alone, with no server at all, runs against its own built-in mock:
git clone https://git.coffeylabs.org/inbuxa/inbuxa-webmail.git
cd inbuxa-webmail
npm install
npm run dev:mock # demo@example.com / demo
That is the quickest look at the interface, and it needs no mail server, no DNS and no certificates.
With the installer
inbuxa is one program that installs the suite on the machine you run it on.
It is early. Each of the three goes in a container or straight onto the
host, in any mixture, and the console and the webmail can go on a machine of
their own. Run it on its own for a full-screen interface, or with options
for the same thing without one. Either way it shows you the whole
plan before it does anything.
Download it and check it against the release's checksums:
V=v2026.10.15.2 # the newest is at git.coffeylabs.org/inbuxa/inbuxa-installer/releases
curl -fLO https://git.coffeylabs.org/inbuxa/inbuxa-installer/releases/download/$V/inbuxa-linux-amd64
curl -fLO https://git.coffeylabs.org/inbuxa/inbuxa-installer/releases/download/$V/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS
install -m 755 inbuxa-linux-amd64 /usr/local/bin/inbuxa
Use inbuxa-linux-arm64 on an ARM machine. Then run it as root:
sudo inbuxa
The interface takes you through six screens, always in this order: what it
found on this machine, the names, which components go here and how, what
answers on ports 80 and 443, the plan, and the install. Skip the mail server
on the components screen to join one that runs elsewhere instead: a
Server screen then asks for it and for an API key. Choices this machine
can't carry out are greyed out with the reason beside them; something it
lacks but can add, such as Docker, is offered instead of refused. The plan
screen lists every file, port, DNS record and credential it would create,
and the command that does the same without the interface. Nothing happens
until you answer y, and the credentials and addresses it ends on stay in
your terminal afterwards.
The same with options, for scripts or if you prefer:
inbuxa survey # what this machine is
inbuxa install --domain example.com --dry-run # every file, port, record and credential it would create
inbuxa install --domain example.com --yes # do it
It brings the server up in bootstrap mode with a credential that exists only
for that step, completes first boot, starts the console and the webmail, puts
Caddy in front with certificates, and creates the first mailbox. It writes
credentials.txt and dns.zone: publish the records in dns.zone before you
expect mail to arrive.
| Option | Default | What it is for |
|---|---|---|
--domain |
required, unless --local |
The mail domain |
--mail-host |
mail.DOMAIN |
The server's hostname |
--console-host |
admin.DOMAIN |
The console's hostname |
--webmail-host |
webmail.DOMAIN |
The webmail's hostname |
--email |
postmaster@DOMAIN |
The ACME contact |
--server |
container |
container, host to run the server on the host, or skip |
--console, --webmail |
container |
container, host to install it on the host, or skip |
--proxy |
caddy |
caddy, snippets to write configuration for your own web server, or none |
--key |
none | An API key made in the console, needed only to move the server; - reads it from standard input |
--dir |
/var/lib/inbuxa |
Where the installation lives |
--local |
off | Evaluate on loopback: no public ports, no certificates |
--install-deps |
off | Install what this machine lacks (Docker, the Compose plugin, Node) rather than refusing |
--dry-run |
off | Print the plan and stop |
--yes |
off | Skip the confirmation |
inbuxa status says what is installed and whether it is still running as
installed. The installer's README
covers the rest, including describing several machines in one file.
The mail server's certificate, updating and
removing are further down.
Behind your own web server
If the machine already runs nginx or Caddy, --proxy snippets leaves ports 80
and 443 to it. Instead of a proxy of its own, the installer writes
/var/lib/inbuxa/proxy/nginx-inbuxa.conf and
/var/lib/inbuxa/proxy/Caddyfile.fragment: the mail server's names, the
console and the webmail, passed to the ports they listen on locally. On port
80 it passes the mail server's own certificate challenges through to it.
Load one of them yourself. For nginx, include the file, set each
ssl_certificate pair to the certificate you have for those names, then
nginx -t and reload. For Caddy, import the fragment from your Caddyfile
and reload. Caddy gets the certificates itself.
The mail server trusts that web server to tell it each visitor's address and
won't ban it automatically, so one bad visitor can't lock everyone out. Once
the file is loaded, turn on the mail server's own certificates for its mail
ports in the console, under Settings › Overview, Certificates, or
with inbuxa certs.
--proxy none writes nothing and leaves all of it to you, as
below.
Your own web server, by hand
A server installed without the installer, or with --proxy none, goes
behind nginx the same way in three steps. The examples use
mail.example.com for the mail server's name.
1. Take the server off ports 80 and 443. Under
Settings › Network › Listeners (ports), give it a plain HTTP listener
bound to 127.0.0.1:8081 and delete the one on 443, then restart the server.
The console loses it until nginx is in place, so have step 2 ready first.
2. Pass its name to that port. On port 80 only the certificate challenges go through; everything else moves to HTTPS. Push is a long-lived stream, so it isn't buffered or cut off early.
server {
listen 80;
listen [::]:80;
server_name mail.example.com;
location /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location / {
return 308 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name mail.example.com;
ssl_certificate /etc/ssl/inbuxa/mail.example.com.fullchain.pem;
ssl_certificate_key /etc/ssl/inbuxa/mail.example.com.key.pem;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_buffering off;
proxy_read_timeout 1h;
}
}
Keep X-Forwarded-For $remote_addr rather than
$proxy_add_x_forwarded_for: the server is about to trust that header, and
the second form passes on whatever the client put in it. Then nginx -t and
reload. The certificate on 443 is nginx's to get and renew.
3. Tell the server it is behind a proxy. Under
Settings › Network › HTTP › General, turn on
Obtain remote IP from Forwarded header.
Under Settings › Security › Allowed IPs, add 127.0.0.1. Without the
first, every visitor looks like nginx; without the second, one visitor's
failed sign-ins can get nginx banned, and everyone with it.
The mail ports still need the server's own certificate. Turn on automatic certificates under Settings › Security › Certificates; their challenges reach the server through the port 80 block. The console and the webmail each get a block of their own: the console is static files, and the webmail is passed to its port the same way as above.
The server on the host
--server host installs the server without a container: the release binary
for your architecture, checked against a checksum built into the installer,
as /usr/local/bin/inbuxa-server. It gets a system user, inbuxa, its
configuration and data in /var/lib/inbuxa/server, and a systemd unit,
inbuxa-server.service. The console and the webmail go beside it in
containers or on the host too.
The binary needs glibc 2.39 or newer: Debian 13, Ubuntu 24.04, Fedora 40 or Arch. On an older system the installer says so and points you to the container shape instead.
While first boot runs, the server can be reached from this machine and
nowhere else. Afterwards its plain HTTP listens on 127.0.0.1:8081, where
the proxy and the webmail reach it, and port 443 is left to the proxy. The
unit won't start without the server's configuration file, so a server that
has lost it stays down rather than coming back in bootstrap mode.
Moving the server
Run inbuxa install again with the other --server to move the server from
a container to the host, or back. Its configuration and mail come along. The
move needs an API key made in the console, because the server's listeners
and its automatic bans change with the shape; the key is never written down:
echo "$KEY" | sudo inbuxa install --domain example.com --server host --key - --yes
Everything stops for a minute or two. The data is archived into
/var/lib/inbuxa/backups first and then copied, not moved: the old copy, the
container's volumes or /var/lib/inbuxa/server, stays where it was until you
delete it, and a failure on the way puts the server back as it was. The
interface doesn't offer the move yet.
The console and the webmail on the host
--console host installs the console as files: the ones its release
publishes, checked against a checksum built into the installer, in
/var/lib/inbuxa/console, pointed at the server. Nothing runs; the web
server in front serves the directory, so it needs one: the managed Caddy, or
your own, whose configuration the installer writes
to serve the files.
--webmail host installs the webmail from its release the same way, in
/var/lib/inbuxa/webmail, and runs it under a systemd unit,
inbuxa-webmail.service, as its own system user, on 127.0.0.1:8080. It
runs on the installer's own copy of Node 26, in /opt/inbuxa/node, whatever
version the system has; --install-deps fetches it. Its files belong to
root, so it can read them and can't change them.
With all three on the host behind your own web server, no container runtime is installed at all:
sudo inbuxa install --domain example.com --server host --console host --webmail host \
--proxy snippets --install-deps --yes
Front ends on another machine
When the server already runs somewhere else, inbuxa join installs the
console and the webmail on this machine for it. It never logs in to the
server's machine: it talks to the server over its own HTTPS, with an API key
that a server administrator makes in the console.
export INBUXA_JOIN_KEY=... # the API key; kept out of the shell's history this way
inbuxa join https://mail.example.com --dry-run
inbuxa join https://mail.example.com --yes
The interface does the same: run sudo inbuxa, skip the mail server on the
components screen, and give the server's address and the key on the Server
screen. The key is typed without being shown, and the plan screen shows the
inbuxa join command that does the same, with --key -.
It asks the server first, even on a dry run, and stops with the reason if
the key is refused, if the server is too old to take front ends this way, or
if the server already lists a different console or webmail. --replace puts
these in place of those. Then it starts the console and the webmail with
Caddy in front, tells the server their addresses, and checks that the server
lets the console call it. Nothing has to be changed on the server's machine,
and the server doesn't need restarting. The key isn't written down anywhere;
you can delete it in the console afterwards.
The webmail's requests all reach the server from this machine's address, so
join exempts that address from the server's automatic bans. It uses the
address the webmail's hostname points to; pass --exempt-address if the
server sees this machine at another one. The exemption takes effect when the
server next restarts.
join needs a server with inbuxa:FrontEnds, which is in release
2026.10.12.1 and newer. An older server is refused with that reason. Each
front end goes in a container, on the host
(--console host, --webmail host), or nowhere (skip), as with
install; with both on the host behind your own web server,
no container runtime is installed at all. The hostnames default to admin.
and webmail. under the server's domain, and the other options
(--console-host, --webmail-host, --proxy, --dir, --install-deps,
--dry-run, --yes) work as they do for install. A server on the same machine is installed and
extended with install, not join.
The mail server's certificate
The mail server has a certificate of its own, for the mail ports: the one mail clients check. The installer asks for it during the install, and it can't be issued until the mail host points at this machine. If DNS wasn't in place yet, the install finishes without it, and the server doesn't order again by itself once an order has failed.
inbuxa certs
shows the certificate the mail ports present: who issued it, until when,
for which names, whether it verifies, and where the mail host resolves. If
something is wrong, publish the records in dns.zone if you haven't, make
an API key in the console (your account, API keys), and run:
sudo inbuxa certs --key - # paste the key; INBUXA_API_KEY works too
That starts a new order and waits until the mail ports present the new certificate, or says why not. Behind your own web server, it turns the server's certificates on first. The key isn't kept. Once there is a certificate, the server renews it by itself. The console's and the webmail's certificates are the proxy's, which renews those too.
Updating
Each program is released on its own and carries its own version, so check what you are moving between rather than assuming the three move together.
With the installer
Each installer release installs particular releases of the server, the console and the webmail, so a newer installer is what brings newer ones. Download it as above, then:
sudo inbuxa upgrade --dry-run # each component: what it is now, what it would become
sudo inbuxa upgrade # do it
inbuxa status shows the same versions. inbuxa upgrade server (or
console, or webmail) moves just that one.
The server goes first, then the console, then the webmail. Each is checked
once it is running again, and if the check fails it is put back as it was
and the upgrade stops there. The server is stopped first and its data
archived into /var/lib/inbuxa/backups, because a new release may convert
its store on first start and that can't be undone. --no-backup skips the
archive; a store kept elsewhere, such as PostgreSQL or S3, has to be backed up
where it lives either way. A server on the host keeps its previous binary
beside the new one, as /usr/local/bin/inbuxa-server.<version>.
By hand
Without the installer, pull the new image, restart that one program, and check it before touching the next.
Back up before an update that crosses a store format
Data-format upgrades are one-way. The server's --version names the
upstream release it is built on, and a release that changes that base may
convert your store on first start. Take a snapshot you have actually
checked before starting one — for the container install, that is the
/etc/inbuxa and /var/lib/inbuxa volumes.
Removing
sudo inbuxa uninstall --dry-run # what goes and what stays
sudo inbuxa uninstall
uninstall takes away what the installer put on this machine: the
containers, the systemd units, the binaries, and the console's and the
webmail's files. The data stays: the server's data, Caddy's certificates,
the credentials and secrets. Run install again with the same options and
it all comes back as it was.
--purge removes the data as well, except /var/lib/inbuxa/backups, which
you delete yourself once you are sure. Docker, and any configuration your
own web server loads, are left alone either way. To take away one component
and keep the others, run install again with that one set to skip.