mirror of
https://github.com/axllent/mailpit.git
synced 2026-04-26 00:35:51 +03:00
[GH-ISSUE #447] OOM kill with multiple RAM limits #289
Labels
No labels
awaiting feedback
bug
docker
documentation
enhancement
github_actions
invalid
pull-request
question
stale
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/mailpit#289
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @Fertigwaren on GitHub (Feb 20, 2025).
Original GitHub issue: https://github.com/axllent/mailpit/issues/447
Hey,
first of all - we recently swapped from mailhog to mailpit and are pretty happy with it, thank you for your work!
Sadly we are running into an oom kill error.
We deploy mailpit to our k8s cluster while using the official latest mailpit docker image.
The deployment isn't doing anything fancy, simply mounting a persistent storage for the database file and setting up ingress.
We are able to receive simple mails without any issues but as soon as we include some attachments the application crashes with status code 137 (oom kill).
To be a bit more precise: the attachments are mostly images and pdf's ranging from 1mb to 5mb up to 5 attachments per mail.
The mails are getting sent through our laravel backend.
After encountering it the first time i tried to raise the RAM limits for the container.
First from 500mb to 1gb following with a raise to 2gb.
Even with 2gb of RAM the container kept running out of memory when receiving the mails mentioned above.
When idling it pretty much uses no memory at all.
After crashing mailpit is able to display the mail and also knows about the attachments and its sizes but can't display the image or document itself.
Mailpit does also crash when clicking these broken attachments, after that the attachments are usually healthy again which is even more baffling to me.
We use the following environment variables:
Do you guys see what im missing? Im pretty much clueless at this point.
Thanks!
@axllent commented on GitHub (Feb 20, 2025):
@Fertigwaren To be honest I've never encountered an OOM error with Mailpit, and it should easily handle large attachments. I have a database that is around 4GB in size, and contains emails with 30+MB attachments, and memory remains lower than 150MB.
What I will need to check though is if I limit ram, as I'm not sure if there are any sort sudden spikes with large attachments (I need to get back to you on this in the next day or so).
But... I have a suspicion it may relate to your
/datamount. Can you please tell me what that mount is? If it's a network mount (eg: NFS) then we may have the cause (https://mailpit.axllent.org/docs/configuration/email-storage/).@axllent commented on GitHub (Feb 21, 2025):
To follow on from my previous message - I believe you did in fact have hit memory limits (and nothing to do with the database store). To put things into context, Mailpit, unlike a regular mail server, doesn't just store a message, it processes and compresses the message before storing it in a database ~ all of which is far more memory intensive. This is usually very light weight, however as large attachments are introduced, so does the temporary memory requirements to process and store these. Go (which Mailpit is written in) has a built-in garbage collector (to free unused memory), however when multiple messages are received in sequence, the overall memory usage can increase quite significantly until the memory is freed again.
An email containing a single 27MB PDF attachment is approximately 39MB in size (ie: email encoding). This is received by Mailpit's SMTP server, and then:
After further debugging I can conclude that steps 2 & 3 are the main consumers of RAM in this process. Step 2 is what it is and won't change - this makes a significant difference to the size and performance of the database, and ZSTD is one of the least memory-intensive, fast and good compression techniques available (more below). Step 5, however, is currently only needed due to the optional rqlite database connection (storing binary data in the database needs to be manually converted to a SQL query using hexadecimal encoding).
I have found that conditionally doing step 3 (ie: only when rqlite is used) does have an impact on the memory usage, as converting a compressed binary to a hexadecimal string, and then converting that string into a SQL query uses a fair bit of additional RAM.
I am not saying that this completely resolve your issue, but it should help somewhat. I was able to to run three SMTP threads pumping the 40MB message into an instance of Mailpit which was limited to 1.5GB without a crash (300 messages), something I was not able to do prior to my change. It is very difficult to actually gauge the exact difference, but I think it makes an approximate 20% difference. This can of course differ depending on the size of the message, number of attachments, and the type of attachments.
The other thing I have done is to reduce the ZSTD compression from the default setting (3) to the fastest (1), meaning the fastest compression with the least memory. With this setting my test machine barely got over 1GB of ram while processing 300x 40MB messages (10.9GB database size). Using the default ZSTD compression (3) I saw it spike regularly to about 1.2GB with the same test, and which also resulted in approximately the same database size (so the additional compression did not actually make much difference to size).
I am aware my tests are not very scientific, but I am noticing a marked improvement when dealing with large messages like these.
I have just pushed the changes to the develop branch, meaning it is now included in the
axllent/mailpit:edgedocker image. Would you please test that image to see if this helps your OOM issue? You will likely still need 1.5-2GB for your use-case (to be sure), but these changes should help keep it well within those thresholds.@Fertigwaren commented on GitHub (Feb 21, 2025):
Thanks for the insanely detailed answer, really appreciate it.
I did some testing with the edge branch image but it sadly wasn't the magical fix i was hoping for.
Mailpit is not crashing anymore after increasing my limit to about 4GB RAM.
Obviously we won't be running the 4GB on a daily basis but your first comment already gave me another hint.
For some reason, my brain didn't register that our mounted AWS EBS storage is actually network-based...
I'm going to setup a local sqlite db in the same namespace and check the RAM usage afterwards.
I'll probably also check the difference in usage between edge and latest.
Going to update this thread with my findings.
@axllent commented on GitHub (Feb 21, 2025):
That is unfortunate (that it didn't resolve your issue), I was hoping to at least reduce the memory enough while processing large emails given your estimated requirements. I guess it also depends on several factors I mentioned earlier, plus I don't really know know the volume you're sending & processing at the point where it maxes out.
I've been experimenting more to see if there are other optimisations I can make, but alas, I can't find any obvious wins. It definitely revolves around the message parsing, editing (adding/editing headers), and of course compression and storage - however those are all necessary parts of the application.
Networked filesystems are an issue though, especially since Mailpit is currently (hard) configured to use WAL (wite-ahead logging) which improves DB performance a lot, not to mention the other benefits listed on that page. You are not the first person to be caught out by the networked filesystem limitation though, so I am contemplating adding a configuration option to disable WAL. That should technically allow a network filesystem to be used, although I'm not sure that would be a good idea - SQLite isn't an ideal database for that scenario (i/o latency), and the risk is also that users may try open the same database via multiple instances, complain about poor performance, or experience other issues (eg: network dropouts etc) is also a factor.
Let me know your what your find once you have had a chance to test, and then we can discuss more 👍
@axllent commented on GitHub (Feb 23, 2025):
I have been doing some more accurate benchmarking to get a better understanding of the potential affects of the ZSTD compression on RAM, trialling three different scenarios: without compression (at all), fastest compression (level
1), and standard compression (level3). Just ignore the level22tests, I added them later for reference. The results are quite interesting.For the main test I sent 100x 37.5 MB raw emails containing a single 27.4MB PDF attachment to Mailpit to tracking the memory usage. Once complete I also clicked on about 10 different emails to render them in the web UI, meaning Mailpit is then extracting the raw message from the database, plus I also opened the attachment.
In the following results,
level 0means no compression:The interesting points to note here are:
3(default).Please note that the RAM values I provided are almost constant ceiling values (it went up to those values and remained there for the duration of the respective part of each test). Go's internal garbage collector (which automatically frees up unused/unreserved memory) will ultimately free up memory at some point, however that's not your issue - your issue is the RAM ceiling reaching your VPS limits ~ which these tests investigate.
I did another similar test with 2,500 regular/normal emails - all with varying sizes, many with small attachments, but also many with just text & html content. Here the compression definitely made a difference to the database size (compared to no compression):
Note that I didn't bother opening any for this test as they are tiny in comparison, and while I'm sure if I clicked enough it would have a minor impact, the garbage collector would also kick in and would recycle the memory anyway (so it may even go down). This test was trying to test a "normal" use situation and was added for reference.
So with this in mind, I think it's clear I do need to add an option to disable compression entirely for use-cases such as yours. The database size isn't an issue, and it should definitely greatly reduce your memory requirements. This also ties in to #448, although I'm skeptical how much difference this will make to CPU usage because ZSTD compression is fast, and what one is trading off in compression is likely replaced with database CPU, read & writes.
*Edit: Just out of curiosity and for future reference I added level
22(max) compression levels for reference.@Fertigwaren commented on GitHub (Feb 26, 2025):
Hey, I finally found some time for testing and to get back to you.
As you suggested in your first reply, I switched from using persistent network storage for SQLite to an rqlite instance running in the same Kubernetes namespace. I used 35mb of attachments split into 2 and 3 files for my tests.
Since allocating 2GB of RAM to Mailpit, it hasn’t crashed. However, anything below that still results in an OOM kill. I also tested both the latest and edge builds, but the difference in my specific case was minimal.
The edge branch consumed right around 1.6gb of ram during spikes.

In comparison the latest branch spiked at around 1.67gb.

To be fair 2GB is still quite a lot but it's fine in our environment and i wouldn't mind leaving it running like that.
I'd also be down to test out different compression levels if you plan to implement them as a configurable runtime option.
If there is anything else you want me test out i'm more than willing to, otherwise you can close this issue.
Thanks again for your help!
@axllent commented on GitHub (Feb 27, 2025):
Thank you. Based on your feedback I believe the real difference between the two was that SQLite was running remotely, so the part of the RAM being saved was because the database was remote, and part of it the faster compression. It may have been the difference needed to push it over the "memory limit edge".....
I am still doing various tests, but once I am complete there will be two changes:
1) instead of3(as per the current edge build). Compression is enabled by default (as it was already), but the compression gain between levels 1 and 3 isn't significant enough for emails in general but does consume less RAM with1.I'm still considering whether the message compression level will be a user-configurable value (with
0turning it off), or a hardcoded1with a separate option to disable compression - I'm leaning now towards the configurable value as this provides more flexibility, and either way there is the addition of at least one more command flag (I don't like having any unnecessary ones in there).I am also investigating a separate option to disable WAL journaling for local databases (ie: not rqlite). WAL (Write Ahead Logging) greatly improves SQLite performance and crash recovery, but as a trade-off does not work with network filesystems. That's not to say that SQLite is an ideal database for any network filesystem as all read & writes are prone to network latency, however it should technically work I read. mileage may vary, so I'm very hesitant to say "it will work flawlessly with NFS".
@axllent commented on GitHub (Mar 1, 2025):
I have just released v1.23.0 which contains two separate options that relate to your issue.
--compression 0flag or envMP_COMPRESSION=0) - this should definitely resolve your OOM issues as the benchmarks have shown. You will end up with a larger database, however that shouldn't be an issue at all (unless you're planning to store 4 billion emails :-)). Please note that this setting only impacts new messages after you apply the setting, not those already stored in the database.--disable-walflag or envMP_DISABLE_WAL=true) . WAL improves database performance, however also makes SQLite incompatible with network file systems. This option should allow you to use your original NFS mounted volumes for storage. Please note that storing SQLite databases over the network is always prone to latency and other issues (see caveats and considerations).Please let me know how it goes as I'd love to see how it compares to your previous benchmarks.
@Fertigwaren commented on GitHub (Mar 5, 2025):
I finally got around to testing the new options, and they look very promising - especially considering that our setup isn’t ideal to begin with.
Here are the metrics after switching from RQLite to SQLite on network storage with
MP_DISABLE_WAL=true:As you can see, Mailpit still peaks at around 1.6 GB of memory usage. At first glance, the improvement might not seem drastic compared to my previous metrics. However, considering that I initially had to raise my limits to 4 GB just to run this same setup, this is a massive improvement.
Next, I tested with
MP_COMPRESSION=0, and the results were even better:I also experimented with a local Mailpit setup for my personal projects. With a more simplified configuration, it naturally performs much better. But as mentioned, our use case is far from ideal.
I'm insanely happy with the improvements and very thankful for your support!
If you are equally happy with the outcome feel free to close the issue thread :)
@axllent commented on GitHub (Mar 5, 2025):
Thanks for the testing and feedback @Fertigwaren, and I'm really glad to see it has helped your situation. Your memory usage is still higher that I expected (when using no compression), however that could very well come down to database size + NFS (caching), I really don't know. The big thing is that there is no additional compression while reading and writing to the database which ramps up RAM usage with large emails. I am investigating another email parser which could help more, however that's still a very long way off, and that investigation has more to do with speed (+ less CPU) rather than memory.
I'll close this as I am satisfied this has resolved the issue as far as you are concerned 👍