FreshRSS + RSS-Bridge on Ubuntu 24.04 on Azure User Guide
Overview
This image is a paired feed appliance: FreshRSS, the fast self hosted RSS and Atom reader, together with RSS-Bridge, the companion that manufactures feeds for sites that do not publish their own. FreshRSS gives you a single clean reading interface that you own and control, with no tracking and no third party cloud. RSS-Bridge extends its reach: when a site, video channel or social account has no RSS feed, RSS-Bridge builds one from the page so FreshRSS can subscribe to it.
The two are wired together with security in mind. FreshRSS is the authenticated, internet facing half, served by nginx and PHP 8.3 FPM on port 80. RSS-Bridge is the internal half: because it fetches upstream URLs on the requester's behalf, an internet exposed instance could be abused by anyone as an open feed proxy, so it is bound to loopback only on 127.0.0.1:8080 and is never reachable from the network. FreshRSS reaches RSS-Bridge internally over that loopback address and subscribes to the feed URLs it produces. The whole stack runs on a hardened, fully patched Ubuntu 24.04 LTS base, stores everything in a self contained SQLite database on a dedicated Azure data disk, and generates a unique administrator password on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- FreshRSS 1.29.1 served by nginx and PHP 8.3 FPM on
:80, with a per VMadminaccount - RSS-Bridge (release 2025-08-05) as the internal feed generator, bound to
127.0.0.1:8080only - Open proxy protection by design: RSS-Bridge is never exposed to the network, only FreshRSS is
- An example RSS-Bridge feed pre subscribed for
admin, so the integration works out of the box - A self contained SQLite database (no separate database server to run or tune)
- A per VM admin password generated on first boot and recorded in a root only file
- The Google Reader and Fever compatible APIs enabled for mobile and desktop reader apps
- A dedicated Azure data disk at
/var/lib/freshrssfor the database, cache, favicons and feeds nginx.serviceandphp8.3-fpm.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for large feed collections or many users. NSG inbound: allow 22/tcp from your management network and 80/tcp for the FreshRSS web interface. Do not open port 8080 to the network, and there is no need to: RSS-Bridge is bound to loopback and is reached by FreshRSS internally. FreshRSS serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain (see Step 11).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for FreshRSS by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Review the dedicated data disk on the Disks tab, then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name freshrss-rss-bridge \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name freshrss-rss-bridge --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<public-ip>
Step 4 - Confirm the services are running
FreshRSS is served by nginx on port 80, which forwards PHP requests to PHP 8.3 FPM over a local socket. RSS-Bridge runs behind the same PHP-FPM. Confirm the services are active and that nginx is listening on port 80:
systemctl is-active nginx php8.3-fpm freshrss-firstboot
ss -tlnp | grep ':80 '

Step 5 - Retrieve your admin password
On the first boot of every VM the freshrss-firstboot.service generates a unique administrator password, seeds the FreshRSS admin account with it (and sets the same password as the Google Reader and Fever API password), and writes the details to a root only file. Read it with:
sudo cat /root/freshrss-credentials.txt
The file records the FreshRSS URL, the admin username and its password, the Google Reader and Fever API endpoints, and a note that RSS-Bridge runs on loopback only. Store the password somewhere safe.
Step 6 - Confirm the FreshRSS stack and health endpoint
nginx serves an unauthenticated /healthz endpoint for load balancer probes. Confirm the FreshRSS release, the PHP version, that the web interface responds and that the Fever API is enabled:
grep -oP "FRESHRSS_VERSION = '\K[^']+" /var/www/freshrss/constants.php
php -v | head -1
curl -sI http://127.0.0.1/healthz | head -1
curl -s "http://127.0.0.1/api/fever.php?api"
You should see the FreshRSS release 1.29.1, PHP 8.3, HTTP/1.1 200 OK from /healthz, and {"api_version":4,"auth":0} from the Fever API confirming it is enabled.

Step 7 - Confirm RSS-Bridge is loopback only and generates feeds
RSS-Bridge is the companion that turns sites without feeds into feeds. Because it fetches upstream URLs on your behalf, it is deliberately bound to 127.0.0.1:8080 and is never exposed to the network, so it cannot be abused as an open proxy. Confirm it is listening on loopback only, and responding:
ss -tln | grep ':8080 '
curl -sI http://127.0.0.1:8080/ | head -1
The listen address is 127.0.0.1:8080 (and the IPv6 loopback [::1]:8080), never 0.0.0.0, and the interface returns HTTP/1.1 200 OK over loopback. From outside the VM the same port is closed:
curl --max-time 5 http://<public-ip>:8080/
# curl: (28) Connection timed out — port 8080 is not open to the internet by design.
# RSS-Bridge is only reachable from the VM itself, over 127.0.0.1.
Now prove the feed pipeline end to end. The image ships a small built in self test source, reachable only on loopback, so you can confirm feed generation without depending on an external site. The following asks the CSS Selector Bridge to turn that page into an Atom feed, exactly as FreshRSS does internally:
curl -s -G http://127.0.0.1:8080/ \
--data-urlencode action=display \
--data-urlencode bridge=CssSelectorBridge \
--data-urlencode home_page=http://127.0.0.1:8080/selftest/feed-source.html \
--data-urlencode 'url_selector=article h2 a' \
--data-urlencode format=Atom | head -12
It prints a valid Atom document, an <feed> element with <entry> items, proving RSS-Bridge can fetch a page, extract items and emit a feed for FreshRSS to consume.

Step 8 - Open FreshRSS in your browser
Browse to http://<public-ip>/. FreshRSS redirects anonymous visitors to the sign in page.

