Self-host FreshRSS on a managed VPS: your RSS feeds on your own server

When you self-host FreshRSS on a managed VPS, you and your team read your RSS feeds from a machine of your own, which our teams keep up to date and monitor day and night. FreshRSS is free software under the AGPL-3.0 license. This guide follows its official documentation: installing with Docker Compose, HTTPS, feed refresh, accounts and login, the API for mobile apps, the database, backups and updates.

Simon Vasconcelos Lee By Published on 11 November 202613 minute read

All guides

In short

  1. The AGPL-3.0 license lets you run FreshRSS freely, at work or anywhere else; if you modify it, you offer the source code of your version to the people who use it.
  2. Install it with Docker Compose: one image, a port published locally and two volumes, data and extensions, behind an HTTPS reverse proxy.
  3. The CRON_MIN variable refreshes feeds on its own: the project recommends about two runs per hour.
  4. Signups are closed by default. For mobile apps, enable the API, then each user sets an API password.
  5. Back up the data volume and, with PostgreSQL or MySQL, the database, then update with docker compose pull.

What FreshRSS does

FreshRSS is a self-hosted RSS feed aggregator written in PHP. Its README describes it as lightweight, easy to work with, powerful and customizable, translated into more than twenty languages. It is a multi-user application, with an anonymous reading mode and custom tags.

FreshRSS is developed by its community on GitHub, and its official website is published by the FreshRSS community. Extensions live in a separate repository, and the documentation covers users, administrators and developers.

What the AGPL-3.0 license allows

FreshRSS is published under the GNU Affero General Public License, version 3: its composer.json manifest declares it as AGPL-3.0, and its README links to the license text. That text affirms an unlimited permission to run the unmodified program. You can therefore use the image published by the project for your company and your team, with no license to buy.

Section 13 adds a condition specific to the AGPL: if you modify the program, your version must offer everyone who uses it remotely over a network a way to get its source code at no charge. A third-party extension added to the extensions volume follows its own license.

Installing with Docker Compose

The project publishes its official images on Docker Hub, as freshrss/freshrss, along with an example Compose file. On a server with Docker installed, create a folder named freshrss and put this compose.yaml in it, drawn from that file and the Docker README:

compose.yaml
volumes:
  data:
  extensions:

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    hostname: freshrss
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
    volumes:
      - data:/var/www/FreshRSS/data
      - extensions:/var/www/FreshRSS/extensions
    ports:
      - 127.0.0.1:8080:80
    environment:
      TZ: UTC
      CRON_MIN: '3,33'
      TRUSTED_PROXY: 172.16.0.1/12 192.168.0.1/16
    healthcheck:
      test: ["CMD", "cli/health.php"]
      timeout: 10s
      start_period: 60s
      start_interval: 11s
      interval: 75s
      retries: 3
Terminal, in the freshrss folder
docker compose up -d

The latest tag, the default, tracks the latest versioned release; edge tracks the rolling release; a tag like 1.30.4 pins a version, and the -alpine suffix gives the same thing on an Alpine base instead of Debian. The updates section comes back to this choice.

Finishing the installation

Once the reverse proxy is in place, open your HTTPS address: the setup wizard asks for the language, checks the server, offers the database choice, then creates the administrator account, with its password and the login method. SQLite works with nothing to prepare. Installation also works from the command line, with cli/do-install.php and cli/create-user.php, run through docker exec --user www-data freshrss.

On resources, the README asks for a light server, running Linux or Windows, and notes that FreshRSS even runs on a Raspberry Pi 1, with response times under a second, with 150 feeds and 22,000 articles. The size depends on the number of users, feeds and articles kept. On a managed VPS, describe your use to us and we will recommend the right size.

HTTPS: the reverse proxy

For production, the Docker README advises a reverse proxy on the host server, providing HTTPS. First choose a domain name or subdomain, such as rss.example.com, pointing to the server with A and AAAA records. The project recommends Traefik and publishes a full Compose file for it, with Let’s Encrypt certificates and the redirect from HTTP to HTTPS; it also gives examples for Apache, Nginx and Caddy.

With Caddy

Caddy gets the certificate and enables HTTPS on its own. This block follows the documentation’s example for a subdomain, pointed here at the port published locally:

