guides/miniflux
RSS · Feed aggregation · Self-hosting

Miniflux · Docker

Minimalist, fast RSS reader written in Go — one binary, no JavaScript-heavy frontend, low resource usage. Perfect as a feed engine behind automation via its REST and Fever API.

Open Source · Apache 2.0 Docker / Linux Frédéric Guillot
Goone small binary
PostgreSQLonly backend
REST + FeverAPI for integration

Why Miniflux

Deliberately minimalist: no social features, no bloated UI, extremely easy to operate. It reads feeds, stores them in PostgreSQL and exposes a clean API — which makes it ideal as a data source for a threat intelligence pipeline rather than just a reader.

For your pipeline: Miniflux acts as the RSS engine that gathers your security feeds; n8n then pulls new articles via the API for screening/enrichment. Prerequisite: Docker installed.

Compose stack

PostgreSQL

Miniflux requires PostgreSQL — there is no SQLite option. Stack with an isolated database and admin created on first start.

compose.yaml
services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: miniflux
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: miniflux
    volumes:
      - mf_db:/var/lib/postgresql/data
    networks: [ internal ]
    healthcheck:
      test: [ "CMD", "pg_isready", "-U", "miniflux" ]
      interval: 10s
      retries: 5

  miniflux:
    image: miniflux/miniflux:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://miniflux:${DB_PASSWORD}@db/miniflux?sslmode=disable
      RUN_MIGRATIONS: 1
      CREATE_ADMIN: 1
      ADMIN_USERNAME: ${ADMIN_USERNAME}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      BASE_URL: https://rss.defencia.dk/
    ports:
      - "127.0.0.1:8080:8080"
    networks: [ internal, web ]

volumes:
  mf_db:

networks:
  internal:
    internal: true
  web:
RUN_MIGRATIONS creates/updates the schema automatically. CREATE_ADMIN creates the admin user on first startup from the env values.
.env
DB_PASSWORD=# openssl rand -base64 24
ADMIN_USERNAME=lars
ADMIN_PASSWORD=# choose a strong password

First start

Start the stack
docker compose up -d
docker compose logs -f miniflux
Create an extra admin manually (optional)
docker compose exec miniflux \
  miniflux -create-admin
Login: Once the proxy is in place, go to https://rss.defencia.dk and log in with the admin user. Add feeds one by one or import an entire OPML file under Settings → Import.

Reverse proxy (Nginx)

/etc/nginx/sites-available/rss.defencia.dk
server {
    listen 443 ssl;
    server_name rss.defencia.dk;

    # ssl_certificate ... (Certbot inserts)

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}
Miniflux has no websockets — the proxy block is simpler than n8n's. BASE_URL in compose must match this hostname.
Cert: sudo certbot --nginx -d rss.defencia.dk or your wildcard for *.defencia.dk.

API for automation

REST + Fever

This is where Miniflux shines as a pipeline component. Generate an API token under Settings → API Keys and call the REST API from n8n.

EndpointFunction
GET /v1/entries?status=unreadFetch unread articles (for screening in n8n)
GET /v1/feedsList all subscribed feeds
POST /v1/feedsAdd a new feed programmatically
PUT /v1/entriesMark articles as read/unread in bulk
GET /v1/entries?search=TERMFull-text search across feeds
GET /v1/meVerify token / user info
Example — fetch unread via curl
curl -H "X-Auth-Token: YOUR_TOKEN" \
  "https://rss.defencia.dk/v1/entries?status=unread&limit=50"
Pipeline pattern: n8n schedule trigger → call /v1/entries?status=unread → screen with Mistral → enrich → mark as read via PUT /v1/entries. The token is set as an n8n credential, so it never sits in cleartext in the workflow.

Operation

Update
docker compose pull
docker compose up -d
Migrations run automatically at startup due to RUN_MIGRATIONS=1.
Backup database
docker compose exec db \
  pg_dump -U miniflux miniflux > mf_$(date +%F).sql
Export feeds (OPML)
# in UI: Settings → Export → OPML
# or via API: GET /v1/export
Check status
docker compose ps
docker compose logs --tail=50 miniflux
For your backup stack: Take an OPML export (your ~71 curated feeds, portable) plus a pg_dump (read/unread states, starred items). OPML alone is enough to rebuild the subscriptions if the database is lost.

Hardening

ControlRecommendation
ExposureBind to 127.0.0.1:8080, only via Nginx + HTTPS
DatabasePostgres on the internal network, no host port
API tokenUse a token, not basic auth, for automation; rotate as needed
LoginStrong admin password; Fail2ban jail on login
Feed proxyConsider FETCH_* timeouts against slow/malicious feeds
UsersCreate a separate non-admin user for the API if possible
UpdatesKeep the image updated — the Go binary is small, upgrades are fast
Remember the Docker+UFW trap: Bind to 127.0.0.1:8080 — never 0.0.0.0 for internal services. See the Docker guide.