Skip to content
  • Categories
  • Recent
  • Popular
  • Tags
  • Users
  • Groups
  • Documentation
  • Downloads
  • Report a bug
  • GitHub mirror
  • Paid support
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
hMailServer Community Forum
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ
  1. Support and discussion for hMailServer
  2. Running hMailServer
  3. Guides & tutorials
  4. Quotas, retention and the archive: how much mail the server keeps, and for how long

Quotas, retention and the archive: how much mail the server keeps, and for how long

Scheduled Pinned Locked Moved Guides & tutorials
administrationsmtpimapwebmailrest-api
1 Posts 1 Posters 117 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ProgressiverobotP
    ProgressiverobotP
    Progressiverobot
    hMailServer Team
    wrote on last edited by chris
    #1

    This topic covers how much mail an hMailServer 6.3.4 server keeps, and for how long: each mailbox's limit and the quota policy 6.3.4 adds around it; retention, the administrator's and each person's, with the recover-deleted window; and the server archive under ArchiveDir. The one thing to know first: out of the box nothing is deleted because of its age. A new account has no limit (0), no domain has a retention policy, the archive is off, and once it is on, ArchiveRetentionDays is 0, keep for ever.

    The limit, and what a full mailbox does

    Where it is set. Account.MaxSize over COM, max_size_mb over REST, in megabytes; 0, the default, is no limit. Set it on the Control Panel's account dialog (Quota (MB), Windows only), in the Control Deck's account editor, with PUT /api/v1/accounts/{address}, in the max_size_mb column of an accounts CSV file (since 6.3.4), or with Set-HmAccount. A domain's Limits tab caps the whole domain (Maximum domain size (MB), 0 for none; once reached, delivery to its mailboxes fails) and sets the quota new accounts get.

    What counts is each message's original size: compression does not lower it, and a mailbox encrypted at rest keeps its sizes in the clear. The kept copy of a message the person sent to expire counts against their quota (413, quota_exceeded).

    Where At the limit
    SMTP RCPT TO, with RejectFullMailboxAtRcpt on (1, the default) A mailbox already at or over its limit, and past any grace (below), is answered 452 4.2.2 Mailbox is full., a temporary refusal: the sending server keeps the message and retries
    A message that arrives while there is room and would overflow it Caught at delivery, with a non-delivery report to the envelope sender
    RejectFullMailboxAtRcpt at 0 Accepted, then bounced to an envelope sender that spam has usually forged. Leave it on
    IMAP APPEND, COPY, MOVE Refused with [OVERQUOTA], so a client can tell a full mailbox from any other refusal
    APPEND into a folder shared from another mailbox Since 6.3.4 charged to the folder's owner, as COPY and MOVE were
    A migration or PST import job Pauses with the account has no room left under its quota and tries again; raise the limit or make room

    A full disk is a different refusal. Below MinimumFreeDiskSpaceMB (100) free on the store's volume, MAIL FROM gets 452 4.3.1 Insufficient system storage. and IMAP APPEND NO [UNAVAILABLE], for every mailbox. The log warns first, below DiskSpaceWarningThresholdMB (1024).

    What the person sees.

    • The webmail's Storage page (since 6.3.2, GET /api/v1/me/storage๐Ÿ˜ž the share used, every folder by size, the twenty largest messages, and a clean-up of Trash and Junk older than 7, 30 or 90 days, or everything. See The webmail portal at /portal.
    • GET /api/v1/me answers quota with limit_mb and used_bytes.
    • IMAP QUOTA: QUOTA=RES-STORAGE is advertised; GETQUOTA and GETQUOTAROOT answer one unnamed root for the whole account with STORAGE in kilobytes, and an empty list for an account with no limit. SETQUOTA is refused: the limit is the administrator's. It is on as seeded; the switch is imap_quota_enabled on PUT /api/v1/settings (IMAPQuotaEnabled over COM).

    The quota policy (since 6.3.4)

    The server-wide warning at QuotaWarningPercent (90) has been there since 6.2.22-pre6. Schema 6061 adds a domain's own warning share and words, a grace and a send-stop, each a share of the limit; schema 6088 lets an account hold its own shares too.

    Point Server Domain (-1 takes the server's) What it does
    Warning QuotaWarningPercent, 90; 0 warns nobody quota_warning_percent The delivery that takes the mailbox across this share sends its owner one notice, from the domain's postmaster
    Grace quota_grace_percent, 0 quota_grace_percent How far past the limit mail from other people is still accepted
    Send-stop quota_send_stop_percent, 0 (never) quota_send_stop_percent The share at which the account can no longer send

    Over COM (Windows): Settings.QuotaGracePercent, Settings.QuotaSendStopPercent, and Domain properties of the same names.

    The warning is sent once, by the delivery that crosses the threshold; nothing records who was warned, so a mailbox emptied and filled again is warned again.

    The grace is for mail other people send, the mail otherwise lost. A 1 GB mailbox with a 50% grace receives until it holds 1.5 GB; the owner's own saves and copies over IMAP and the webmail stop at the limit.

    The send-stop refuses the account, not the address. An authenticated MAIL FROM is refused with 552 5.2.2 and the reason, keyed on the account that signed in, even under a domain alias, not the address it named. Receiving goes on. QuotaPolicy.md describes the refusal at SMTP only; test the webmail's send and the send API before relying on it there.

    The words are the QUOTA_WARNING_SUBJECT and QUOTA_WARNING server messages, with %MACRO_MAILBOX%, %MACRO_QUOTA% (MB), %MACRO_USED% (MB) and %MACRO_PERCENT%. Server messages are server-wide (the Control Panel's Mail flow & delivery โ†’ Server messages, the Deck's Server messages page, PUT /api/v1/settings/messages/{name}). A domain sets its own subject and text, quota_warning_subject and quota_warning_text (Domain.QuotaWarningSubject, Domain.QuotaWarningText), so its people can be warned in their own language. They are chosen apart: a domain that sets only the text keeps the server's subject.

    Levels. The warning is a preference: the deepest level holding a value wins. The grace and the send-stop are limits: an account's own value may tighten its domain's, never loosen it, while a domain's may sit either side of the server's. The domain's shares are the domain administrator's, and a domain-restricted key's. <name>_effective and <name>_source on the domain and account routes say why a mailbox was not warned. An account's own quota shares are tested only through what the routes answer, and only on Windows. The mechanism is in Settings: what is not stock, when a save takes effect, and the three levels.

    Where it is set.

    • Control Deck: the domain editor's Quota policy group; the server's grace and send-stop on the Settings page.
    • Control Panel (Windows): the domain dialog's Limits tab; the server's grace and send-stop under SMTP, beside QuotaWarningPercent.
    • REST: the five domain keys on PUT /api/v1/domains/{name}; the server's two on PUT /api/v1/settings.
    • An account's own value: the Levels tab of the Control Panel's account dialog, the Deck account editor's levels card, hmctl account set, or PUT /api/v1/accounts/{address}.

    QuotaWarningPercent is a key of the settings store: set it in the Control Panel (Windows) on Connections & protocols โ†’ Protocols, SMTP tab, with PUT /api/v1/settings/ini/{name} or hmctl ini set, which answer to the administrator password alone (never an API key, nor an account holding a role), or, on the Linux server itself, hmailserver --set-setting, not by editing hMailServer.ini. Every value here applies when it is saved through the server, at every level; when a save takes effect explains why a line edited in the file does not hold and when a value stored by --set-setting arrives. A backup taken before the policy restores with the server's values.

    One domain warned at 85%, stopped from sending at the limit and given a 20% grace, with a German subject; then one account put back on its domain's grace:

    hmctl domain set example.com quota_warning_percent 85
    hmctl domain set example.com quota_send_stop_percent 100
    hmctl domain set example.com quota_grace_percent 20
    hmctl domain update example.com --set "quota_warning_subject=Ihr Postfach ist fast voll"
    hmctl account set [email protected] quota_grace_percent inherit
    

    Who is nearest their limit

    GET /api/v1/reports/quota lists every mailbox with a limit and at least one message, nearest first: megabytes, quota_megabytes, percent, the warns_at and sending_stops_at points that apply to it, and a state of ok, warned, stopped or over. It takes domain, top (1 to 200, default 10) and format=csv, and ignores the date window. Its test has run on the Windows bench only. The Deck's Reports page shows it as Near their quota. Other than the administrator password, a credential needs view_configuration and view_message_trace. The sections hmctl report documents do not include it:

    curl -u Administrator:your-admin-password \
      "http://127.0.0.1:8443/api/v1/reports/quota?domain=example.com&top=200&format=csv"
    

    Nothing records what a mailbox held yesterday; per-domain storage by day is the storage section, in Alerts, the audit trail, syslog and the journal, and per-domain reports.

    Retention: the administrator's policy

    MessageRetentionDays (since 6.2.25) is set per domain and per account; there is no server value. On a domain, 0 (the default) is no policy and a number deletes mail older than that many days. On an account, 0 (the default) takes the domain's, a number is its own, and -1 keeps for ever whatever the domain says.

    The sweep. MailboxRetentionTask runs at start-up and every six hours. It removes delivered messages whose stored creation time is older than the window, in every folder of the mailbox, through the same deletion an IMAP EXPUNGE performs, so the quota, the QRESYNC record and an open IMAP session all see it. Utilities.RunMessageRetention (COM, Windows only) runs it now and returns the count; no document names a REST route or hmctl verb for it, so on Linux wait for the next pass.

    Where it is set. On Windows the Control Panel's domain dialog (Limits tab) and account dialog (General tab); the Deck's domain and account editors; message_retention_days on the domain and account routes; hmctl domain set and hmctl account set. A domain's retention is the server administrator's (COM refuses a domain administrator, the domain route a restricted key); an account's own value is the domain administrator's, so an account's -1 can switch its domain's policy off for that account.

    Before switching a domain's policy on:

    1. Give -1 to each account that must keep everything.
    2. Decide whether what the sweep removes should be recoverable for a while (recover_deleted_days, below). Off, a removed message is gone.
    3. Set the domain's days. It applies when saved; the next pass, at most six hours away, deletes.

    Easy to miss. A restore of the built-in backup does not carry a domain's retention days: the wiki's Backup and Restore page lists them as silently reset to 0, so re-enter them. And a legal hold outranks retention: a held account is left alone by the policy and by the person's own choices, and the log says so.

    Each person's own retention (since 6.3.4)

    Three choices on the webmail's preferences page: Empty Trash after, Empty Junk after and Archive Inbox mail after, each never, 7, 30 or 90 days, or a year, kept as retention_trash_days, retention_junk_days and retention_archive_days. The same six-hourly sweep runs them after the administrator's policy. Trash and Junk (the folders designated \Trash and \Junk, or named as clients name them) are emptied of what is older; Inbox mail older than the choice is copied to the \Archive folder, made as a top-level Archive if there is none, then removed from the Inbox. That is a folder of the person's mailbox, not the server archive. A value that is not a whole number of days from 1 to 3650 is off.

    The recover-deleted window (since 6.3.4)

    recover_deleted_days on PUT /api/v1/settings or the Deck's Settings view (0 to 365; 0, off, by default) keeps a copy of what a person deletes, and of what either retention removes, for the person to put back from the webmail's Recover deleted items. A domain or an account may keep copies for fewer days than the level above, never more. The copies, under <data directory>/Recoverable/<account id>/, count against no quota and are not in the backup, so size the window with the disk in mind; the six-hourly task ends those past the window, and all of them at 0. The person's side is in The webmail in depth: search, offline mail, expiring messages, send-as and recovery.

    The server archive

    With ArchiveDir set, the server writes a copy of every message received by SMTP into a tree of its own, as received and in the clear: encryption at rest does not cover it, and the mailbox retention policy does not reach it. The layout, as the Settings Reference writes it (Windows separators, names lower-cased):

    Message Where the copy goes
    From a local sender ArchiveDir\<senderdomain>\<sendername>\Sent-<file>
    From anybody else ArchiveDir\Inbound\<file>
    For each local recipient ArchiveDir\<recipientdomain>\<recipientname>\<file>, made from the first copy when there is one
    A bounce or report with a null sender The archive's Error folder, with no index row

    The archive's settings:

    Key Default What it does
    ArchiveDir empty (off) The folder the copies go to
    ArchiveDomains empty (every message) Comma-separated: a message is archived only when its local sender or a recipient is in a listed domain, and only the listed domains' copies are made
    ArchiveHardLinks 0 Hard-link instead of copy: a promise that nothing outside the server edits a message file in place
    ArchiveRetentionDays 0 (keep for ever) Deletes archived copies older than this many days

    Its sweep runs at start-up and every twelve hours. It removes only .eml files, follows no symbolic link or junction, is depth-capped, and asks the index first: a held copy stays however old.

    The index (since 6.2.25, schema 6029): a row per copy in hm_archiveindex, with who, when, what, where and whether it is held; copies made before schema 6029 have none. Search it in the Deck's Archive view (since 6.3.4), with GET /api/v1/archive (domain, mailbox, sender, recipient, subject, since, until, hold=1, limit 1 to 1000, default 200, newest first), or with Utilities.SearchArchive over COM (Windows). A role needs view_archive; a key restricted to named domains must name one of its own, and the Inbound copies are the administrator's alone. Searching the words of archived mail is an eDiscovery case's work.

    A hold on one copy, POST /api/v1/archive/{id}/hold or Utilities.SetArchiveHold, keeps its file and row from the sweep and from an address's erasure; DELETE lifts it. Holds on whole accounts and domains are eDiscovery's: see Compliance: eDiscovery, legal holds, personal data, erasure and the audit trail.

    curl -u Administrator:your-admin-password \
      "http://127.0.0.1:8443/api/v1/archive?domain=example.com&[email protected]&since=2026-09-01&limit=50"
    curl -u Administrator:your-admin-password -X POST http://127.0.0.1:8443/api/v1/archive/4471/hold
    

    Where it is set. On Windows, the Control Panel's Maintenance โ†’ Advanced, Copies of mail tab (ArchiveDir, ArchiveHardLinks, ArchiveDomains), and ArchiveRetentionDays on the SMTP tab of Connections & protocols โ†’ Protocols. The Deck has no page for these keys: use PUT /api/v1/settings/ini/{name} or hmctl ini set <name> <value> (the administrator password only), which take effect when saved, or hmailserver --set-setting NAME=VALUE (with sudo) on the Linux server itself, which, while the service runs, waits for the next save, a reinitialise (POST /api/v1/server/reinitialize) or a restart.

    Setting one up:

    1. Choose a folder outside the data directory: a full restore with the messages option replaces that directory, and an archive inside it too, held copies included.
    2. On Windows, a new installation runs the service as NT SERVICE\hMailServer, which writes only where it is granted: approve the folder by running hMailServer.exe /GrantServiceAccess with --approve-modify "<folder>" from an elevated Command Prompt in the installation's Bin folder. It must be a full path on a fixed local drive, outside the program folder, Program Files, the Windows folder and the profiles folder: never a share, a drive's root, or a path through a junction or link.
    3. On Linux the service runs as User=hmailserver, and the packaged unit lets it write only under /var/lib/hmailserver, the data directory itself, and /var/log/hmailserver. Every other folder is read-only to it (/home is not even visible), and an ArchiveDir it cannot write stops archiving. Make the folder writable by the hmailserver user, add it to the unit with a drop-in (sudo systemctl edit hmailserver, then under [Service] a ReadWritePaths= line naming the folder) and restart the service. SecureDefaults.md gives this drop-in for a data folder; no document gives it for the archive.
    4. Set ArchiveDomains first if only some domains are archived, then ArchiveDir.
    5. Send a test message and find its row in the Deck's Archive view.

    Before relying on it.

    • The backup does not carry it. The backup copies the data directory, and hm_archiveindex is not carried: back up the archive folder, and the database with its own tool for the index.
    • Moving it strands the index. hm_archiveindex.archivepath is matched as an exact string by the sweep, holds and erasure; do not rewrite it in bulk. If the folder moves, move it back or accept that older rows point at the old place.
    • A mailbox move leaves the archive under the old address, and an address's erasure with the archive included leaves the copies in other people's trees and in Inbound until ArchiveRetentionDays ages them out.
    • It is not journaling, which sends a copy elsewhere and keeps nothing here: Journaling and dual delivery.
    • SMTP, as documented. No 6.3.4 document says whether mail the server queues itself, such as the webmail's send, or mail a fetch account collects, is archived.

    What it does not do

    • No message-count quota: storage is the only quota enforced.
    • No server-wide retention policy, and no documented way to run the retention sweep on demand on Linux.
    • No write-once archive: a hold is a flag an administrator can lift.
    • The archive only grows while ArchiveRetentionDays is 0, and the recover-deleted store is bounded by its window alone: watch the disk they are on.

    Where to read more

    • Documents at v6.3.4: QuotaPolicy.md, SettingLevels.md, Reports.md, RecoverDeleted.md, Discovery.md, Backup.md, ServiceAccount.md, RelocatingAnInstallation.md, PersonalData.md, Journaling.md, SecureDefaults.md, SettingsApply.md.
    • Wiki: Settings Reference (section 9.13), Your First Domain and Mailbox, Compliance and eDiscovery, Backup and Restore, APIs Reference.
    • The documentation page: Storage, retention and archive, Queue, quarantine and archive.
    • On this forum: Backup and restore, for what a restore resets; Relocating an installation, and moving or renaming a mailbox, for the archive index; the service account on Windows and the Linux unit, for the folders the service may write; The Control Deck.
    1 Reply Last reply
    0

    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

    With your input, this post could be even better ๐Ÿ’—

    Register Login
    Reply
    • Reply as topic
    Log in to reply
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes


    hMailServer Community

    The community support forum for hMailServer โ€” the free, open-source mail server for Windows and Linux โ€” maintained by Progressive Robot Ltd. The software is AGPL-3.0 and stays that way; this is where it is discussed, debugged and documented.

    The software

    • Documentation
    • Downloads
    • hMailServer 6.3
    • Source on GitLab

    Getting help

    • Forum rules
    • About this forum
    • Support plans
    • Pay monthly support
    • Talk to Progressive Robot

    Legal

    • Privacy policy
    • Terms and conditions
    • Cookie policy

    Progressive Robot Ltd ยท 220 Chester Business Park, Heronsway, Chester, Cheshire CH4 9GB, United Kingdom ยท +44 (0)1244 911212 ยท [email protected] ยท VAT 506152326

    Powered by NodeBB

    • Login

    • Don't have an account? Register

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Popular
    • Tags
    • Users
    • Groups
    • Documentation
    • Downloads
    • Report a bug
    • GitHub mirror
    • Paid support