FreshRSS Docker Install: Self-Host a Lightweight Feed Aggregator

Complete guide to self-hosting FreshRSS using Docker Compose. Build a fast, lightweight RSS feed aggregator with multi-user support and feed fetching.

System Requirements & Prerequisites

Before deploying FreshRSS, ensure your VPS meets the following requirements: * OS: Ubuntu 22.04 LTS or any modern Linux distribution with Docker installed. * Resources: Minimal resources are required (as low as 1 CPU core and 512MB RAM for small instances). * Docker Engine: v20.10+ and Docker Compose v2.0+. * Networking: Ports 80 and 443 open on the host. DNS A/AAAA records configured for your subdomain (e.g., rss.example.com).


Docker Compose Configurations

FreshRSS can be deployed using SQLite for small, single-user setups, or PostgreSQL for high-volume, multi-user deployments. Below are both configurations.

Option A: FreshRSS with SQLite (Single-User Stack)

SQLite is lightweight and requires no separate database container. All data is stored in a single file on the host.

Create a new directory /opt/freshrss and place the following docker-compose.yml inside it:

version: '3.8'

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - TZ=Europe/Paris
      - CRON_MIN=*/20
    volumes:
      - ./data:/var/www/FreshRSS/data
      - ./extensions:/var/www/FreshRSS/xExtension

Option B: FreshRSS with PostgreSQL (Multi-User Stack)

For multiple active users, extensive feed lists, or long retention periods, PostgreSQL provides better concurrency and reliability.

version: '3.8'

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss-app
    restart: unless-stopped
    depends_on:
      freshrss-db:
        condition: service_healthy
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - TZ=Europe/Paris
      - CRON_MIN=*/20
    volumes:
      - ./data:/var/www/FreshRSS/data
      - ./extensions:/var/www/FreshRSS/xExtension
    networks:
      - freshrss-net

  freshrss-db:
    image: postgres:15-alpine
    container_name: freshrss-db
    restart: unless-stopped
    environment:
      - POSTGRES_DB=freshrss
      - POSTGRES_USER=freshrss_user
      - POSTGRES_PASSWORD=secure_db_password_here
    volumes:
      - ./postgres_data:/var/lib/postgresql/data
    networks:
      - freshrss-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U freshrss_user -d freshrss"]
      interval: 10s
      timeout: 5s
      retries: 5

networks:
  freshrss-net:
    driver: bridge

Directory Setup & Permissions

The FreshRSS container runs its internal web server (Apache) as the www-data user (UID/GID 33:33). To avoid permission errors when writing configuration files or downloading feeds, set permissions on the host directories before launching.

Execute the following commands on your VPS:

# Create directory structure
mkdir -p /opt/freshrss/data /opt/freshrss/extensions

# Set ownership to www-data (UID 33, GID 33 is standard for FreshRSS alpine/debian images)
chown -R 33:33 /opt/freshrss/data /opt/freshrss/extensions
chmod -R 755 /opt/freshrss/data /opt/freshrss/extensions

If you are using PostgreSQL (Option B), also initialize the database directory:

mkdir -p /opt/freshrss/postgres_data
# Postgres container handles its own permissions on postgres_data

To start the stack, run:

cd /opt/freshrss
docker compose up -d

Reverse Proxy Configurations

Do not expose the raw port 8080 to the public internet. Use a reverse proxy with TLS termination.

Nginx Configuration

Install Nginx on your VPS:

sudo apt update && sudo apt install nginx certbot python3-certbot-nginx -y

Create a server configuration block at /etc/nginx/sites-available/rss.example.com:

server {
    listen 80;
    listen [::]:80;
    server_name rss.example.com;

    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;

        # FreshRSS API extensions require HTTP Authentication header passthrough
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header Authorization;

        # Disable buffering to allow stream updates if configured
        proxy_buffering off;
    }
}

Enable the configuration and obtain an SSL certificate:

sudo ln -s /etc/nginx/sites-available/rss.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d rss.example.com --non-interactive --agree-tos -m admin@example.com

Caddy Configuration

If you prefer Caddy, add this block to your /etc/caddy/Caddyfile:

rss.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }
}

Automating Feed Refreshes

FreshRSS requires a periodic trigger to download new posts from feeds.

Method 1: Built-in Container Cron (Easiest)

By setting the CRON_MIN=*/20 environment variable in your docker-compose.yml, FreshRSS runs a cron daemon inside the container that refreshes the feeds every 20 minutes. No host-level configuration is needed.

For busy instances, executing via the host crontab avoids long-running cron daemons inside the container.

First, set CRON_MIN= (empty) in your docker-compose.yml to disable the internal daemon. Then, add the following entry to your host crontab:

# Open host crontab editor
crontab -e

Add this line to run the update every 15 minutes:

*/15 * * * * docker exec -u www-data freshrss php /var/www/FreshRSS/app/actualize_script.php > /dev/null 2>&1

(If you are using PostgreSQL, replace freshrss with the container name defined in docker-compose, e.g. freshrss-app).


First-Run UI & API Configuration

  1. Open https://rss.example.com in your browser.
  2. Select your language and proceed through the environment checks.
  3. Database Configuration:
  4. SQLite: Select SQLite and accept the default path.
  5. PostgreSQL: Select PostgreSQL and input the database connection details:
    • Host: freshrss-db (the service name in docker-compose)
    • User: freshrss_user
    • Password: secure_db_password_here
    • Database: freshrss
  6. Create your admin user account and set a secure password.
  7. Log in.

Enabling API Access for Mobile Clients (Reeder, NetNewsWire, FeedMe)

To sync your read/unread status across mobile and desktop apps:

  1. Go to Settings > Administrative > Authentication.
  2. Under API compatibility (Fever, Google Reader), check Allow API access.
  3. Go to Settings > Profile > API.
  4. Set an API password (this password is separate from your web login password).
  5. Open your mobile app and add a Google Reader API or Fever account:
  6. Endpoint URL: https://rss.example.com/api/greader.php (for Google Reader API)
  7. Username: Your FreshRSS admin/user username.
  8. Password: The API password you just set in your profile settings.