Tech Digest

Head to head

FreshRSS vs Miniflux

The choice is not features. It is whether you want a reader you configure or a reader that refuses to be configured, and what that costs you in databases and upgrades.

Last reviewed 2 tools compared

Should you self-host FreshRSS or Miniflux?

Install Miniflux if you want a feed reader you never think about again, and FreshRSS if you want to shape how feeds behave. Miniflux is a single static Go binary against PostgreSQL with a feature list the author openly refuses to grow; FreshRSS is a PHP app that defaults to SQLite in one container and adds extensions, filter rules and XPath scraping for sites that have no feed at all. Both serve the Google Reader API and the Fever API, so an existing mobile client is not the tiebreak. Miniflux costs you a Postgres backup procedure; FreshRSS costs you a settings screen you will fiddle with.

Both of these read RSS well. Neither is going to disappoint you on the core job, and anyone who tells you one has a materially better reading experience is describing a preference. The decision underneath is about temperament, and it has an operational bill attached.

Miniflux is one static Go binary talking to PostgreSQL. The project publishes a page titled "Opinionated" whose whole purpose is explaining which features will be refused and why. There is no plugin API, no theme marketplace, no second database engine. FreshRSS is a PHP application that defaults to a SQLite file in a single container, ships an extensions directory, per-feed filter rules, XPath scraping and a settings surface deep enough that you will lose an evening to it at least once.

The counterintuitive part: the minimalist one is the heavier thing to operate. Miniflux scores 3 on our ops load scale and FreshRSS scores 2, because "PostgreSQL only, forever" turns a one-container app into two containers, a dump-and-restore procedure and a database major-version upgrade path you eventually have to walk.

What the spec table is actually deciding#

Read three rows and ignore the rest. Datastore is the fork in the road: SQLite-by-default versus Postgres-only is the difference between a file copy and a documented restore order. Container count follows from it. Idle RAM is the row people over-weight, because Miniflux's 40 MB looks decisive until you add the Postgres process sitting beside it, at which point the two land within about 50 MB of each other on a small instance. For context, the median idle footprint across every tool in this catalog is 150 MB, so both of these are at the light end regardless.

SpecificationFreshRSSMiniflux
LicenceAGPL-3.0 (Copyleft)Apache-2.0 (Permissive)
Written inPHPGo
First release20132017
MaturityMatureMature
DatastoreSQLite by default; PostgreSQL 10+, MariaDB 10.6+ or MySQL 8.0+ supportedPostgreSQL only
Services to run12
Idle memory90 MB40 MB
Memory in use200 MB90 MB
Operational load2 / 5, Light3 / 5, Moderate
IdentityNative OIDCNative OIDC
arm64 buildsYesYes
Default ports808080
Backup shapeSQLite backupPostgres dump

Which one works with your existing reader app#

This is what actually decides it for most people, and the honest answer is that it usually does not decide anything, because both sides serve the Google Reader API and the Fever API.

FreshRSS exposes both at https://your.host/api/, gated behind an "Allow API access" toggle in the admin authentication settings plus a per-user API password set in the profile screen, separate from the login password. Its own documentation calls Google Reader "the best option" and describes Fever as limited and less efficient, which is fair.

Miniflux enables each API under Settings then Integrations, where you invent a username and password per protocol. Its Google Reader page names Capy Reader, NetNewsWire, Reeder Classic 5 and later, and RSS Guard as working clients. Its Fever page is blunt about the gaps: JSON only, no links, sparks or kindlings, and Reeder cannot trigger a refresh because it sends no user data.

The uncomfortable line the marketing never includes: Miniflux's own docs say it implements only a subset of the Google Reader API. FreshRSS's implementation is the more complete one, and it is the reason niche clients that break against Miniflux tend to work against FreshRSS. If you already own a paid reader app with an unusual sync implementation, install FreshRSS first and test, because switching servers later means re-syncing every device.

Can either one pull the full article text#