Step 9 - Sign in and read your feeds
Enter admin and the password from Step 5, then select Sign in. FreshRSS opens the reader: your subscriptions are listed in the left hand sidebar, and articles appear in the main pane. The image ships with two subscriptions so you have content straight away: a FreshRSS releases feed, and a cloudimg RSS-Bridge example source feed. That second feed is served by RSS-Bridge over loopback, so you can see the FreshRSS to RSS-Bridge integration working out of the box.

Step 10 - Subscribe FreshRSS to an RSS-Bridge feed
This is the core of the paired appliance. When you find a site with no RSS feed, you build one with RSS-Bridge and subscribe FreshRSS to it. Because RSS-Bridge lives on the same VM on loopback, the feed URL FreshRSS uses points at 127.0.0.1:8080, in the form:
http://127.0.0.1:8080/?action=display&bridge=<Bridge>&...&format=Atom
To add one, select the + (subscribe) control at the top of the FreshRSS sidebar and paste an RSS-Bridge feed URL of that shape (for example a CssSelectorBridge, YoutubeBridge or RedditBridge URL). FreshRSS fetches it internally and adds it like any other feed. The pre seeded example was created this way. Open Manage subscriptions (Subscription management) to see each feed and its underlying URL, including the loopback RSS-Bridge address.

To discover the exact bridge and parameters for a site, open RSS-Bridge's own UI from the VM (it is loopback only, so use an SSH local port forward from your workstation, which needs no NSG change):
ssh -L 8080:127.0.0.1:8080 azureuser@<public-ip>
# then browse to http://localhost:8080/ on your workstation
RSS-Bridge shows a searchable list of bridges; fill in a bridge form, click Generate feed, then copy the resulting http://127.0.0.1:8080/...&format=Atom URL into FreshRSS. The enabled bridges include the generic CSS Selector, XPath, Feed Merge, Feed Reducer and Filter bridges plus popular site bridges such as YouTube, Reddit, Mastodon, Telegram and Twitch.
Step 11 - Connect mobile and desktop reader apps
To read on the go, point a compatible mobile or desktop app (for example Reeder, Fluent Reader, FeedMe or NetNewsWire) at this instance. In your app choose the Google Reader API or Fever API and use these endpoints with the admin username and the API password from Step 5:
Google Reader API: http://<public-ip>/api/greader.php
Fever API: http://<public-ip>/api/fever.php
Step 12 - Production hardening: add a domain and TLS
The image ships FreshRSS on plain HTTP on port 80. TLS is a documented production step, not baked into the image, so you can bring your own domain and certificate. For production:
- Put your own domain in front of the VM and terminate TLS with a reverse proxy such as Azure Application Gateway, or with certbot on the VM issuing a Let's Encrypt certificate for your domain, forwarding to nginx on
:80. - Once HTTPS is in place, restrict the NSG so only
443/tcp(and22/tcpfrom your management network) is open to the internet. - Leave RSS-Bridge exactly as shipped, on loopback only. It never needs a public port; FreshRSS reaches it internally. Never add an NSG rule for
8080.
Step 13 - Confirm your data lives on the dedicated disk
All FreshRSS data lives under /var/lib/freshrss, which is a dedicated Azure data disk captured into the image and re provisioned on every VM. It holds the SQLite database, the per user configuration, the feed cache and favicons:
df -h /var/lib/freshrss | tail -1
ls /var/lib/freshrss
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/freshrss

Server Components
| Component | Version | Role |
|---|---|---|
| FreshRSS | 1.29.1 | Self hosted RSS and Atom reader, internet facing on :80 |
| RSS-Bridge | 2025-08-05 | Feed generator for sites without feeds, loopback only on 127.0.0.1:8080 |
| nginx | 1.24.0 | Reverse proxy fronting FreshRSS on :80 |
| PHP FPM | 8.3 | Runs FreshRSS and RSS-Bridge |
| SQLite | bundled | FreshRSS database on the dedicated data disk |
Maintenance
- Back up your data: a backup is a copy of
/var/lib/freshrss(the SQLite database, per user config, cache and favicons). Snapshot the data disk ortarthe directory. FreshRSS also ships./cli/db-backup.phpfor database only exports. - Add users: sign in as admin and use the user management screen under Settings, or run
sudo -u www-data php /var/www/freshrss/cli/create-user.php --user <name> --password <pw>on the VM. - Refresh feeds: FreshRSS refreshes feeds on a schedule. Force a refresh for a user with
sudo -u www-data php /var/www/freshrss/cli/actualize-user.php --user admin. - Enable more bridges: the RSS-Bridge configuration at
/var/www/rss-bridge/config.ini.phpenables a curated set under[system]. Add anenabled_bridges[] = <BridgeName>line and refresh; keep it curated rather than enabling all bridges. The available bridge names are in/var/www/rss-bridge/bridges/. - Keep RSS-Bridge internal: never open port 8080 in your NSG or rebind RSS-Bridge off loopback. Its loopback binding is the open proxy protection for the whole appliance.
- Mobile and desktop apps: connect over the Google Reader or Fever API endpoints shown in Step 11 with the
adminuser and the API password. - Security updates: unattended security upgrades are enabled, so the OS keeps itself patched. Reboot when a new kernel is installed.
- TLS: for production, front FreshRSS with your own domain and a TLS terminating reverse proxy or Azure Application Gateway (see Step 12).
Support
cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. FreshRSS is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0); RSS-Bridge is released into the public domain under the Unlicense. This image is provided by cloudimg; additional charges apply for build, maintenance and 24/7 support.