Miniflux: The RSS Reader That Refuses to Bloat
A 30 MB Go binary, an aggressively closed feature request list, and a better reading habit

Contents
Go and read the closed issues on the Miniflux repository. It is the most instructive thing about the project and it takes ten minutes.
You will find hundreds of reasonable feature requests — a mobile app, a plugin system, folders inside folders, a recommendation engine, a dark theme variant, machine-learning article scoring — closed politely and firmly. Some of them are good ideas. The maintainer’s position, stated over and over across years, is that a feed reader has a job and the job is finished. Adding things to finished software makes it unfinished again.
This is either the most refreshing thing in self-hosted software or deeply annoying, and which one depends entirely on whether the features you want happen to already exist. I have been running it for a while now and I have landed firmly on refreshing, with one caveat I will get to.
Why This Matters More Than the Feature List
Every self-hosted service you run is a small ongoing debt. It needs updates, it holds data you have to back up, it consumes memory, and one day it will break at an inconvenient hour. The cost is real and it is proportional to the software’s surface area.
Miniflux is a single Go binary of roughly 30 MB. It has no runtime dependencies beyond Postgres. It has no JavaScript build step, no node_modules, no plugin loader, no background job queue in a separate container. The entire attack surface is one process that fetches HTTP, parses XML, and serves HTML.
Idle memory on my box is around 40 MB. My previous reader, a well-regarded one with a plugin ecosystem, sat at 400 MB and had a browser-based admin panel that needed its own reverse proxy quirks. Both of them show me articles.
The philosophical point is that a feed reader’s value comes almost entirely from the feeds, and every hour the software spends being interesting is an hour it is competing with the thing it is supposed to be showing you. Miniflux is boring on purpose so that your reading can be the interesting part.
Standing It Up
Two containers. Postgres and the binary.
| |
A few of those need explaining, because the defaults are conservative in ways that will confuse you.
RUN_MIGRATIONS and CREATE_ADMIN are only needed on first boot and are harmless afterwards — the migration is idempotent and admin creation skips if the user exists. Leaving them set means a fresh volume rebuilds itself, which I consider a feature.
POLLING_FREQUENCY is in minutes and it behaves as a floor rather than a fixed schedule. Miniflux respects the caching headers a feed sends and will back off further if the server asks it to. Setting this to 5 because you want news fast mostly earns you rate limits and a reputation as a bad citizen.
BATCH_SIZE controls how many feeds get refreshed per cycle. On a low-power box, a big batch means a CPU spike every hour; a small one spreads the work. Twenty-five is fine for a couple of hundred feeds.
CLEANUP_ARCHIVE_READ_DAYS is the one people miss. Without it your database grows forever. Sixty days is a reasonable retention for a reading habit, and it keeps the Postgres volume in the low hundreds of megabytes rather than the gigabytes.
The metrics endpoint speaks Prometheus, so if you already run a monitoring stack that scales down, you get feed error rates and refresh durations for free. It is worth wiring up — a feed that has been silently 403ing for six weeks is invisible otherwise.
Do not expose the metrics port to the world. METRICS_ALLOWED_NETWORKS is the guard, and it takes CIDR ranges.
The Rules Engine Almost Nobody Uses
Here is the actual reason to run Miniflux rather than a hosted reader, and it is buried in a per-feed settings tab.
Miniflux has three rule systems and they compose. Filtering rules discard entries before they reach you. Rewrite rules transform the content. Scraper rules fetch the full article when a feed only ships a summary.
The filtering rules take a regex against a field. My news feeds run this:
| |
That is a block rule, and it removes about fifteen per cent of the volume of a general news feed without me ever seeing it. There is a corresponding keep rule if you want a firehose reduced to a topic.
The scraper rules use CSS selectors and they are the ones that change your reading life. A feed that gives you two sentences and a “read more” link becomes a full article in your reader:
| |
Miniflux fetches the page, extracts that selector, and stores it. Combined with the rewrite rules — add_dynamic_image, remove(div.newsletter-signup) and friends — you can turn a hostile, pop-up-infested site into clean text. There is a parse_markdown rewriter for the feeds that ship raw markdown, which is more common than you would think.
This is the feature that survives the no-bloat policy because it does the core job better. It also has zero discoverability, which is my one real criticism of the project: the most valuable thing in the software is a text box with no examples next to it.
Troubleshooting
A feed shows an error and no articles. Click the feed, look at the last error string — Miniflux stores it verbatim and it is usually honest. The three common causes are a 403 from a site that blocks non-browser user agents, an expired TLS certificate on some poor soul’s blog, and a feed that is now serving HTML because the site moved.
For the user-agent case, set a custom one per feed in the settings. Miniflux lets you do this precisely so you can stop pretending you are a robot at the sites that care.
Everything stops updating after an upgrade. Check the logs for a migration that did not run:
| |
If you upgraded across several major versions at once, the migration chain may need a manual run with RUN_MIGRATIONS=1 set explicitly. Miniflux is well behaved about this and the error message tells you the truth, which puts it ahead of most software.
The database is enormous. You skipped CLEANUP_ARCHIVE_READ_DAYS. Set it, then reclaim the space, because Postgres will not give it back on its own:
| |
The --full variant takes an exclusive lock and rewrites the table, so do it when you are not reading. A few of the Postgres settings worth tuning matter here if your box is small, though honestly Miniflux’s query load is so light that the defaults will carry you.
Images are broken behind HTTPS. Miniflux proxies media by default only for some cases. MEDIA_PROXY_MODE=all fixes mixed-content warnings at the cost of your server fetching every image, which also has a privacy benefit: the sites you read stop seeing your IP.
OPML import claims success and imports nothing. Some exporters produce nested OPML with categories as attributes rather than outlines. Miniflux is strict. Flatten it and reimport.
What It Genuinely Will Not Do
Being honest about the cost of the no-bloat philosophy.
There is no official mobile app, and there will never be one. What exists is a competent progressive web app plus API compatibility with the Google Reader and Fever protocols, which means the good third-party clients work. That is the right answer architecturally and it is still a worse first-run experience than an app store link.
There are no nested categories. One level. If your mental model of 200 feeds is a tree, you will be irritated on day one and over it by day thirty.
There is no built-in full-text search worth the name. It searches, and it does so with Postgres against titles and content, and at a few thousand entries that is fine. Anyone expecting Meilisearch-grade ranking will be disappointed.
There is no read-it-later, no highlighting, no annotations. There are one-click integrations that shove an article at something that does those jobs — Wallabag, Linkding, a self-hosted push notification service, and about twenty others. This is Unix philosophy applied to a web app, and it means running a second service.
And there is no theming beyond a handful of built-ins. The CSS is served from the binary. You get a text box for custom CSS and that is your lot.
The Part the Software Cannot Fix
Six weeks after I got the rules working I had a reader that was technically perfect and 1,400 unread items, which is the same failure I installed it to escape.
The software has no opinion about this, and that is correct, so here is mine.
Feeds have wildly different rates and treating them identically is the mistake. A personal blog publishes eleven times a year and every post is worth reading. A news site publishes eleven times an hour and roughly none of them are, individually. Mixing those two in one list means the good stuff drowns, every time, and no amount of tagging saves you.
What worked was splitting on cadence. One category for slow feeds — individual people writing on their own sites — which I read completely and which produces maybe five items a day. One category for firehoses, which I mark as read wholesale without guilt and skim only when I want to. The filtering rules do the heavy lifting on the second group; the first group needs no rules at all, because a person publishing eleven times a year is already the filter.
The other thing that helped was a rule I enforce by hand: unsubscribing is free. If a feed has produced nothing I finished in three months, it goes. I do a pass every January and remove about a fifth. The feed list is a garden and the default state of a garden is weeds.
Miniflux supports none of this in software. There is no “importance” score, no smart inbox, no unread-count psychology beyond a number in the corner. You get categories and regexes and your own discipline, which is fewer tools than a hosted reader would give you and, after a year, a smaller pile.
That is the whole argument for the project in one paragraph. Software that refuses to solve your reading problem for you leaves the problem visible, and a visible problem occasionally gets solved.
Verdict
Miniflux is for people who have already decided that RSS is infrastructure. If you want a reader that delights you, that surfaces things, that has a beautiful iPad experience and recommends articles based on what you liked — this is emphatically the wrong software and you will spend six months resenting it.
If you want a process that reliably turns 200 URLs into a list of things to read, costs 40 MB, needs attention twice a year, and gets out of the way, install it this afternoon. The setup is fifteen minutes and the maintenance is a docker compose pull when you remember.
The caveat I promised: the no-bloat policy means the project’s ceiling is set by one person’s judgement about what a feed reader is for. That judgement has been consistently good, and it is still one person’s. If your requirements drift outside it, you have no plugin escape hatch and no negotiating position. Your out is the API and an OPML export, both of which work properly — check that they do before you have 400 feeds, because a data model you can leave is the only real guarantee any of this offers.
I have not needed the escape hatch. Two years of a reader that has never once asked me for anything is a rare thing, and I would rather have the closed issue list than the features in it.




