Tech Digest

Feeds

FreshRSS

Multi-user PHP feed aggregator with extensions, scraping and both Fever and Google Reader APIs

FeedsAGPL-3.0Maturearm64 buildsNative OIDC
Last reviewed Profile maintained against the project's own documentation
Operational load
2 / 5
Light
Idle memory
90 MB
typical use ~200 MB
Moving parts
1
containers in a normal deployment
Backup shape
SQLite backup
needs a dump or a stop

A self-hosted RSS and Atom aggregator with per-user accounts, filter rules, HTML scraping for feeds that only publish excerpts, and an extension system. It replaces Feedly, Inoreader and whatever you moved to when Google Reader closed.

Our verdict on FreshRSS#

FreshRSS is the configurable one, and you should pick it when configurability is the point. It runs on SQLite in a single container, or on PostgreSQL 10+, MariaDB 10.6+ or MySQL 8.0+ when you outgrow that; it serves the Google Reader API and the Fever API so essentially every mobile client works; and its extension system covers the things Miniflux will never add, including full-content scraping via CSS selectors and, since 1.29.0 (May 2026), a bundled LLM classification extension for auto-tagging. That flexibility is the cost too: extensions are third-party code running in your PHP process, and they are the usual thing that breaks on upgrade. Releases come every few months rather than monthly, PHP 8.1+ is the floor, and the README's own benchmark is a Raspberry Pi 1 handling 150 feeds and 22,000 articles with sub-second responses. Choose FreshRSS over Miniflux when you want filter rules, per-feed scraping and several users with different reading habits.

Who FreshRSS is for#

Choose it if

  • A household or club where three or four people each want their own account, their own feeds and their own unread counts
  • Anyone following feeds that publish excerpts only, since the scraping and full-content extensions pull the article body in
  • People with shared PHP hosting or an existing LAMP box who do not want to introduce PostgreSQL for a feed reader
  • Readers with heavy filtering needs: mark-as-read rules, per-category sort orders, saved user queries

Look elsewhere if

  • You want a fixed, opinionated reader you will never tune; the settings surface here is large and it invites fiddling
  • You dislike PHP deployments enough that the extension directory, the cron container and the config file feel like liabilities
  • You need a single static binary you can drop on a host with no runtime; that is Miniflux's job

What running it actually looks like#

One container, freshrss/freshrss or ghcr.io/freshrss/freshrss, listening on port 80 internally and usually published on 8080. Mount /var/www/FreshRSS/data for config, the SQLite database and user data, and /var/www/FreshRSS/extensions if you install any. Set CRON_MIN (for example 1,31) or the built-in refresh cron never runs and feeds only update when a page load happens to trigger it, which is the single most common 'FreshRSS is broken' report. Set TZ too, since article timestamps follow it. First run is a web installer that asks for the database choice and creates the admin user. Multi-arch images cover amd64, arm64 and armv7. Upgrades are a pull and a restart; the schema migration runs on first request, and third-party extensions are what occasionally need a version bump afterwards.

Resource profile#

Memory
90 MB idle, around 200 MB in ordinary use. Apache plus PHP-FPM workers; memory is a function of concurrent requests, not feed count. The README notes it runs usably on a Raspberry Pi 1 with 150 feeds and 22k articles, so the floor is genuinely low.
CPU and acceleration
Flat except during the refresh cron, which fetches and parses every due feed. Scraping extensions and full-text retrieval multiply that cost; 500 feeds on a Pi means a visibly busy few minutes each cycle.
Storage growth
Article bodies dominate. Set a retention policy per feed or the database grows without limit; roughly a few hundred megabytes per 100k retained articles with full content, plus a small favicon cache.
Operational load
2 of 5, Light. One container with SQLite is a file copy to back up and a pull to upgrade. It moves to a 3 if you put it on MariaDB or PostgreSQL, or if you install third-party extensions, which are the usual source of breakage after a version bump.

Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.

Data and backup surface#

Back up the data/ directory and, if you use them, extensions/. With the default SQLite backend data/ contains the databases, config.php, user configs and the favicon cache; stop the container or use sqlite3 .backup rather than copying a live file, because FreshRSS writes on every refresh cycle. On MariaDB, MySQL or PostgreSQL take a dump of the database and keep data/config.php alongside it, since the connection settings and salt live there and a restore without them will not authenticate. Favicons and the thumbnail cache regenerate and can be excluded. Your feed list is also exportable as OPML, which is worth doing on a schedule as a format-independent fallback.

Derived backup shape

SQLite backup. sqlite3 <db> ".backup out.db" while running, or stop the container and copy. A live cp of a WAL-mode SQLite file can restore corrupt.

Traps and surprises#

No CRON_MIN means feeds never refresh

The Docker image only starts its internal cron if you pass CRON_MIN. Leave it unset and FreshRSS looks perfectly healthy, logs nothing alarming, and simply stops showing new articles until someone triggers a refresh in the UI. Set CRON_MIN=1,31 or similar, then confirm on the admin page that the last refresh time actually moves.

trusted_sources makes header auth a trust decision

Proxy authentication works by accepting a Remote-User or X-WebAuth-User header from any IP inside the trusted_sources CIDR list. The docs warn directly that FreshRSS will trust any IP configured there, so if your proxy is not locked down an attacker can simply set the header and become any user. Narrow the CIDR to the proxy's exact address and make the proxy strip inbound copies of that header.

HTTP auth auto-creates users

New users authenticating over HTTP or proxy headers are created automatically on first login. On a public-facing instance behind a permissive proxy that quietly becomes open registration. Disable it with the http_auth_auto_register setting unless you are deliberately using your IdP as the source of accounts.

Third-party extensions break across versions

Extensions run as PHP inside FreshRSS and use internal hooks that change. 1.29.0 alone sanitized extension name and version parsing and added a new CSP-amending function and a new entry-state property for extension authors. After every minor upgrade, check that your extensions still load; a broken one can take the whole page down rather than degrading quietly.

Unbounded retention fills the disk

FreshRSS keeps articles until you tell it not to. With full-content scraping enabled on a few dozen busy feeds, the database grows steadily and SQLite in particular gets slow before it gets alarming. Set a global and per-feed article retention policy early, and run the purge from the admin page once to see how much it reclaims.

Specifications#

Category
Feeds, bookmarks and read-it-later
Licence
AGPL-3.0 (Copyleft)
Written in
PHP
First release
2013
Maturity
Mature
Datastore
SQLite by default; PostgreSQL 10+, MariaDB 10.6+ or MySQL 8.0+ supported
Default ports
80
Ships as
docker, php on any web server, yunohost / cPanel package
arm64 builds
Yes
Identity
Native OIDC. Form login, HTTP Basic, reverse-proxy headers (`Remote-User` or `X-WebAuth-User` from a CIDR in `trusted_sources`) and OpenID Connect are all supported in core, with auto-registration of new users on first proxy login.
Replaces
Feedly, Inoreader, NewsBlur, Google Reader
Project site
freshrss.org
Source
github.com/FreshRSS/FreshRSS
Documentation
freshrss.github.io/FreshRSS

Alternatives to FreshRSS#

Everything else in feeds, bookmarks and read-it-later, closest in operational weight first.

ToolOps loadIdle RAMLicence
Karakeep3, Moderate700 MBAGPL-3.0
Miniflux3, Moderate40 MBApache-2.0
linkding1, Set and forget90 MBMIT
wallabag3, Moderate180 MBMIT

Where FreshRSS comes up elsewhere#

Sources#

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