ImageMaker 05
Reply “quote” to any message and get a designed image back in about a second — and the same service quietly renders for a second product.
People quote each other with screenshots. This replaces that with one word.
In group chats people quote each other with screenshots — ugly, unreadable, and impossible to share outside the app. ImageMaker replaces that with one word: reply “quote” to any message and the bot returns a typeset image with the text, the author’s avatar, their name and the date on a matching background.
The decision that doubled its value. Instead of leaving it a standalone toy, I turned it into the shared rendering engine for the company’s other product. The same service now answers its own audience and three environments of a different bot on another physical server — one codebase, one team, two product lines.
In production since December 2022, 118 thousand new users in the last year alone, and the entire hosting bill is twelve dollars a month. That number is not a brag about frugality; it is the reason half the engineering below exists.
Typography by measurement, and 586 MB of render on a 1.9 GB box
Dual-mode runtime, binary-searched type sizing, cascading LLM fallback, every memory kill contained in its own cgroup.
Dual-mode runtime
Asymmetric queue federation across two servers
Typography by measurement, not heuristic
586 MB of peak render on a 1.9 GB box
MALLOC_ARENA_MAX=2 in the systemd drop-in) because RMagick holds memory outside Ruby’s GC; disciplined image destruction in every ensure block; rendering moved out of the web process into Sidekiq, after which the web process stopped being killed at all — and every kill elsewhere stays inside its own cgroup.Cascading LLM fallback
Formatting survives · author identity
Who worked on it · 5 contributors
Danyil Shkoropad
- The dual-mode runtime, and the decision to make it the shared render engine for a second product.
- Drove peak render to 586 MB on a 1.9 GB box, and the web process stopped being killed at all.
Vladyslav Fomenko
- Moved the quote rendering into this service and built the API endpoint the second product calls.
- Part of the type measurement and the text formatting the rendered image depends on.
Oleksandr Shemberko
- The
/qxcommand — ready-made author quotes, which is the half of the service the second product calls. - Per-user quote settings, AI-picked backgrounds and the admin
/statcommand.
Mykhail Yun
- The conversation that turned a standalone toy into the shared render engine for a second product.
- Positioning and the requirements for what a quote card has to contain.
Claude
- Reviews the render path, where a leak shows up as an OOM two days later.
- Checks every image object is destroyed in an ensure block.
Read to the end and want to talk it over?



