Skip to main content
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

oximail setup
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:
FlagEffect
--config <path>Server TOML path. Default /etc/oximail/oximail.toml.
--config-file <path>Run non-interactive from a setup TOML file (no prompts).
--dry-runPrint 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

  1. Primary email domain (for example oximail.ch) — the domain part of your addresses.
  2. Mail hostname (defaults to mail.<domain>).
  3. Deploy mode: Primary mail server or Backup MX. (Backup MX has its own flow, described later.)
  4. 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:
PortPurpose
25SMTP inbound (receive mail)
80HTTP (ACME certificate challenges)
443HTTPS / JMAP
465SMTPS (encrypted submission)
587SMTP submission (send mail)
993IMAPS (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>
FlagDefaultNotes
--domain(required)Domain to sign for, for example oximail.ch.
--selector(required)DKIM selector, for example default.
--algorithmrsaOnly rsa (RSA-2048) is accepted at runtime. ed25519 is rejected because the runtime signer loads RSA keys only.
--config/etc/oximail/oximail.tomlUsed 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