Files
ihasmail-oneshot/docs/reference.md
jcoffey-dev 97995fdebb Shorten the README; move the technical detail into docs/
The README keeps what the tool is, how to install it and the first commands,
and points to the guide on docs.ihasmail.org. Everything else moves, whole,
into docs/ and CONTRIBUTING.md, where it is organized for readers who want
the detail. Where the old README disagreed with the code, the code wins.
2026-09-15 12:14:34 -07:00

8.4 KiB

Reference

Everything an operator looks up: requirements, installing, each command and flag, what the deployment directory holds, and running, upgrading, backing up and removing a deployment. For a walk-through, see the guide on docs.ihasmail.org. Back to the README.

Requirements

On the host:

  • Linux, amd64 or arm64.
  • Docker Engine with the compose plugin (docker compose version works), run as a user allowed to use Docker.

For a mail host, additionally:

  • A domain whose DNS you control.
  • Ports 25, 80, 443, 465, 993, 995 and 4190 free on the host and open in any firewall or cloud security group in front of it.
  • Outbound port 25. Many cloud and VPS providers block it by default and unblock it on request. Without it you can receive mail but not send it.
  • Reverse DNS: a PTR record for the host's IP address naming the mail host (mail.example.com). This is set at your hosting provider, not in your DNS zone. Many receiving servers reject mail from an address without one.
  • A static public IP address.

Resources: ihasmail on its own needs 256 MiB of RAM at minimum. Stalwart's needs grow with the mail it stores and the number of people using it; size the host for Stalwart.

Installing

From a release

Download the archive for your architecture and its checksums, verify, and unpack:

ARCH=amd64   # or arm64
curl -fsSLO https://github.com/Coffey-Labs/ihasmail-oneshot/releases/latest/download/ihasmail-oneshot-linux-$ARCH.tar.gz
curl -fsSLO https://github.com/Coffey-Labs/ihasmail-oneshot/releases/latest/download/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS
tar -xzf ihasmail-oneshot-linux-$ARCH.tar.gz
sudo install -m 0755 ihasmail-oneshot /usr/local/bin/
ihasmail-oneshot version

Every release is built by the release workflow from a tagged commit on main, after the tests and a known-vulnerabilities check pass. The archives are reproducible: scripts/build-release.sh builds the same bytes from the same commit.

From source

With Go 1.26.8 or newer:

git clone https://github.com/Coffey-Labs/ihasmail-oneshot.git
cd ihasmail-oneshot
go build -o ihasmail-oneshot ./cmd/ihasmail-oneshot

Commands

deploy

ihasmail-oneshot deploy --domain example.com [flags]
ihasmail-oneshot deploy --local [flags]
Flag Default Meaning
--domain required, or example.test with --local The mail domain this server receives for
--local off The loopback-only shape: no mail ports, no Caddy, no certificates
--mail-host mail.DOMAIN Stalwart's hostname. Must be one label under the domain, e.g. mx.example.com
--webmail-host webmail.DOMAIN The webmail's hostname. Any name except Stalwart's
--email postmaster@DOMAIN ACME contact address for both Caddy and Stalwart
--user NAME none Create a mailbox NAME@DOMAIN with a generated password. Repeat for more
--dir ./PROJECT Deployment directory to write. Must be new or empty
--project ihasmail-DOMAIN (dots as dashes) Compose project name, which prefixes containers, network and volumes
--stalwart-image stalwartlabs/stalwart:v0.16.22 Stalwart image
--ihasmail-image the newest release ihasmail image. By default the tool looks up ihasmail's newest release and writes it into compose.yaml by its dated tag; name an image to use a particular one
--caddy-image caddy:2.11.4 Caddy image
--webmail-bind 127.0.0.1:8080 Host address for ihasmail's own port, for reaching it without Caddy
--stalwart-bind 127.0.0.1:8081 Host address for Stalwart's plain-HTTP port. The tool configures Stalwart through it
--subnet 172.31.253.0/24 The stack's private network. Change it if it overlaps a network you already have
--acme-directory Let's Encrypt ACME directory URL of a private CA, for both Caddy and Stalwart
--acme-ca-root none PEM file of the root the private CA's HTTPS endpoint is signed by
--yes off Don't ask for confirmation. Required without a terminal