Caddyfile
rss.example.com {
  reverse_proxy 127.0.0.1:8080
}

With Nginx and Certbot

This block follows the Docker README’s Nginx example for a domain root, reduced to port 80 so that Certbot can set up HTTPS. The last two lines pass along the Authorization header, which the Google Reader API needs:

Nginx block
upstream freshrss {
  server 127.0.0.1:8080;
  keepalive 64;
}

server {
  listen 80;
  listen [::]:80;
  server_name rss.example.com;

  location / {
    proxy_pass http://freshrss/;
    proxy_redirect off;
    proxy_buffering off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_read_timeout 90;

    proxy_set_header Authorization $http_authorization;
    proxy_pass_header Authorization;
  }
}

Certbot then installs the certificate and takes care of renewing it:

Terminal
sudo certbot --nginx

The public address: base_url

FreshRSS keeps its public address in base_url, in the data/config.php file. The API and WebSub rely on it: check that it holds your https address. The Docker README shows where to find this file in the volume:

Terminal
docker volume inspect freshrss_data
sudo nano /var/lib/docker/volumes/freshrss_data/_data/config.php
docker compose restart freshrss
data/config.php
'base_url' => 'https://rss.example.com/',

Refreshing feeds: CRON_MIN

FreshRSS fetches new articles with its app/actualize_script.php script. The Docker image ships a scheduled job that runs it at the minutes given in CRON_MIN. Without this variable, or with an empty value, the job stays off and feeds no longer refresh on their own.

Another documented method: a scheduled job on the host server, which calls the script inside the container. In that case, remove CRON_MIN from compose.yaml, to keep a single method:

/etc/cron.d/FreshRSS
7,37 * * * * root docker exec --user www-data freshrss php ./app/actualize_script.php > /tmp/FreshRSS.log 2>&1

Since version 1.30.0, FreshRSS refuses local network addresses such as 127.0.0.1 by default, to guard against server-side request forgery. A neighboring service that produces feeds, such as RSS-Bridge, is allowed through the INTERNAL_HOST_ALLOWLIST variable, in the form rss-bridge:80, or in the system configuration. The value * removes all checks, and the documentation calls it unsafe.

Accounts and login

During installation, FreshRSS offers form login, which requires JavaScript. It is the default method, and it suits most instances. The documentation describes others:

MethodWhat it doesWhere it is set
FormA username and password specific to FreshRSSThe setup wizard
HTTP authenticationThe web server controls access, and new users are created on their first loginA p/i/.htaccess file mounted into the container, with its .htpasswd
External authenticationThe proxy passes the logged-in user in the Remote-User or X-WebAuth-User headerTRUSTED_PROXY, which lists the trusted proxies
OpenID ConnectLogin goes through your identity provider, using the Apache module mod_auth_openidcOIDC_ENABLED and the OIDC_* variables, in the Debian image on x86_64

The documentation warns that FreshRSS trusts any address listed in TRUSTED_PROXY: a poorly secured proxy would let an attacker set the header and gain admin access. Here, the port listens only on 127.0.0.1, which reserves it for the server’s own proxy. The documentation also advises against any instance without authentication as soon as the server can be reached from outside your network.

Signups and users

For an instance shared by several people, the documentation advises opening accounts to people you trust and keeping the instance behind a proxy or firewall that blocks access to internal address ranges.

Mobile apps: the Google Reader and Fever APIs

FreshRSS works in the browser, on large and small screens alike, and in apps for Android, iOS, Linux, Windows and macOS. These apps use one of its two APIs: Google Reader, which the README rates as the best, and Fever, with more limited features, less efficient and less safe.

  1. As an administrator, in the Authentication section, turn on the option that allows API access.
  2. Each user sets an API password in their profile. It is separate from the main password, grants access to fewer things, and is meant for less safe situations.
  3. In the app, enter the address https://rss.example.com/api/greader.php, or https://rss.example.com/api/fever.php for Fever, with the username and that API password.

To check, the link shown next to the API password, in the profile, opens a test page: PASS on the full check confirms that everything works. The Docker image already sets Apache to accept encoded slashes, which some apps need; with Nginx in front, keep the Authorization header forwarding.

