Skip to content

Your First Domain and Mailbox

chrisholloway5 edited this page Sep 8, 2026 · 3 revisions

Your First Domain and Mailbox

This page began as a chapter of the 6.2.10 manual and has been corrected against the 6.2.28 source tree. The Control Panel's pages are grouped differently now, so the paths below use today's groups; the TLS ports 465, 993 and 995 exist only after you create them on the TCP/IP ports page (a fresh install seeds 25, 587, 110 and 143); and everything added since 6.2.10 is in Changes-Since-6210. Where a value here disagrees with the Settings Reference, which is generated from the code, the reference is right.

This is the chapter where you get working email.

6.1 Add your domain

Go to Accounts & domains → Domains and click Add domain.

The Control Panel's "Domains & accounts" page: a searchable domain list on the left with example.com selected, its four accounts on the right with Edit and Delete buttons, and Aliases and Distribution lists panels along the bottom

This 6.2.10-era shot predates the current grouped navigation — today the left rail is organised into Monitoring & troubleshooting, Accounts & domains, Mail flow & delivery and so on. The page itself works the same way.

Type the domain name into the box at the bottom of the domain list and press Add domain. Then select it and press Properties to open the domain editor.

The domain editor for example.com, on its General tab: a ticked "Domain enabled" checkbox, the postmaster address, and an optional Active Directory domain with a Browse button

Five tabs in this 6.2.10-era shot. Current builds have seven — Relay and Out of office were added since — and the General tab now also carries the domain name itself, so renaming a domain is done here.

The domain editor, tab by tab

Tab Field Default What it does What happens when it is wrong
General Domain enabled ticked Whether this domain accepts mail at all Unticked: every RCPT TO for the domain is answered 550 Domain has been disabled.
Domain name The part after the @ Changing it renames the domain and moves every account, alias and list with it. Setting it to the host name (mail.example.com) is the classic first-day mistake
Postmaster address empty This is the catch-all. Mail to any address in the domain that resolves to nothing is redirected here Set: no address in the domain can ever be refused, so every typo and every dictionary-attack recipient is accepted and stored. Empty: unknown recipients get 550 Unknown user, which is usually what you want
Active Directory domain empty For AD-synchronised domains Only meaningful with Security Hardening's directory features
Names Alias domain name Domain aliases: yourcompany.net delivering to the accounts of yourcompany.com See Aliases Lists and Public Folders
Limits Maximum domain size (MB) 0 0 = unlimited; otherwise a cap on the whole domain Reached: delivery to the domain's mailboxes fails
Maximum message size (KB) 0 Per-message cap for this domain 0 falls back to the server-wide maxmessagesize, seeded at 20480 KB
Maximum size for accounts created in this domain (MB) 0 The quota new accounts get Only applies at creation; existing accounts keep theirs
Delete messages older than (days) 0 Retention policy for the domain's mailboxes 0 = no policy. An account's own value overrides it
Limit number of accounts / aliases / distribution lists off Tenant caps
Enable plus addressing + character off, + alice+invoices@ delivers to alice@ — see §6.3 Off: alice+invoices@ is an unknown recipient and falls through to the catch-all, or is refused
Enable greylisting for this domain off Per-domain greylisting Needs greylisting enabled server-wide too; see Stopping Spam
Signature Add signature to outgoing messages, method, plain-text and HTML text off Domain-wide signature. Method is use only if the account has none, overwrite the account signature, or append to it
Relay Relay host, port, connection security, credentials empty Where mail from this domain leaves through. Empty = the server-wide relayer applies This is not a route. A route decides where mail addressed to a domain goes, and beats this. See Routes and Relays
Out of office Domain-wide reply, subject, message, separate text for local senders off Answers only for accounts with no vacation message of their own The sender's address is forgeable, so the "local senders" text is a courtesy, never a place for anything confidential
DKIM Enable signing, selector, private key file, canonicalisation, algorithm, key rotation off DKIM signing for this domain, with a guided key rotation Enabling before the DNS TXT record is live makes every message fail DKIM at the receiver. The dialog generates the record and checks it for you

The one that surprises people: Postmaster is the catch-all. In RecipientParser::CheckDeliveryPossibility, an address that matches no account, alias, distribution list or route falls through to pDomain->GetPostmaster() and the resolution restarts with that address. RFC 5321 wants a working postmaster@ mailbox — but that is an account, not this field. Create the account; leave this box empty unless you really do want every misaddressed message.

6.2 Add a mailbox