Stalwart's and Caddy's defaults are the versions tested together for this release. ihasmail's default is its newest release, looked up when the tool runs and written into compose.yaml by its dated tag.

certs

ihasmail-oneshot certs --dir DIR

Starts a new certificate order in Stalwart for the deployment in DIR and waits for it, typically after fixing DNS. If Stalwart already holds a valid certificate for the mail host, it reports that and does nothing.

destroy

ihasmail-oneshot destroy --dir DIR [--yes]

Removes the deployment in DIR with all of its data. See Removing a deployment.

version

Prints the tool's version.

The deployment directory

The deploy writes a directory named after the domain, ./ihasmail-example-com by default (--dir to choose):

File Mode What it is
compose.yaml 0644 The whole deployment: three services, a private network, named volumes. Commented
Caddyfile 0644 Caddy's configuration: the webmail, Stalwart's web names, and port 80's challenge forwarding
.env 0600 APP_SECRET, which seals ihasmail's session cookies. Read by compose
credentials.txt 0600 The administrator's and each mailbox's generated password, plus what certs needs to reach Stalwart
dns-records.zone 0644 The DNS records to publish
acme-ca-root.pem, ca-bundle.crt 0644 Only with --acme-ca-root: the private CA, and the system roots with it added

Data lives in Docker named volumes, prefixed with the project name:

Volume Holds
…_stalwart-data All mail, accounts, calendars, contacts, files, settings, DKIM keys, Stalwart's certificates
…_stalwart-etc Stalwart's store location file
…_caddy-data Caddy's certificates and ACME account
…_caddy-config Caddy's autosaved configuration

ihasmail has no volume. It runs read-only and keeps sessions in memory.

Running a deployment

The directory is a standard compose project. From inside it:

docker compose ps                      # what's running
docker compose logs -f stalwart        # follow Stalwart's log
docker compose logs caddy | grep -i error
docker compose restart ihasmail        # signs everyone out of the webmail; nothing else is lost
docker compose down                    # stop everything (data stays in the volumes)
docker compose up -d                   # start it again

The containers restart by themselves after a crash or a reboot (restart: unless-stopped).

Upgrading

Nothing upgrades on its own: every image in compose.yaml is a fixed version, ihasmail included. To upgrade, change the image tag there and apply it:

docker compose pull && docker compose up -d
  • ihasmail is safe to move to any newer release that supports your Stalwart version. Its release notes say which. The newest is on ihasmail's releases; its image tag is the version with + written as -, e.g. 2026.9.13-pr344.
  • Stalwart: read its upgrade notes before changing versions. Check that the ihasmail version you run supports the new Stalwart release first, since ihasmail validates against one Stalwart release at a time. Back up stalwart-data first.
  • Caddy minor releases are routine.

Backing up

Everything that can't be recreated is in the stalwart-data volume. For a consistent copy, stop Stalwart briefly:

docker compose stop stalwart
docker run --rm -v ihasmail-example-com_stalwart-data:/data -v "$PWD":/backup alpine \
  tar -czf /backup/stalwart-data-$(date +%F).tar.gz -C /data .
docker compose start stalwart

Keep caddy-data too, or certificates are requested again on a rebuild. That's harmless unless it happens often enough to meet Let's Encrypt's rate limits. Keep the deployment directory itself, which is small and contains your secrets.

Removing a deployment

ihasmail-oneshot destroy --dir ihasmail-example-com

This removes the containers, the network, the volumes with every message and account, and the files the tool wrote. It asks first unless given --yes. The directory goes too, unless it holds files you added yourself; those are left in place, and so is the directory around them.