The README keeps a table of compatible apps, with their platform, API and features: Capy Reader and Readrops on Android, FeedFlow on mobile and desktop, RSS Guard and NewsFlash on desktop, ReadKit and Reeder Classic on iOS and macOS, among others.

SQLite, PostgreSQL, MySQL or MariaDB

FreshRSS has SQLite built in, which the Docker README describes as the easiest option, with good performance. The requirements page recommends PostgreSQL 10 or newer, and also accepts MariaDB 10.6 and MySQL 8.0 or newer. You make the choice in the setup wizard.

DatabaseSetupBackup
SQLiteNone: one file per user, data/users/<user>/db.sqliteThe data volume
PostgreSQLA postgres service in the same compose.yamlpg_dump, or cli/db-backup.php
MySQL or MariaDBYour database server, and its host name in the wizardmysqldump, or cli/db-backup.php

For PostgreSQL, add this service to compose.yaml, drawn from the project’s docker-compose-db.yml file, and the db volume to the list of volumes. The password goes in a .env file, next to compose.yaml:

compose.yaml, under services
  freshrss-db:
    image: postgres:18
    container_name: freshrss-db
    hostname: freshrss-db
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
    volumes:
      - db:/var/lib/postgresql
    environment:
      POSTGRES_DB: freshrss
      POSTGRES_USER: freshrss
      POSTGRES_PASSWORD: ${DB_PASSWORD}
.env
DB_PASSWORD='a-long-unique-password'

In the wizard, choose PostgreSQL and enter freshrss-db as the host, with the user, the password and the freshrss database.

You can revisit this choice later. The documentation describes moving from one database to another, from MySQL to PostgreSQL for example, or to a newer major version of PostgreSQL: cli/db-backup.php copies each user’s database into a SQLite file, then cli/db-restore.php loads it into the new database.

Backups

The README states that FreshRSS comes with no warranty at all: your backups keep your subscriptions, your articles and your settings. Here is what the documentation asks you to back up:

ItemWhat it holdsBackup
data/The configuration, accounts, logs and, with SQLite, the databasesRequired; cache/ can be skipped, FreshRSS rebuilds it
extensions/Third-party extensionsRecommended, if you use any
The external databaseWith PostgreSQL, MySQL or MariaDB, the users’ dataRequired, separately

The volumes, with the service stopped

The documentation considers it safer to stop the service during maintenance operations. Docker volumes are copied with a temporary container, the method described in Docker’s documentation:

Terminal, in the freshrss folder
docker compose stop freshrss
docker run --rm --volumes-from freshrss -v "$PWD":/backup ubuntu tar -czf /backup/freshrss-$(date +%F).tgz --exclude=data/cache -C /var/www/FreshRSS data extensions
docker compose start freshrss

The databases, with the service running

Since version 1.29.1, FreshRSS can export each user’s database to a SQLite file by itself, whatever database you use, and keep only the most recent ones. Turn this export on in data/config.php:

data/config.php
'auto_sqlite_export' => [
    'enabled' => true,
    'retention' => 7,
],

Then schedule the command on the server, here every night. Each run writes data/users/<user>/sqlite-backups/<date>.sqlite and deletes files beyond the last seven:

/etc/cron.d/freshrss-export
20 2 * * * root docker exec --user www-data freshrss cli/export-sqlite-auto.php --quiet

With PostgreSQL, add a dump with the database’s own tool, as in the Docker README. The -T option drops the terminal, which a scheduled job has no use for:

Terminal, in the freshrss folder
docker compose exec -T freshrss-db pg_dump -U freshrss freshrss | gzip -9 > freshrss-postgres-$(date +%F).sql.gz

The OPML export, from the interface, backs up your feed list, without some FreshRSS-specific settings such as credentials or page scraping rules. For a complete copy, keep the SQLite exports: stored on the server’s disk, they are also part of its nightly backups.

Restoring

  1. Stop FreshRSS with docker compose stop freshrss.
  2. Load the archive back into the volumes: docker run --rm --volumes-from freshrss -v "$PWD":/backup ubuntu tar -xzf /backup/freshrss-YYYY-MM-DD.tgz -C /var/www/FreshRSS.
  3. With an external database, restore it from its dump, or from the SQLite files with cli/db-restore.php --delete-backup --force-overwrite.
  4. Start it again with docker compose start freshrss, then check that your feeds and articles are there.