Select your domain, type the address and a password into the boxes under the account list, and press Create account. Then press Edit to open the full editor.

The account editor for chris@example.com on its General tab: Account enabled, Administration level "Normal user", Quota 5000 MB, first and last name, a "New password (leave empty to keep current)" box, a "Generate strong password" button, and Last logon

Seven tabs in this 6.2.10-era shot; current builds have twelve — Spam, App passwords, Two-factor, Folders and Directory were added since.

Field Value to use Notes
Address you@yourcompany.com Changing it renames the mailbox, and it must stay in this domain
Password Press Generate strong password 16 characters from a cryptographic RNG, guaranteed one lower-case letter, one upper-case, one digit and one symbol, with the ambiguous 0/O and 1/l/I left out. It is shown once and copied to the clipboard (Services/PasswordGenerator.cs)
Administration level Normal user Domain administrator can manage its own domain; Server administrator can manage everything. Give the last one only to yourself
Quota (MB) 0 for unlimited See the warning below
Delete messages older than (days) 0 0 = use the domain's policy; -1 = keep forever
Account enabled ticked Unticked: 550 Account is not active. at RCPT TO, and the account cannot log in

The remaining tabs: Forwarding, Auto-reply (out-of-office, with start and expiry dates), Spam (per-account threshold overrides), Signature, Sieve (Rules and Sieve), External (collect mail from a POP3 or IMAP account elsewhere), App passwords, Two-factor, Rules, Folders (the mailbox's IMAP folders, plus Empty mailbox and Unlock mailbox) and Directory (Active Directory linking).

A full mailbox is refused during the SMTP conversation, not bounced. When RejectFullMailboxAtRcpt is on — it is, by default — a mailbox already at or over its quota causes RCPT TO to be answered 452 4.2.2 Mailbox is full. That is a temporary refusal, so the sending server holds the message and the eventual failure is reported by the machine that knows who really sent it. The alternative, accepting and bouncing, sends a non-delivery report to an envelope sender that spam has almost always forged — backscatter, and a reputation problem earned on somebody else's behalf. (RecipientParser::IsMailboxFull_.)

Create at least these two:

  • postmaster@yourcompany.com — required by the email standards
  • you@yourcompany.com — your actual mailbox

6.3 How an address is resolved

Everything on this page and the next two is one algorithm, and it is worth seeing whole. This is RecipientParser::CheckDeliveryPossibility — the function that answers every RCPT TO — drawn from the code rather than from memory.

flowchart TD
    IN(["RCPT TO alice+invoices@company.net"]) --> LOOP["One iteration. After 25 the answer is: Mail server configuration error. Too many recursive forwards."]
    LOOP --> DA["Apply domain aliases. company.net is an alias of company.com, so the address becomes alice+invoices@company.com"]
    DA --> LOCAL{"Is the domain hosted here?"}
    LOCAL -->|no| ROUTE
    LOCAL -->|yes| SRS{"SRS or BATV switched on, and the address looks like SRS0 or prvs?"}
    SRS -->|signature valid| REWRITE["Rewrite to the address the signature encodes, treat it as local, start again"]
    SRS -->|signature invalid| REJ1["550 Unknown user - a forged bounce"]
    REWRITE --> LOOP
    SRS -->|no| PLUS["Plus addressing, when the domain enables it: drop everything from the plus character on, so alice+invoices@ becomes alice@"]
    PLUS --> ACTIVE{"Is the domain enabled?"}
    ACTIVE -->|no| REJ2["550 Domain has been disabled."]
    ACTIVE -->|yes| ACC{"An ACCOUNT with this address?"}
    ACC -->|yes and enabled| QUOTA{"Already at or over quota, with RejectFullMailboxAtRcpt on?"}
    QUOTA -->|yes| REJ3["452 4.2.2 Mailbox is full - temporary, so the sender retries"]
    QUOTA -->|no| OK(["250 OK - deliver locally"])
    ACC -->|yes but disabled| REJ4["550 Account is not active."]
    ACC -->|no| ALIAS{"An ALIAS with this address?"}
    ALIAS -->|yes and enabled| FOLLOW["Replace the address with the alias target, start again"]
    FOLLOW --> LOOP
    ALIAS -->|yes but disabled| REJ5["550 Alias is not active."]
    ALIAS -->|no| LIST{"A DISTRIBUTION LIST with this address?"}
    LIST -->|yes but disabled| REJ6["550 Distribution list is not active."]
    LIST -->|yes| MAY{"May this sender post to it?"}
    MAY -->|yes| OK
    MAY -->|no, moderator set| MOD(["250 OK - but the posting goes to the moderator, not to the members"])
    MAY -->|no moderator| REJ7["550 with the reason: SMTP authentication required, Not authorized owner, Not authorized domain, or Not authorized sender"]
    LIST -->|no| ROUTE{"A ROUTE whose domain matches, wildcards allowed?"}
    ROUTE -->|yes, address covered| OK
    ROUTE -->|yes, address not covered| REJ8["550 Recipient not in route list. Only when the domain is external or has no postmaster"]
    ROUTE -->|no| CATCH{"Does the local domain have a postmaster address?"}
    CATCH -->|yes| REDIR["Replace the address with the postmaster address, start again"]
    REDIR --> LOOP
    CATCH -->|no, domain is local| REJ9["550 Unknown user"]
    CATCH -->|no, domain is external| RELAY(["Possible. Whether it may actually be relayed is decided next - see 6.4"])
Loading

Four things follow from that picture, and each of them explains a support question:

Question Answer, from the diagram
Why does my alias to a distribution list work, but the list's alias not? Aliases are followed first and re-enter the loop, so an alias can point at a list, an account, or an external address. The chain is bounded at 25 hops
Why did mail to a typo'd address get delivered? The domain has a postmaster address, so the typo fell through to the catch-all rather than being refused
Why does alice+anything@ not work? Plus addressing is off by default and is per domain. Turn it on in the domain's Limits tab. The character is configurable
Why does an alias to an external address still count as local? Only at the first hop. bTreatSecurityAsLocal is set when iteration 1 finds a local domain, precisely so user@local.com → user@external.com is treated as inbound mail rather than as relaying

Domain aliases are applied to the sender address too, once, before the loop — which is why a distribution list in membership or domain members mode recognises a member posting from the alias domain.

6.4 Test it locally, before touching DNS

Prove the server works before you involve the internet.

Why this works without a password

Before the test, understand what makes it succeed, because it is also what stops the rest of the internet doing the same thing. A fresh database seeds exactly two IP ranges (CreateTablesMSSQL.sql:760-762), and the one with the higher priority number wins where they overlap:

My computer Internet
Range 127.0.0.1 only 0.0.0.0255.255.255.255
Priority 30 (wins on loopback) 10
Allow SMTP / POP3 / IMAP ✓ ✓ ✓ ✓ ✓ ✓
Relay local → local
Relay local → external
Relay external → local
Relay external → external
Require auth: local → local no yes
Require auth: local → external no yes
Require auth: external → local no no
Require auth: external → external yes yes
Anti-spam / anti-virus applied
Require TLS when authenticating no no

Read the two bold rows together and the default policy is exactly right: anyone may send you mail (external → local needs no authentication, or nothing could reach you), nobody may relay through you without logging in (external → external requires auth everywhere), and the server itself may send as one of its own domains without a password — which is what makes the test below work, and what makes local scripts and the OnAcceptMessage handler able to inject mail.

The decision is made in SMTPConnection::ProtocolRCPT_ in two steps: first is this direction permitted at all, then does this direction need authentication:

Envelope sender Recipient Bit checked for permission Bit checked for authentication
a domain this server hosts (or a route with treat sender as local) local RELAY_LOCAL_TO_LOCAL SMTP_AUTH_LOCAL_TO_LOCAL
the same external RELAY_LOCAL_TO_REMOTE SMTP_AUTH_LOCAL_TO_EXTERNAL
anything else local RELAY_REMOTE_TO_LOCAL SMTP_AUTH_EXTERNAL_TO_LOCAL
anything else external RELAY_REMOTE_TO_REMOTE SMTP_AUTH_EXTERNAL_TO_EXTERNAL

A refused direction is 550 Delivery is not allowed to this address.; a direction that needs authentication from an unauthenticated session is 530 SMTP authentication is required. Edit any of it on Access & abuse protection → IP ranges.

The test

From PowerShell on the server:

Send-MailMessage -SmtpServer localhost -Port 25 `
   -From "you@yourcompany.com" -To "you@yourcompany.com" `
   -Subject "First test" -Body "If you can read this, hMailServer is working."

Send-MailMessage is deprecated but perfectly good for a one-off test.

If you would rather see the conversation, do it by hand — this is what a correct exchange looks like on a default installation, and every line of it is worth recognising:

telnet localhost 25
220 MAILSRV ESMTP
EHLO localhost
250-MAILSRV
250-SIZE 20971520
250-8BITMIME
250-PIPELINING
250-CHUNKING
250-BINARYMIME
250-SMTPUTF8
250-ENHANCEDSTATUSCODES
250-DSN
250-STARTTLS
250 AUTH LOGIN SCRAM-SHA-256
MAIL FROM:<you@yourcompany.com>
250 2.1.0 OK
RCPT TO:<you@yourcompany.com>
250 2.1.5 OK
DATA
354 OK, send.
Subject: First test

If you can read this, hMailServer is working.
.
250 2.0.0 Queued (0.016 seconds)
QUIT
221 goodbye
Line What it tells you
220 <name> ESMTP The banner. <name> is the machine name unless you set a welcome message on Connections & protocols → Protocols
250-SIZE 20971520 The server-wide maxmessagesize, seeded at 20480 KB, in bytes
250-STARTTLS Present only when the port's connection security is STARTTLS optional or required. Absent on a plain port
250 AUTH … Absent when the port requires TLS, or the connecting range sets Require SSL/TLS when authenticating — the server refuses to advertise a mechanism it will not accept, so the credential is never put on the wire before the refusal
250 2.1.5 OK after RCPT Resolution in §6.3 returned "possible" and the relay and authentication checks passed
250 2.0.0 Queued (0.016 seconds) The message is on disk and in the database. The time is how long acceptance took — spam tests, message modifications, the OnAcceptMessage script and the save — and it is the number to watch when senders report timeouts
250 2.0.0 Queued for delivery Not the same reply. That wording means the message was quarantined: accepted so a false positive stays recoverable, but stored in the review queue rather than delivered. See Stopping Spam

Where it went

Check Monitoring & troubleshooting → Delivery queue (it should be empty — local delivery is immediate) and Monitoring & troubleshooting → Live logs.

The Live logs page streaming an SMTP conversation, with each line tagged by session id and remote address

Then look on disk. A delivered message is one file:

C:\Program Files\hMailServer\Data\yourcompany.com\you\a3\{a3f01e6c-....eml}
                             │    │                │  │   └── the message, headers and all
                             │    │                │  └────── two-character fan-out folder
                             │    │                └───────── the mailbox's local part
                             │    └────────────────────────── the domain
                             └─────────────────────────────── DataFolder from hMailServer.INI

Open it and the first header is the server's own trace line — the same one every spam filter and every Received-chain analysis reads:

Received: from localhost (localhost [127.0.0.1])
	by MAILSRV with ESMTP id 4
	; Mon, 08 Sep 2026 11:04:17 +0100

The letters after ESMTP are the flags this hop records: S for a TLS session, A for an authenticated one, so an authenticated submission over TLS reads with ESMTPSA. When TLS is in use a second line carries the version, cipher and key size.

The whole trip, end to end

sequenceDiagram
    autonumber
    participant C as Your client
    participant L as SMTP listener
    participant R as RecipientParser
    participant DB as Database
    participant D as SMTPDeliveryManager
    participant M as Local mailbox
    C->>L: Connect to port 25
    L-->>C: 220 MAILSRV ESMTP
    C->>L: EHLO, MAIL FROM
    L->>L: Free-space check. Below MinimumFreeDiskSpaceMB it answers 452 4.3.1 right here
    L-->>C: 250 2.1.0 OK
    C->>L: RCPT TO
    L->>R: CheckDeliveryPossibility, the flowchart in 6.3
    R-->>L: Possible, or a 550 / 452 with a reason
    L->>L: Relay and authentication check against the matching IP range
    L-->>C: 250 2.1.5 OK
    C->>L: DATA, then the message, then a lone dot
    L->>L: Spam tests, message modifications, then the OnAcceptMessage script and the save
    L->>DB: Write the message row and its recipients
    L-->>C: 250 2.0.0 Queued
    Note over L,D: Everything above happens while the client waits. Everything below does not.
    D->>DB: Pick the message up from the queue
    D->>M: LocalDelivery: quota check, rules, Sieve, then write the file into the account folder
    D->>DB: Mark delivered, or record the failure and schedule a retry
Loading

Everything above the note happens inside the SMTP conversation, which is why a slow spam test shows up as a sender timeout rather than as a queue problem — see Diagnosing Stalled Mail if that is what you are looking at.

Now connect a mail client (Connecting a Mail Client) and read the message. If it is there, your server works. Everything from here is about the outside world being able to reach it: DNS for Your Domain, then Encryption and Certificates.

6.5 Before you tell anyone the address

A short list, each item with the page that covers it:


Clone this wiki locally