Yes, and the usual claim that this is FreshRSS's exclusive advantage is wrong.

Miniflux has scraper rules in per-feed properties: give it a CSS selector like div#articleBody or article, div.article and it refetches the page and extracts that node. It also has rewrite rules for the practical annoyances, adding image titles, converting Markdown, fixing images that only exist after JavaScript runs, and regex block and keep rules applied globally then per feed.

FreshRSS does the same per-feed selector trick, then goes somewhere Miniflux will not: it manufactures feeds from sites that publish none. The subscription screen lets you pick an HTML plus XPath 1.0 feed type, or a JSON feed, or JSON with dotted paths including string concatenation like meta.title & " some text". You point it at a page, give it XPath expressions for the item container, title, link and body, and you have a feed for a site whose owner never made one.

So the split is:

  • Feed exists but truncates the body: either tool, roughly equal effort.
  • No feed exists at all: FreshRSS, or you go build a separate scraper service.

What the extension system buys, and what it costs#

FreshRSS extensions are PHP loaded into the running application. That gets you things Miniflux has permanently declined: alternative themes, sharing integrations, per-feed content transformations, and as of 1.29.0 in May 2026 a bundled LLM classification extension for auto-tagging incoming articles.

The cost is stated plainly in our profile and worth repeating: extensions are the usual thing that breaks after a minor upgrade. They hook internals that move. The 1.29.0 release alone changed extension name and version parsing, added a CSP-amending function and added a new entry-state property. A broken extension can take the whole page down rather than degrading quietly, so after every bump you load the extensions admin page before you assume the upgrade succeeded.

Miniflux's answer to the same needs is to hand off. One keystroke pushes an article to wallabag, linkding, Pinboard or Shaarli, and there are roughly thirty such integrations. Nothing runs in-process, so nothing in-process can break.

What 400 feeds actually costs#

Neither of these is heavy. FreshRSS's README benchmark is a Raspberry Pi 1 serving 150 feeds and 22,000 articles with sub-second responses, which is a low floor by any standard. Miniflux's process barely registers between polls.

The number that bites at a few hundred feeds is not memory, it is Miniflux's polling arithmetic. The scheduler wakes every POLLING_FREQUENCY (60 minutes by default) and hands BATCH_SIZE feeds (100 by default) to the fetchers. With 400 feeds and stock settings, each feed is checked roughly every four hours no matter what per-feed interval you set in the UI. People blame the feeds. The fix is arithmetic:

yaml
environment:
  LISTEN_ADDR: 0.0.0.0:8080     # default is 127.0.0.1, unreachable in Docker
  RUN_MIGRATIONS: 1
  POLLING_FREQUENCY: 30
  BATCH_SIZE: 200

FreshRSS's equivalent trap is the opposite shape. Its refresh cron fetches and parses every due feed in one pass, so 500 feeds with scraping extensions enabled means a visibly busy few minutes each cycle, and on SQLite with unbounded retention it gets slow before it gets alarming. Set a global and per-feed article retention policy on day one, then run the purge once from the admin page to see what it reclaims.

Backup and restore, which is the real cost difference#

FreshRSS on SQLite: back up data/ and, if you use them, extensions/. Stop the container or use sqlite3 .backup rather than copying a live file, because it writes on every refresh cycle. That is a file copy, and it is why the ops score is a 2.

Miniflux: one pg_dump, and nothing else, because the application keeps no local state at all. Simple, but it is a database procedure with a restore order (database up, restore dump, then start the app with RUN_MIGRATIONS=1), plus an eventual Postgres major-version upgrade that is a dump and reload rather than a container swap. If you are new to that, read Backing up a running database before you commit. Either way, export OPML on a schedule as the format-independent fallback, and test the restore per Backups that actually restore.

The verdict, by situation#

