How to Install FreshRSS for Ubuntu VPS
Install FreshRSS 1.29.1 for Ubuntu 24.04 VPS with Apache, PHP 8.3 and MariaDB, plus cron refresh and the encoded-slash fix for mobile API.
Wetin you dey build
Self-hosted RSS reader na feed reader wey dey run for server wey you own, so nobody fit shut am down or change wetin e dey show you. This guide go install FreshRSS for Ubuntu 24.04 VPS: Apache go dey front, PHP go dey behind am, MariaDB go store data, and one cron job go fetch new articles. RSS (really simple syndication) na file format wey website publish so software fit read the articles. FreshRSS na PHP application wey dey collect those files, keep the articles, and give you web interface plus API (application programming interface) wey phone apps dey use communicate.
The installation itself small: unpack one release, create database, write one virtual host, then run one command line installer. Most of the work wey people dey get wrong na wetin happen afterwards: refresh job, encoded slashes for mobile API, and file ownership.
FreshRSS 1.29.1 na the current release as of July 2026, and e need PHP 8.1 or newer. Ubuntu 24.04 dey ship PHP 8.3, so distribution packages dey enough and you no need third party PHP repository.
Start with LAMP stack wey dey work
FreshRSS na normal PHP application, so e need the same base like any other one. If you never build this base yet, follow LAMP stack setup for Ubuntu 24.04 first, then come back here. The short version na:
sudo apt update
sudo apt install -y apache2 mariadb-server php libapache2-mod-php
sudo systemctl enable --now apache2 mariadbsystemctl status apache2 suppose report active (running). If Apache no gree start, the common cause na another process wey already dey hold port 80, and sudo ss -ltnp | grep :80 go show the process name.
PHP extensions wey FreshRSS need
FreshRSS dey treat libxml, cURL, JSON, PDO_MySQL, PCRE and ctype as mandatory. E still need mbstring, iconv, Zlib and ZipArchive, plus GMP for 32 bit system. For Ubuntu, distribution packages dey provide dem:
sudo apt install -y php-curl php-mbstring php-xml php-zip php-mysql php-intl php-gmp
sudo systemctl restart apache2Use php -m check wetin PHP really load. If extension dey miss, installer no go stop from starting. E go stop for requirements screen and show red line wey name the extension. E fit confuse person to discover the problem for there, so confirm am now. Restarting Apache matter because libapache2-mod-php dey keep PHP inside Apache process. So, newly installed extension no go visible to web server until you restart am.
Download the release
Install FreshRSS outside the default web root, then point Apache go am. If you keep the application directory separate from the document root, na only the public folder HTTP fit reach.
cd /tmp
curl -fsSLO https://github.com/FreshRSS/FreshRSS/archive/refs/tags/1.29.1.tar.gz
tar xzf 1.29.1.tar.gz
sudo mv FreshRSS-1.29.1 /srv/freshrssNow make we set the permissions. FreshRSS documentation strict for this matter: web server user own the whole tree, group fit read every file, and group fit write to ./data/.
sudo chown -R www-data:www-data /srv/freshrss
sudo chmod -R g+r /srv/freshrss
sudo chmod -R g+w /srv/freshrss/dataIf you skip this step, installer go fail when e dey write configuration. PHP dey run as www-data, and www-data no fit write inside directory wey root own.
Create database
FreshRSS support SQLite, MariaDB, MySQL and PostgreSQL. SQLite no need setup, and e good for one person wey get few hundred feeds. MariaDB na better choice when several people dey share the instance, because concurrent writes from the refresh job and web interface no longer dey compete for one file lock.
sudo mariadb -e "CREATE DATABASE freshrss CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mariadb -e "CREATE USER 'freshrss'@'localhost' IDENTIFIED BY 'ReplaceThisPassword';"
sudo mariadb -e "GRANT ALL PRIVILEGES ON freshrss.* TO 'freshrss'@'localhost';"
sudo mariadb -e "FLUSH PRIVILEGES;"Use utf8mb4, no be utf8. Feeds fit carry emoji and non-Latin scripts, and the old three byte utf8 encoding dey cut article title for the first four byte character.
The Apache virtual host
The public directory na p/, no be the top of the tree. Everything else, including the configuration file wey hold your database password, dey above the document root where Apache no go ever serve am.
<VirtualHost *:80>
ServerName rss.example.com
DocumentRoot /srv/freshrss/p/
<Directory /srv/freshrss/p>
AllowOverride AuthConfig FileInfo Indexes Limit
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
AllowEncodedSlashes On
</VirtualHost>Save am as /etc/apache2/sites-available/freshrss.conf, then enable am:
sudo a2enmod rewrite
sudo a2ensite freshrss
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2configtest suppose print Syntax OK. AllowEncodedSlashes On fit look optional, but e no be optional: the Google Reader API dey send feed identifiers wey contain %2F, and without this directive Apache go reject dem. Because of this, mobile apps no go sync while the web interface still dey work perfectly.
Add HTTPS before you log in
You wan type password for this site now, so first get certificate. Point an A record to the server, then follow Certbot setup for Apache on Ubuntu and run sudo certbot --apache -d rss.example.com. Certbot go rewrite the virtual host for port 443 and add the redirect. Confirm am with curl -I https://rss.example.com/, wey suppose return 200 or redirect go the login page.
Run installer from command line
FreshRSS get browser installer, but command line version fit repeat and e dey leave record of exactly wetin you choose.
sudo -u www-data php /srv/freshrss/cli/do-install.php \
--default-user admin --auth-type form --environment production \
--base-url https://rss.example.com --language en --api-enabled \
--db-type mysql --db-host localhost --db-user freshrss \
--db-password 'ReplaceThisPassword' --db-base freshrss
sudo -u www-data php /srv/freshrss/cli/create-user.php \
--user admin --password 'a-long-passphrase' --api-password 'a-different-passphrase'Run both as www-data. If you run dem as root, e go create configuration files wey root own, and web interface go later fail to save any setting. --environment production matter too, because development setting dey print PHP notices inside the page.
Load https://rss.example.com/ and log in as admin.
Why feeds no dey refresh by demself
Nothing go poll your feeds until you tell am. FreshRSS dey refresh while browser dey open on am, so an instance wey you visit two times for one day go show you articles wey don stale for twelve hours. The fix na the script wey the project release for this work, app/actualize_script.php, run am from cron.
sudo crontab -u www-data -eAdd one line:
*/20 * * * * php /srv/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&1Twenty minutes na the sensible minimum, because the script no dey allow any single feed refresh pass once every twenty minutes. So schedule wey tighter than that na just CPU waste. Run am by hand once first:
sudo -u www-data php /srv/freshrss/app/actualize_script.phpHealthy output go list each feed wey e fetch and finish without PHP error. If e no print anything at all, the cron user no correct. Permission error for data/ mean say the chmod -R g+w step no run.
Add your first feeds
Click the plus button for top left of the interface, paste the site address, and FreshRSS go discover the feed link for you. Most sites still publish one even when dem no advertise am, usually for /feed, /rss or /atom.xml. Categories na folders, and to move feed between dem na just drag am.
If you dey come from another reader, export OPML file there and import am under the subscription management page. OPML (outline processor markup language) na the standard format for feed list, and every reader wey worth leaving support am. Big import fit slow for the first refresh because every feed go fetch once, so give the first cron run time before you judge the speed.
Read am for your phone
FreshRSS dey speak Google Reader API, and almost every RSS app support am. Two things must dey correct. For authentication settings, "Allow API access" must dey on; the --api-enabled flag above don already set am. For your profile, API password field must get value. E separate from your login password on purpose because phone dey easier to lose.
Visit https://rss.example.com/api/ and choose "Check full server configuration". If setup dey work, e go return PASS. If e fail here, almost always na because AllowEncodedSlashes On line dey miss. For the app, enter server address as https://rss.example.com/api/greader.php, username as your FreshRSS user, and API password as the password.
Docker alternative
If you no want maintain PHP and Apache by hand, the project publish official freshrss/freshrss image, and one compose file go give you the application and its database together. The trade-off na the usual one: fewer moving parts for the host, but one extra layer to debug when something break. You still need reverse proxy for TLS (transport layer security). If this arrangement suit you better, Docker Compose basics for VPS explain the file format, and the cron line go become docker exec --user www-data freshrss php ./app/actualize_script.php.
Backups and upgrades
Na two things dey hold your state: the database and /srv/freshrss/data/. Use sudo mysqldump freshrss > freshrss.sql dump the first one, copy the second one, and keep both for another place wey no be this server. Your subscription list still worth occasional OPML export, because that file fit rebuild your reading setup for any RSS software.
To upgrade, unpack newer release for the same directory, then run the ownership commands again. FreshRSS go apply its own database migrations the next time you load page. Make backup first, because if migration fail and you no fit restore the database, you no get way to recover am. Reader service dey low risk to run, so e good as first option if you dey work through list of things wey worth self-hosting.
FAQ
Why my feeds dey update only when I open FreshRSS?
Because no scheduler dey until you create one. FreshRSS dey refresh feeds while browser session dey open, and e no do anything when you close the tab. Add the cron line wey dey call app/actualize_script.php as user www-data, then run the script by hand once and read wetin e output. If e no print anything, usually cron dey run am as wrong user, so PHP no fit write to data/.
My mobile app no fit connect, but the website dey work. Why?
Google Reader API dey put encoded slashes (%2F) inside request paths, and Apache dey reject dem by default. Add AllowEncodedSlashes On inside the virtual host and reload Apache. Confirm the fix by opening https://rss.example.com/api/ and running "Check full server configuration", wey suppose report PASS. Also check say API password dey set for your profile, because e separate from your login password.
Make I use SQLite or MariaDB?
Use SQLite for one user, because nothing dey to install and you no need manage password. Use MariaDB once more than one person dey read for the instance, or once feeds pass a few hundred, because refresh job and web interface dey write at the same time, and one file lock become the limit. You fit move between dem later through the export and import commands, so this no be permanent decision.
The installer dey fail when e dey write configuration. Wetin dey wrong?
PHP dey run as www-data under Apache, and that user no fit write inside directory wey root own. Run sudo chown -R www-data:www-data /srv/freshrss and sudo chmod -R g+w /srv/freshrss/data again, then start the installer again. If you don already run the command line installer as root, delete the files wey e create under data/ before you try again, because na their ownership cause the problem.
How much server self-hosted RSS reader need?
Very small amount. A few hundred feeds for small plan dey comfortable, because the load na short bursts of HTTP fetching every twenty minutes, and database stay small once old articles don purge. Disk dey grow based on retention, so set article purge policy for archiving settings instead of keeping everything forever.