Getting FreshRSS + RSSHub running is easy. Keeping it stable is the hard part: lost state after restarts, repeated 403 errors, broken proxy headers, and risky upgrades.

This guide gives a production-ready Linux baseline focused on long-term stability, observability, and safe upgrades.

Architecture and Stability Goals

  • FreshRSS: feed aggregation, reading, filtering rules
  • RSSHub: dynamic feed generation
  • Redis: cache layer for RSSHub to reduce upstream pressure
  • Nginx/Caddy: reverse proxy + HTTPS

Target outcomes:

  • Services auto-recover after crashes
  • Persistent data volumes
  • Key settings managed through env vars
  • Health checks and clear log paths

1) Directory Layout and Prerequisites

sudo mkdir -p /opt/rss-stack/{freshrss-data,rsshub-cache,logs}
cd /opt/rss-stack

docker --version
docker compose version

2) docker-compose.yml (Production Baseline)

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      CRON_MIN: '3,33'
      TRUSTED_PROXY: 172.16.0.0/12 192.168.0.0/16
    volumes:
      - ./freshrss-data:/var/www/FreshRSS/data
      - ./freshrss-data/extensions:/var/www/FreshRSS/extensions
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:80/"]
      interval: 30s
      timeout: 5s
      retries: 5
    networks:
      - rss_net

  redis:
    image: redis:7-alpine
    container_name: rsshub-redis
    restart: unless-stopped
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - ./rsshub-cache:/data
    networks:
      - rss_net

  rsshub:
    image: diygod/rsshub:chromium-bundled
    container_name: rsshub
    restart: unless-stopped
    depends_on:
      - redis
    environment:
      NODE_ENV: production
      CACHE_TYPE: redis
      REDIS_URL: redis://redis:6379/
      PUPPETEER_WS_ENDPOINT: ''
      REQUEST_RETRY: 2
      REQUEST_TIMEOUT: 10000
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:1200/healthz"]
      interval: 30s
      timeout: 5s
      retries: 5
    networks:
      - rss_net

networks:
  rss_net:
    driver: bridge

Start services:

docker compose up -d
docker compose ps

3) Reverse Proxy + HTTPS (Nginx Example)

server {
  listen 443 ssl http2;
  server_name rss.example.com;

  location / {
    proxy_pass http://127.0.0.1:1200;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
  }
}

server {
  listen 443 ssl http2;
  server_name reader.example.com;

  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
  }
}

If FreshRSS is not exposed on host port 8080, use the actual mapped port or proxy through an internal Docker network.

4) Common Failure Cases and Fixes

RSSHub keeps returning 403 / timeout

  • Lower crawl frequency and prioritize caching (Redis)
  • Use proxy/IP rotation for heavily protected sources
  • Increase REQUEST_TIMEOUT (10s → 15s) and inspect logs
docker logs --tail=200 rsshub

FreshRSS scheduled updates do not run

  • Verify CRON_MIN
  • Verify container timezone
  • Manually trigger refresh from admin panel

White screen or extension issues after upgrades

Always back up before pull:

tar czf backup-freshrss-$(date +%F).tgz /opt/rss-stack/freshrss-data
docker compose pull
docker compose up -d

5) Conservative Growth Operations

  • Validate new feed routes in small batches for 24 hours
  • Weekly maintenance: docker compose pull && docker compose up -d
  • Tier RSS routes by value: keep high-value feeds stable, reduce frequency for low-value ones

Summary

The key is not just “it works”, but “it is recoverable under failure”.

With persistent data, Redis caching, health checks, and correct proxy headers, FreshRSS + RSSHub can run reliably on Linux and scale smoothly later (monitoring, migration to k3s, etc.).