Your situationPickWhy
You want a reader you configure once and forgetMinifluxNo plugin surface means nothing to break on upgrade
You already run PostgreSQL for something elseMinifluxOne more database, not one more stack
Three or four people, separate accounts and feedsFreshRSSMulti-user is first class and per-user settings are deep
You follow sites with no RSS feed at allFreshRSSHTML plus XPath feed types build one for you
You will not run and restore a Postgres databaseFreshRSSSQLite default, one container, backup is a file copy
You own a fussy paid reader appFreshRSSThe more complete Google Reader API implementation
Deploying without Docker on a plain VMMinifluxOfficial deb, rpm and Alpine packages plus a bare binary
Raspberry Pi with 1 GB of RAMFreshRSSNo second service, and the README's own floor is a Pi 1

Fence-sitting answer avoided: if you have no strong opinion and no existing Postgres, install FreshRSS. It is the lower-attention deployment despite being the more configurable product, and you can ignore every setting you do not want.

What to do next#

Whichever you pick, put it behind TLS before you enable the API, because both authenticate over plain HTTP quite happily. Reverse proxy and TLS covers the Caddy config, and if you plan to add OIDC on top, both support it natively; Single sign-on for self-hosters explains what that is worth for a single user. If you are moving off a hosted service, Replace Feedly covers the export and the DNS-level details. To see how a reader fits alongside the rest of your stack, run the numbers in Stack planner or browse the rest of Feeds.

Questions#

Does Miniflux work with Reeder, NetNewsWire and Capy Reader?

Yes. Miniflux exposes a Google Reader API compatible endpoint that its docs name Capy Reader, NetNewsWire, Reeder Classic 5 and later, and RSS Guard as tested clients, plus a Fever endpoint. Both are off by default: you enable each one under Settings then Integrations and set a username and password there that is separate from your login. The docs are honest that only a subset of the Google Reader API is implemented, so a client that leans on obscure tag or stream endpoints can behave oddly where FreshRSS does not.

Can Miniflux use SQLite instead of PostgreSQL?

No, and it never will. PostgreSQL is the only supported database by design, because supporting several multiplies the testing surface the author is willing to carry. That is the single biggest practical difference between the two: FreshRSS runs on a SQLite file in one container, while Miniflux is always two containers and a pg_dump procedure. If you are not prepared to own a Postgres restore, that fact alone decides this comparison.

Why is FreshRSS not showing new articles?

You almost certainly did not set CRON_MIN. The official Docker image only starts its internal refresh cron if that variable is present, so without it FreshRSS looks completely healthy, logs nothing alarming, and quietly stops pulling feeds. Set CRON_MIN=1,31 and TZ, restart, then check the admin page to confirm the last refresh timestamp actually moves. This is the most common FreshRSS support report by a wide margin.

Which one uses less memory with a few hundred feeds?

Miniflux, but not by as much as the binary sizes suggest. The Miniflux process idles around 40 MB, then you add PostgreSQL next to it, which wants 150 MB or more with stock settings. FreshRSS idles around 90 MB and rises to roughly 200 MB with several PHP-FPM workers, but on SQLite there is no second service at all. Feed count barely moves either number; memory tracks concurrent requests and refresh workers, not how many feeds you subscribe to.

Can either reader pull the full article for feeds that only publish excerpts?

Both can, natively. Miniflux has per-feed scraper rules that take a CSS selector such as div#articleBody and refetch the page, plus rewrite rules for fixing lazy-loaded images. FreshRSS has the same idea in its feed settings and goes further with HTML plus XPath 1.0 feed types, so it can manufacture a feed from a page that publishes none. If the site has a feed with excerpts, either tool solves it. If the site has no feed, that is FreshRSS territory.

How do I move from one to the other?

Export OPML from the reader you are leaving and import it into the new one. Both support OPML in and out, so your subscription list transfers cleanly. What does not transfer is read state, stars and per-feed rules, which have no interchange format. Practical approach: mark everything read in the old instance, import the OPML into the new one, run one refresh, and accept that starred items need to be re-saved or pushed to wallabag before you decommission the old install.

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.