Delivery and store-and-forward

How long can a message wait to be delivered?

A message can wait as long as the shortest limit along its path allows. Some limits are set by the sender, like MQTT's Message Expiry Interval or a DTN bundle's lifetime. Others are set by whoever stores it, for example APNs keeps an undelivered notification for 30 days or less, FCM for at most 28 days, and RFC 5321 says mail servers should generally keep retrying for at least 4 to 5 days.

Learning objectives

After reading this article you will be able to:

  • Explain why the shortest expiry limit on a message's path decides how long it waits
  • Compare the expiry limits documented for MQTT, DTN bundles, APNs, FCM and email
  • Choose a message lifetime from its usefulness, expected gaps and limits on the path

Every holder sets a limit

A message that cannot be delivered right away has to be stored somewhere: on the sending device, on a server, or on a relay in between. Each of those places has finite storage, and each has to decide when a message is no longer worth keeping. So there is rarely one answer to “how long can it wait”. There is a chain of limits, and the shortest one wins.

Limits come from two directions. The sender can say how long the content stays useful, and the holder can say how long it is willing to keep anything. Both are forms of a message TTL, measured in time rather than hops.

Limits the sender sets

Messaging protocols built for unreliable links usually let the sender attach a lifetime.

  • MQTT 5.0 has a Message Expiry Interval, the lifetime of the message in seconds. If it passes before the server has started delivering the message to a subscriber, the server must delete that subscriber’s copy. If the property is absent, the message does not expire. When the server does deliver it, it sends the interval minus the time the message spent waiting, so the remaining lifetime travels on.
  • The Bundle Protocol (RFC 9171), used in delay-tolerant networks, gives every bundle a lifetime in milliseconds after its creation time. When the bundle’s age exceeds it, nodes need no longer keep or forward it, and it should be deleted.

A holder can still cut a lifetime short. RFC 9171 lets a node impose a shorter overriding lifetime on a bundle whose lifetime is so long that keeping it might degrade the node. The bundle’s own lifetime is not changed, but at that node the shorter one applies, and if it runs out the bundle is deleted for the reason “Traffic pared”.

Limits the carrier sets

Services that store messages for many apps publish their own caps.

  • Apple Push Notification service. Apple says that if APNs cannot deliver a notification immediately, it may store it for 30 days or less, depending on the date in the apns-expiration header. A value of 0 means APNs tries once and does not store it. Apple also says APNs stores only one notification per bundle ID for a device, so later notifications can replace earlier ones while the device is away.
  • Firebase Cloud Messaging. On Android and Web, the ttl can be from 0 to 2,419,200 seconds (28 days), and messages without it last up to four weeks. A ttl of 0 means a message that cannot be delivered at once is discarded. Firebase adds that if an Android device has not connected to FCM for more than one month, FCM still accepts messages for it but discards them immediately.
  • Email. RFC 5321 says mail that cannot be sent immediately must be queued and retried. The retry interval should generally be at least 30 minutes, and the give-up time generally needs to be at least 4 to 5 days.
SystemWho sets the limitWhat the source documents
MQTT 5.0SenderMessage Expiry Interval in seconds; none means no expiry
Bundle Protocol v7Sender, with node overridesLifetime in milliseconds after creation
APNsSender, capped by AppleStored for 30 days or less
FCMSender, capped by Google0 to 2,419,200 seconds, default four weeks
SMTPEach mail serverGive-up time generally at least 4 to 5 days

None of these is a promise of delivery. Apple calls APNs a best-effort service, and Firebase says a message ID from FCM means the message was accepted for delivery, not that it was delivered.

Limits on the devices themselves

When there is no server in the path, the message waits on a device: first in the sender’s outbox, and sometimes on relaying devices or at the receiver. These queues also need bounds, both on how many messages they hold and on how long each may stay. A message that is still in the outbox when its time runs out has failed, and the app should say so.

The MQTT standard makes the same point about servers: storage has capacity limits and may be subject to administrative policy, and stored state can be discarded by an administrator or lost through hardware or software failure. A lifetime is a ceiling, not a guarantee that the message survives until then.

As one documented example, the Offline Protocol mesh SDK v0.27.0 keeps up to 500 outbox entries with a 7-day lifetime. Its events reference adds a detail: a direct message parked for a recipient known to be offline is re-sent whenever that peer may be reachable again, and fails only at an absolute cap of four times the configured lifetime, about 28 days on the defaults. The SDK also holds messages it cannot yet decrypt in a pending decryption queue with a 24-hour process TTL. Its configuration docs note that retention bounds must cover the expected disconnection.

Choosing how long to wait

Start from the content and the gaps you expect, not from the largest value a system allows.

  • How long is the message useful? A call invitation or a one-time code goes stale in minutes. A field report or a chat message may still matter days later.
  • How long are devices out of contact? If a recipient can be offline for longer than your expiry, some messages will never be delivered.
  • What is the shortest limit on the path? Your own expiry is irrelevant if a push service or relay drops the message sooner. Check each hop.
  • What happens at expiry? Decide who is told and whether the message can be resent. A delivery that silently never happens is the hardest failure for users to notice.

Frequently asked questions

Can a message wait forever?

Some protocols allow it in principle. MQTT 5.0 says a message without a Message Expiry Interval does not expire. In practice storage has limits, and the MQTT standard itself notes that stored state can be discarded by administrators or lost to failures, so plan for a limit even when the protocol does not set one.

What happens when a message runs out of time?

It is deleted rather than delivered late. Good systems also tell someone. RFC 9171 records the reason "Lifetime expired" when a bundle is deleted, and an app should show the sender that the message was not delivered instead of letting it disappear.

Sources

Build it with Offline Protocol

The configuration page lists the mesh SDK's retention defaults, including the outbox and the pending decryption queue, and explains that they must cover the longest disconnection you expect.

Read the configuration docs