Once the binary is installed, oximail setup brings the
server up: it generates the config, the DKIM key, the first organization, and the
admin account, writes a systemd unit, starts the service, and verifies it. The
wizard is the supported path for a first boot. Everything it does can also be
run as individual subcommands for headless deployments.
This page documents what the wizard in OxiMail v0.30.0 actually does, step by
step, in the order it runs. Where a step is conditional or optional, that is
called out. Steps the wizard does not perform are listed at the end.
Running the wizard
By default the wizard reads and writes the server config at
/etc/oximail/oximail.toml. It needs to bind privileged ports and write under
/etc/oximail and /var/lib/oximail, so run it as root.
Flags:
| Flag | Effect |
|---|
--config <path> | Server TOML path. Default /etc/oximail/oximail.toml. |
--config-file <path> | Run non-interactive from a setup TOML file (no prompts). |
--dry-run | Print what would be done and the generated config, without writing anything or starting the service. |
If /etc/oximail/oximail.toml already exists, the wizard warns and asks whether
to overwrite. Declining cancels setup and leaves the existing config untouched.
Interactive flow (primary mail server)
The wizard asks a small set of questions, then runs the steps below in order.
Questions asked up front
- Primary email domain (for example
oximail.ch) — the domain part of your
addresses.
- Mail hostname (defaults to
mail.<domain>).
- Deploy mode:
Primary mail server or Backup MX. (Backup MX has its own
flow, described later.)
- Deployment topology:
Direct (OxiMail handles TLS on port 443) or
Behind reverse proxy (Caddy, Nginx, and so on). Choosing the proxy option
also asks for the local HTTP port OxiMail should listen on (default 8080).
Step: port preflight check
The wizard tries to bind each required port locally and reports OK or FAIL
with the reason (permission denied, already in use, cannot bind). The required
ports are:
| Port | Purpose |
|---|
| 25 | SMTP inbound (receive mail) |
| 80 | HTTP (ACME certificate challenges) |
| 443 | HTTPS / JMAP |
| 465 | SMTPS (encrypted submission) |
| 587 | SMTP submission (send mail) |
| 993 | IMAPS (read mail) |
When you chose “Behind reverse proxy”, ports 80 and 443 are skipped (the proxy
owns them). Blocked ports do not stop the wizard, but the server may not work
until they are opened in your firewall.
Step: data directories
The wizard creates /var/lib/oximail, /var/lib/oximail/blobs,
/etc/oximail, /etc/oximail/dkim, and /etc/oximail/tls. There is no
/var/log/oximail: logs go to stdout and are captured by journald.
Step: DNS
You are asked for a Cloudflare API token (leave empty to configure DNS
manually). The wizard auto-detects your public IP (asking you to type it if
detection fails), then checks the domain’s DNS records and prints a pass/fail
report.
- With a token, it can create the missing records via the Cloudflare API:
A, MX (priority 10), SPF, DMARC, MTA-STS (TXT + CNAME), TLS-RPT, autoconfig and
autodiscover CNAMEs, a CAA record restricting issuance to Let’s Encrypt, and
SRV records for client autodiscovery (
_autodiscover, _imaps,
_submission, _caldavs, _carddavs). If a conflicting MX record exists it
asks before replacing it. It then re-checks DNS.
- Without a token, it prints the check results and tells you to create the
records yourself.
Step: DKIM
The wizard generates an RSA DKIM keypair (selector default) at
/etc/oximail/dkim/<domain>.default.key and prints the DNS record to publish.
If a Cloudflare token was supplied, it also publishes the DKIM TXT record at
default._domainkey.<domain> (deleting any stale record for that selector
first). The published key is in SPKI format (v=DKIM1; k=rsa; p=...).
The Cloudflare DKIM publish only happens inside the full wizard. Domains added
later, or setups where no Cloudflare token was provided, have no DKIM TXT
record published automatically — generate and publish those manually (see
DKIM key generation below).
Step: configuration file
You are asked for the admin email (defaults to admin@<domain>) and, for a
direct deployment, the ACME (Let’s Encrypt) email (defaults to the admin
email). Behind a reverse proxy, ACME is skipped because the proxy handles TLS.
The wizard writes /etc/oximail/oximail.toml for a primary server. Key contents:
[server] hostname, base URL, bind addresses (direct: 0.0.0.0:443 /
0.0.0.0:80; proxy: 127.0.0.1:<port> with trusted_proxies).
[storage] SQLite at /var/lib/oximail/data.db, blobs at
/var/lib/oximail/blobs, encrypted = true.
[auth] default_tenant = "default".
[mode] role = "primary".
[smtp] bind ports plus a [[smtp.dkim_keys]] block for the domain/selector.
[tls] with ACME on (direct) or off (proxy).
[legacy] IMAP/CalDAV/CardDAV enabled.
[spam], [greylisting], [security] (fail2ban + trusted IPs),
[rate_limit], [network] contribute, and [logging] (JSON to journald).
For a reverse-proxy deployment the wizard prints a ready-to-use Caddyfile
snippet and notes that SMTP (25, 587, 465) and IMAP (993) bind directly and are
not proxied. To tune any of these values afterwards, see
Configuration.
Step: systemd unit
The wizard writes /etc/systemd/system/oximail.service. The unit runs
oximail serve --config /etc/oximail/oximail.toml, restarts on failure, raises
the file-descriptor limit, and sends output to journald under the oximail
identifier. If it cannot write the file (not root), it prints the unit content
so you can install it manually. See Installation for the unit
details.
Step: organization + admin account
The wizard creates the first organization (a tenant in the storage model: id
default, name Default, domain derived from the admin email) and then the
admin account. Creation is idempotent: if it already exists the wizard reports
so and continues.
You are prompted for the admin password, with confirmation. The password
must be at least 8 characters and include an uppercase letter, a lowercase
letter, and a digit; the prompt loops until it is strong enough. The account is
created with the admin role and language fr.
Until an organization exists, the server starts in web-wizard mode (“no
tenants”). The CLI wizard always creates the default tenant before the admin account, so a
completed oximail setup run leaves you ready to log in.
Step: start the service
The wizard runs systemctl daemon-reload, systemctl enable oximail, and
systemctl start oximail, then polls port 443 for up to 90 seconds while the
ACME certificate is provisioned. It prints whether the server became ready, and
if not, points you at journalctl -u oximail -f.
Step: verification
It connects to ports 25, 587, 993, and 443, checks the JMAP endpoint at
https://<hostname>/.well-known/jmap (a 401 counts as up), and confirms the
DKIM directory and config file exist.
On success the wizard prints the login URL
(https://<hostname>/auth/login), the config path, and the log command.
Step: optional data import
Finally the wizard offers to import existing mail. Choices: Skip, From
IMAP server (Dovecot, Exchange, Gmail, any IMAP4 server), From Stalwart
(JMAP API), or From mbox/Maildir files. Each choice prompts for the
relevant connection details. This step runs after the server is already
configured and running, so a failed import does not break the install. You can
retry any time with oximail migrate. See Migration for
the standalone import workflow.
Backup MX flow
If you choose Backup MX as the deploy mode, the wizard runs a dedicated
flow: it asks for the primary MX hostname (defaults to mail.<domain>),
additional relay domains, and the forwarding mode (queue to accept locally and
retry to the primary, with DSN bounces on permanent reject; or proxy to relay
in real time and propagate the primary’s reply, falling back to queue if the
primary is unreachable). It then creates the admin account, generates DKIM,
writes a role = "backup" config, installs the systemd unit, optionally creates
backup DNS records (MX priority 20, A, DKIM, and IMAP/submission SRV records via
Cloudflare), and verifies port 25 and the config.
Non-interactive setup
For headless or automated installs, write a setup TOML file and pass it with
--config-file:
oximail setup --config-file setup.toml
The file has a [server] section (domain, hostname, optional
deploy_mode = "primary" or "backup", topology = "direct" or "proxy",
proxy_port), plus optional [dns] (Cloudflare token, replace_existing_mx),
[tls] (acme_email), [admin] (email plus password or password_file),
[import] (source = "imap" | "jmap" | "mbox" | "skip" with the matching
connection fields), and, for backup mode, [backup] (primary_hostname,
accepted_domains, mode, config_output_path).
The same steps run without prompts. If there is no [admin] section, the wizard
skips admin creation and tells you to create one with oximail account create.
Passwords passed non-interactively are still strength-checked, and a weak one
aborts the run.
Use --dry-run with any of these to preview the generated config and actions
without writing or starting anything.
Individual setup subcommands
Every wizard step is also a standalone subcommand under oximail setup, useful
for re-running one piece:
oximail setup dns --domain <d> --hostname <h> [--cloudflare-api-token <t>] [--replace-mx]
oximail setup dkim --domain <d> [--selector default]
oximail setup admin --email <e> [--password <p> | --password-file <f>] [--tenant-id default] [--config <path>]
oximail setup verify [--hostname <h>] [--config <path>]
All accept --dry-run.
DKIM key generation
There is also a top-level DKIM command (separate from oximail setup dkim),
which is the one referenced in the Quick Start:
oximail setup-dkim --domain <domain> --selector <selector> --config <path>
| Flag | Default | Notes |
|---|
--domain | (required) | Domain to sign for, for example oximail.ch. |
--selector | (required) | DKIM selector, for example default. |
--algorithm | rsa | Only rsa (RSA-2048) is accepted at runtime. ed25519 is rejected because the runtime signer loads RSA keys only. |
--config | /etc/oximail/oximail.toml | Used to determine the key storage directory. |
This generates the keypair under /etc/oximail/dkim/ and prints the DKIM DNS
record to publish. It does not publish the record to Cloudflare; only the
full wizard does that, and only when a token is supplied. Publish the printed
v=DKIM1; k=rsa; p=... value at <selector>._domainkey.<domain> yourself.
Starting and verifying manually
If you skipped the wizard’s start step, or installed the unit by hand:
systemctl daemon-reload
systemctl enable --now oximail
systemctl status oximail
journalctl -u oximail -f
Re-run the wizard’s checks at any time:
oximail setup verify --hostname mail.<domain>
What the wizard does not do
- It does not install the binary. Get
oximail onto the host first; see
Installation.
- It does not publish DNS without Cloudflare. With no Cloudflare token it
prints the records (or check results) for you to add at your provider.
- It does not publish DKIM outside the full wizard. The standalone
setup-dkim and setup dkim commands generate and print the record but do not
push it to DNS.
- It does not configure ed25519 DKIM. Only RSA-2048 keys are generated and
loaded at runtime.
- It does not manage your firewall. The port preflight only reports blocked
ports; opening them in the VPS firewall or security group is manual.
- It does not create extra organizations or accounts. It provisions exactly
one
default tenant and one admin account. Add more with oximail tenant create
and oximail account create.
Next steps