Repeat this exercise from time to time, on a copy: that is how you check that a backup works.

Updates

The Docker README gives two commands, from the compose.yaml folder. The volumes stay in place:

Terminal
docker compose pull
docker compose up -d

FreshRSS publishes on two channels. Versioned releases, tag latest, come out a few times a year, on no set schedule, and their fixes are not backported to older versions. The rolling release, tag edge, gets new features and fixes as they land: since version 1.30.0, the project recommends it for faster security patches.

To follow new releases, the project publishes an Atom feed of its versions: subscribe your own FreshRSS instance to it.

Who does what on a managed VPS

Self-hosting means running the server on top of the application. On a managed VPS, our teams take charge of the server and proactively monitor your instance; FreshRSS remains your software, with its settings and its data.

TopicOur teamsYou
The serverSystem and security updates, server interventions includedThe timing that suits your business
MonitoringServer monitored 24/7, with intervention without waiting for your callReading your instance’s logs
FreshRSSProactive monitoring: errors, slowdowns and disk space spotted, fix recommendedIts installation, updates and settings, which you apply, yourself or through your AI assistant
Accounts and feedsThe server that carries them, monitored and backed upUsers, subscriptions, extensions and API passwords
BackupsEvery night, encrypted and kept off the machine; restores includedFreshRSS’s own exports, and the restore request, when you need one
MigrationYour current server migrated, free of chargeThe timing of the switch

If you would rather have our teams work inside the application itself, that service is billed by the hour or at a fixed rate, with an estimate shared before the work starts.

FreshRSS, on a server run for you

With a Simafri Managed VPS, our teams keep your FreshRSS instance’s server up to date, monitor it day and night and step in without waiting for your call; they also proactively monitor your instance and recommend useful fixes, which you apply, yourself or through your AI assistant. Backups are encrypted every night and kept off the machine, restores are included, and migrating your current server is free.

Explore Managed VPS

Frequently asked questions

Is FreshRSS free for a business?

Yes. FreshRSS is under the AGPL-3.0 license: you can run it at work with no license to buy. If you modify it, your version must offer its source code to the people who use it over the network. What remains is the cost of the server that runs it.

How do I install FreshRSS with Docker Compose?

Create a compose.yaml with the freshrss/freshrss image, the data and extensions volumes and the port published on 127.0.0.1, then run docker compose up -d. Next, put an HTTPS reverse proxy in front, open your address and follow the setup wizard.

How do I refresh feeds automatically in FreshRSS?

With the Docker image, give CRON_MIN the minutes to run at, for example 13,43: the project recommends about two runs per hour. Without CRON_MIN, the built-in job stays off. A scheduled job on the server that runs app/actualize_script.php inside the container does the same work.

Which database should I choose for FreshRSS?

SQLite is built in and needs nothing prepared; the Docker README describes it as the easiest, with good performance. The requirements page recommends PostgreSQL 10 or newer, and accepts MariaDB 10.6 and MySQL 8.0. The db-backup and db-restore commands let you switch databases later.

How do I read FreshRSS on mobile?

Turn on API access in the Authentication section, then set an API password in your profile. In a compatible app, enter your instance’s api/greader.php address, the Google Reader API being the best according to the README, or api/fever.php.

Are signups open on FreshRSS?

Not by default: the configuration limits accounts to the administrator’s. You create the others under Administration, Manage users, or you raise the number of accounts in the system configuration to show the signup form.

How do I back up FreshRSS?

Copy the data and extensions volumes with the service stopped. Also turn on auto_sqlite_export and schedule cli/export-sqlite-auto.php, which exports each user’s database to SQLite every night. With PostgreSQL, add a pg_dump. Test the restore regularly.

Does a managed VPS cover FreshRSS?

A managed VPS covers the server: system, security, monitoring and backups. Our teams also proactively monitor your instance and recommend useful fixes, which you apply. Work inside the application is billed by the hour or at a fixed rate.

Sources and official documentation

Contact us