New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
FreshRSS collects RSS and Atom feeds, filters articles, keeps reading history, and offers APIs for third-party mobile and desktop clients. This recipe adds PostgreSQL 18, scheduled feed refreshes, a deterministic first run, and a web port bound safely to localhost.
FreshRSS is a lightweight RSS and Atom aggregator for a personal feed or a small trusted group. It groups sources, filters and labels articles, supports search, OPML, and WebSub, while its Google Reader and Fever APIs work with third-party Android, iOS, and desktop clients.
This recipe runs pinned FreshRSS 1.29.1 with PostgreSQL 18. Initialization is deterministic: it creates a dedicated administrator with form authentication, enables the API with a different password, selects the English interface and production environment, and refreshes feeds twice per hour. Data, extensions, and PostgreSQL use separate persistent volumes.
The full HTTP smoke test has passed on amd64, arm64, and armv7; backup and
restore remain untested in practice. The server fetches submitted feed URLs and can reach internal
addresses, so this recipe is only for trusted users. FreshRSS 1.29.1 has no
INTERNAL_HOST_ALLOWLIST option; the recipe neither invents it nor permits *.
Untrusted users require a separate outbound network policy. Web binds to
localhost, PostgreSQL has no published port, and proxy-header trust is disabled
by default.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
1.29.11.29.1Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Manual steps after the pull. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has not been tested.
An exact Fully verified check for this target was recorded on 2026-09-03.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
You still need to add: single server, local access, minimum components.
A public deployment with protected access and backups.
You still need to add: domain, https via reverse proxy, off-server backup.
A setup designed for predictable operations and observability.
You still need to add: external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run FreshRSS bundle.
Screenshots have not been added yet.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psUse Ubuntu 22.04+ or Debian 12+ with Docker Engine and Compose v2.24+. Allocate at least 1 CPU, 512 MB RAM, and 2 GB disk; 1 GB RAM plus room for article history and backups is recommended. The official image declares amd64, arm64, and armv7.
docker --version
docker compose version
mkdir -p ~/services/freshrss
cd ~/services/freshrss
cp .env.example .env
chmod 600 .env
login_password="$(openssl rand -hex 24)"
api_password="$(openssl rand -hex 24)"
db_password="$(openssl rand -hex 24)"
sed -i "s|^FRESHRSS_ADMIN_PASSWORD=.*|FRESHRSS_ADMIN_PASSWORD=$login_password|" .env
sed -i "s|^FRESHRSS_ADMIN_API_PASSWORD=.*|FRESHRSS_ADMIN_API_PASSWORD=$api_password|" .env
sed -i "s|^FRESHRSS_DB_PASSWORD=.*|FRESHRSS_DB_PASSWORD=$db_password|" .env
unset login_password api_password db_password
These secrets contain letters and digits only. The 1.29.1 entrypoint evaluates
first-run option strings through shell eval, so spaces and shell metacharacters
are unsafe here. All three values must differ. Store them in a password manager:
API clients use the API password, not the web login password.
Every .env variable:
FRESHRSS_PORT is the local web port, default 8080;FRESHRSS_BASE_URL is the exact public HTTPS URL without a trailing /; a dedicated subdomain is more reliable than a subpath;FRESHRSS_ADMIN_USER is the ASCII-alphanumeric initial administrator login, distinct from secrets and changeable only before initialization;FRESHRSS_ADMIN_PASSWORD and FRESHRSS_ADMIN_API_PASSWORD are independent alphanumeric form-auth and Google Reader/Fever API passwords;FRESHRSS_ADMIN_EMAIL is the initial administrator email;FRESHRSS_DB_PASSWORD is the required independent PostgreSQL password;FRESHRSS_DB_NAME and FRESHRSS_DB_USER are the database and role names, changed only before the first start;FRESHRSS_TIME_ZONE is an IANA time zone;FRESHRSS_CRON_MIN selects built-in cron minutes; 13,43 refreshes twice per hour without joining the minute-zero spike;FRESHRSS_TRUSTED_PROXY controls trust in forwarded client-IP and external-auth headers; the safe default is 0, never a broad network;FRESHRSS_DATA_VOLUME, FRESHRSS_EXTENSIONS_VOLUME, and FRESHRSS_DB_VOLUME name persistent volumes;FRESHRSS_BACKUP_DIR selects the host backup directory.freshrss-data holds configuration, users, and service files;
freshrss-extensions holds third-party extensions; freshrss-database holds
PostgreSQL.
docker compose config
docker compose pull
docker compose up -d --wait
docker compose exec freshrss cli/health.php
docker compose exec freshrss cli/list-users.php
On an empty data volume, FRESHRSS_INSTALL creates a production PostgreSQL
configuration with form auth, anonymous access disabled, API enabled, English
as the interface language, and the exact base URL. FRESHRSS_USER creates the administrator.
Changing these variables after initialization does not modify the existing user.
Keep 127.0.0.1:${FRESHRSS_PORT}:80; only an HTTPS proxy on the host can reach
web. PostgreSQL has no published port. Allow only SSH, HTTP, and HTTPS through
the firewall. Configure DNS and replace FRESHRSS_BASE_URL and one proxy example
before starting. Do not invite untrusted users: they can make the server fetch
URLs from internal networks.
Prefer ssh -L 8080:127.0.0.1:8080 user@server; before the first start, use
FRESHRSS_BASE_URL=http://localhost:8080 for that route. For permanent LAN
access, replace the localhost bind with one specific private IP, use a matching
base URL, and restrict the port with a firewall. Do not use 0.0.0.0 without
network controls.
Use a dedicated host such as https://rss.example.com without a trailing / in
both .env and Caddy, Nginx, or Traefik. Caddy obtains a certificate, Nginx
expects Certbot files, and Traefik uses the letsencrypt resolver. The proxy
keeps Host and forwards X-Forwarded-Proto and Authorization, required by
some Google Reader API clients. FreshRSS does not require WebSockets, so upgrade
headers are deliberately absent. Never replace or remove FreshRSS’s CSP;
upstream explicitly warns proxies not to override it.
FRESHRSS_TRUSTED_PROXY=0 does not trust forwarded client IP or external-auth
headers. Form auth and the API work with an exact base_url without a broad
trusted range. If external auth is genuinely needed, list only the exact IP/CIDR
of the final secured proxy; an extra trusted address can spoof a user.
chmod +x backup.sh restore.sh
./backup.sh
The script stops FreshRSS and its built-in cron, keeps PostgreSQL available for
native pg_dump, archives complete data and extensions plus .env and Compose,
then restarts the app. This is a consistent full backup. OPML is insufficient:
it omits articles, users, feed credentials, refresh frequency, user agents, and
XPath scraping rules. Encrypt the archive, copy it off the server, and test
restores regularly.
Restore irreversibly replaces data, extensions, and PostgreSQL. Use the same
FreshRSS version and the active .env, then run:
./restore.sh ./backups/freshrss-YYYYMMDDTHHMMSSZ.tar
docker compose exec freshrss cli/health.php
The script first backs up the state being replaced, recreates all three volumes,
restores the native dump, and starts FreshRSS. The archived configuration.env
is retained for comparison only. This procedure has not passed a practical
restore test; test it on a separate server first.
Create a backup and read release notes. Replace the exact
freshrss/freshrss:1.29.1 tag with a reviewed version, never latest, then run:
docker compose pull
docker compose up -d --wait
docker compose exec freshrss cli/health.php
docker compose logs --tail=200 freshrss
Application migrations run at startup. Do not update FreshRSS and PostgreSQL at the same time, so rollback remains unambiguous.
postgres:18-alpine is pinned independently. A major change requires a new data
volume; old and new server majors must never share a cluster directory. Stop
FreshRSS, keep a native pg_dump and complete data/extensions backup, create a
new volume with the new image, and restore the dump. Upstream also offers
cli/db-backup.php and cli/db-restore.php for portable per-user SQLite exports.
Keep the old volume until the new database is verified.
Never run an older FreshRSS over migrated data. Restore the previous exact
FreshRSS and PostgreSQL tags and the complete pre-update archive with
restore.sh. After a failed PostgreSQL major change, attach the old image only
to its preserved old volume, or restore its dump into an empty cluster of the
same major.
docker compose down preserves data. After verifying an off-server backup,
remove everything irreversibly:
docker compose down
docker volume rm freshrss-data freshrss-extensions freshrss-database
rm -rf ~/services/freshrss
Substitute actual names when volume variables differ.
Sources: Docker and first run, backup and OPML, access control and SSRF, server CSP, and PostgreSQL upgrades.
docker compose ps
docker compose logs --tail=200 freshrss database
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q freshrss)"
docker compose exec freshrss cli/health.php
Check the three required distinct secrets, PostgreSQL health, free disk, and first-run errors. Passwords must be alphanumeric without spaces or shell characters. If an empty installation failed before completion, inspect logs and only then remove the empty volumes and retry; never delete working data.
FRESHRSS_BASE_URL must exactly match the public URL: scheme, host, optional
port and path, without a trailing /. It applies only during installation; for
an existing instance, change base_url in data config.php using upstream’s
documented procedure, then recreate the container. A dedicated subdomain is
simpler and more reliable than a subpath.
Ensure the client uses FRESHRSS_ADMIN_API_PASSWORD, not the web form password,
and the Google Reader endpoint on the same HTTPS host. Verify that Nginx forwards
Authorization; Caddy and Traefik preserve it by default. FreshRSS does not need
WebSockets.
This is FreshRSS 1.29.1 behavior, not a recipe error. Do not add a wildcard internal-host allowlist: this version has no such supported variable, and allowing every address would create a false sense of protection. For untrusted users, block container egress to loopback, RFC1918, link-local, and cloud metadata addresses with network/firewall policy, or isolate FreshRSS on a dedicated network.
The safe FRESHRSS_TRUSTED_PROXY=0 disables forwarded client-IP handling. If an
exact client IP is required, trust only the final proxy IP/CIDR and secure the
connection to FreshRSS. A broad range permits spoofed IP and
Remote-User/X-WebAuth-User headers, potentially granting administrator access.
Disable the third-party extension through the UI or restore extensions from a
backup. Do not hide the problem by suppressing the warning or overriding
Content-Security-Policy at the reverse proxy; FreshRSS emits its own CSP.
On the host, run curl -I http://127.0.0.1:8080/. Inspect the upstream, firewall,
and proxy namespace. Inside a container, 127.0.0.1 refers to the proxy itself,
not FreshRSS on the host; use a host gateway or a shared private network.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.