New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Miniflux is one Go binary and one PostgreSQL database: no plugins, no recommendation feed, no account anywhere else. It reads RSS, Atom, JSON Feed, and RDF, pulls the full article when a feed ships only an excerpt, and serves the Google Reader and Fever APIs so existing mobile readers sync against it.
Miniflux is a deliberately small feed reader: one Go binary, one PostgreSQL database, no plugins, no recommendation engine, and no account on somebody else’s server. It reads RSS, Atom, JSON Feed, and RDF, fetches the full article text when a feed only ships an excerpt, keeps entries readable offline in the browser, and exposes the Google Reader and Fever APIs so existing mobile readers can sync against it.
This recipe runs pinned Miniflux 2.3.3 with PostgreSQL 18. The application itself stores nothing on disk — feeds, entries, icons, sessions, and API keys all live in the database, so a single dump is the whole backup. The web port binds to localhost and PostgreSQL is not published at all.
The full application smoke test has passed on amd64 and arm64, and practical
backup and restore have passed on amd64.
Miniflux fetches arbitrary URLs on a schedule, so treat the poller as an
outbound HTTP client with the reach of its network — put an egress policy in
front of it if the instance is shared with people you do not fully trust.
Passwords are the only credential the recipe sets up; both the administrator
password and the PostgreSQL password sit in .env, which must be mode 600 and
must never enter Git. TRUSTED_REVERSE_PROXY_NETWORKS is limited to loopback,
so forwarded client-IP and proxy-authentication headers are ignored until an
operator deliberately trusts the Docker bridge network. The recipe does not set
up OAuth2/OIDC or passkeys; both are supported upstream and are configured
after the first sign-in.
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.
2.3.32.3.3Open 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.
Change the tag and restart. 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 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 Miniflux 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+. One CPU core, 512 MB RAM, and 2 GB disk are enough for a few hundred feeds; the database grows with retained entries and downloaded icons. The official image declares amd64 and arm64.
docker --version
docker compose version
mkdir -p ~/services/miniflux
cd ~/services/miniflux
cp .env.example .env
chmod 600 .env
admin_password="$(openssl rand -hex 24)"
db_password="$(openssl rand -hex 32)"
sed -i "s|^MINIFLUX_ADMIN_PASSWORD=.*|MINIFLUX_ADMIN_PASSWORD=$admin_password|" .env
sed -i "s|^MINIFLUX_DB_PASSWORD=.*|MINIFLUX_DB_PASSWORD=$db_password|" .env
echo "administrator password: $admin_password"
unset admin_password db_password
The database password ends up inside DATABASE_URL, so keep it alphanumeric: a
@, /, or # would break the connection string. The administrator password
must be at least 12 characters. Store both in a password manager; the
administrator password is shown once here and is not recoverable from the
database.
Every .env variable:
MINIFLUX_PORT is the local web port, default 8080;MINIFLUX_BASE_URL is the exact public URL without a trailing /; links, OAuth2 redirects, and the Google Reader endpoint are derived from it;MINIFLUX_ADMIN_USER and MINIFLUX_ADMIN_PASSWORD create the first account on the first start;MINIFLUX_DB_PASSWORD is an alphanumeric PostgreSQL password;MINIFLUX_DB_NAME and MINIFLUX_DB_USER are the database and role names, changed only before the first start;MINIFLUX_POLLING_FREQUENCY is the poller interval in minutes;MINIFLUX_POLLING_PARSING_ERROR_LIMIT is how many consecutive parse errors disable a feed;MINIFLUX_CLEANUP_ARCHIVE_READ_DAYS is how long read entries are kept;MINIFLUX_TRUSTED_PROXY_NETWORKS lists CIDR networks whose forwarded headers are trusted, loopback only by default, and must never be empty;MINIFLUX_LOG_LEVEL is error, warning, info, or debug;MINIFLUX_TIME_ZONE is an IANA time zone;MINIFLUX_DB_VOLUME names the PostgreSQL volume;MINIFLUX_BACKUP_DIR selects the host backup directory.RUN_MIGRATIONS and CREATE_ADMIN are pinned to 1 in the recipe. Both are
idempotent: migrations run at every start and the administrator is created only
while the user table is empty.
docker compose config
docker compose pull
docker compose up -d --wait --wait-timeout 600
docker compose ps
curl --fail http://127.0.0.1:8080/healthcheck
/healthcheck answers OK only after the database connection works, so a
successful call means the migrations finished. Sign in at MINIFLUX_BASE_URL
with MINIFLUX_ADMIN_USER, then add feeds or import an OPML file from
Settings → Import.
Keep 127.0.0.1:${MINIFLUX_PORT}:8080; only an HTTPS proxy on the host can
reach the application. PostgreSQL has no published port. Allow SSH, HTTP, and
HTTPS through the firewall and nothing else. Miniflux polls feeds from the
server, so outbound HTTPS must stay open; if the box also reaches private
services, restrict container egress to public networks.
Prefer ssh -L 8080:127.0.0.1:8080 user@server with
MINIFLUX_BASE_URL=http://localhost:8080 for that route. For permanent LAN
access, replace the localhost bind with one specific private IP, set a matching
MINIFLUX_BASE_URL, and restrict the port with a firewall. Do not bind
0.0.0.0 without network controls: the login form and the Fever API would then
answer on every interface over plain HTTP.
Set MINIFLUX_BASE_URL=https://reader.example.com, replace the host in the
Caddy, Nginx, or Traefik example, and recreate the container. The proxy must
forward the Authorization header — Caddy and Traefik do it by default, and the
Nginx sample does it explicitly — otherwise the Google Reader and Fever APIs
reject every client. Miniflux needs no WebSocket support. Serving from a subpath
is possible with BASE_URL, but a dedicated subdomain avoids rewriting the
static asset paths.
chmod +x backup.sh restore.sh
./backup.sh
The script stops the application so no poller writes mid-dump, keeps PostgreSQL
running for a native pg_dump, and archives the compressed dump together with
.env and compose.yaml. Everything Miniflux owns is in that dump: feeds,
entries, enclosures metadata, icons, sessions, and API keys. An OPML export is
not a backup — it lists subscriptions and nothing else. The archive contains
secrets; encrypt it and copy it off the server.
Restore irreversibly replaces the database. Use the same Miniflux and PostgreSQL
versions with the active .env:
./restore.sh ./backups/miniflux-YYYYMMDDTHHMMSSZ.tar
curl --fail http://127.0.0.1:8080/healthcheck
The script first backs up the state being replaced, recreates the volume,
restores the dump, and starts the stack. The archived configuration.env is
kept for comparison only and is never activated. This procedure has not passed a
practical restore test; rehearse it on a separate server before you rely on it.
Create a backup and read the release notes. Replace the exact
miniflux/miniflux:2.3.3 tag with a reviewed version, never latest, and do
not change PostgreSQL in the same step:
./backup.sh
docker compose pull
docker compose up -d --wait --wait-timeout 600
curl --fail http://127.0.0.1:8080/healthcheck
docker compose logs --tail=200 miniflux
Schema migrations run automatically at startup because RUN_MIGRATIONS=1.
postgres:18-alpine is pinned independently of the application. A major change
needs a new data volume: two server majors must never share a cluster directory.
Take a backup, start the new major on an empty volume, and restore the dump with
restore.sh. Keep the old volume until the new database is verified.
Never start an older Miniflux over a database that newer migrations have already touched. Restore the previous exact tag together with the pre-update archive:
docker compose down --timeout 60
./restore.sh ./backups/miniflux-BEFORE-UPDATE.tar
After a failed PostgreSQL change, attach the old image to its preserved old volume, or restore a compatible dump into an empty volume.
docker compose down preserves the database. After verifying an off-server
backup, remove everything irreversibly:
docker compose down
docker volume rm miniflux-database
rm -rf ~/services/miniflux
Substitute the actual name when MINIFLUX_DB_VOLUME differs.
Sources: Docker installation, configuration parameters, release 2.3.3, and PostgreSQL upgrades.
docker compose ps
docker compose logs --tail=200 miniflux database
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q miniflux)"
Miniflux exits on a bad configuration value rather than starting with a partial
one, and the message names the key. Two values fail most often: a database
password containing @, /, or #, which breaks DATABASE_URL, and an empty
MINIFLUX_TRUSTED_PROXY_NETWORKS, which is rejected with “at least one CIDR
notation network is required”. The health check itself calls /healthcheck,
which pings the database, so a healthy container also means migrations ran.
CREATE_ADMIN only acts while the user table is empty. If the first start
failed after the schema was created, the account may be missing. Create it by
hand instead of wiping the volume:
docker compose exec miniflux miniflux -create-admin
MINIFLUX_BASE_URL must match the public address exactly: scheme, host,
optional port and path, no trailing /. Miniflux builds cookie scope, OAuth2
redirects, and the Google Reader endpoint from it. Change the variable and
recreate the container; the value is read at startup.
Use the Google Reader endpoint at MINIFLUX_BASE_URL/googlereader (or the Fever
endpoint at /fever/) and the separate API password from Settings → API keys,
not the web password. Nginx must pass Authorization through — the sample in
proxy/nginx.conf sets proxy_pass_header Authorization for that reason; Caddy
and Traefik forward it by default.
A feed is disabled after MINIFLUX_POLLING_PARSING_ERROR_LIMIT consecutive
failures; the feed page shows the last error. Check the feed by hand before
raising the limit:
docker compose exec miniflux miniflux -refresh-feeds
docker compose logs --tail=200 miniflux | grep -i "unable to"
A whole instance that stops polling usually means the poller cannot reach the
internet from the container, or MINIFLUX_POLLING_FREQUENCY is far higher than
expected.
That is what the recipe intends: MINIFLUX_TRUSTED_PROXY_NETWORKS trusts
loopback only, so forwarded headers are ignored and every request appears to
come from the bridge. If you need real client addresses, add only the bridge
network of this project to the variable and make sure the proxy sets
X-Forwarded-For itself. Trusting a broad range lets a caller spoof both the
client IP and, if AUTH_PROXY_HEADER is ever enabled, the authenticated user.
On the host, run curl -I http://127.0.0.1:8080/. If that answers, the proxy is
looking at the wrong upstream: inside a proxy container 127.0.0.1 is the proxy
itself, not the host, so use a host gateway address or put the proxy on the same
Docker network. If it does not answer, the container is unhealthy — start from
the first section.
Retained entries and downloaded icons dominate the size. Lower
MINIFLUX_CLEANUP_ARCHIVE_READ_DAYS, then reclaim space during a maintenance
window:
docker compose exec miniflux miniflux -flush-sessions
docker compose exec database vacuumdb --analyze --dbname=miniflux --username=miniflux
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.