A fast, reliable, private self-hosted feed reader supporting RSS/Atom, YouTube, Reddit, and Podcasts.
- π° RSS/Atom feeds - Standard web feed support
- πΊ YouTube channels - Subscribe to YouTube content via RSS
- ποΈ Podcasts - Full podcast support with audio player
- π Full-text search - Search across all articles (FTS5)
- π Folders - Organize feeds by folder
- π± PWA - Install as an app on mobile
- π Background sync - Automatic feed updates
- π Reader mode - Clean article view using Mozilla Readability
- π Dark theme - Easy on the eyes
- π€ OPML import/export - Migrate from other readers
- π Password protection - Secure your instance with JWT auth
# Clone the repository
git clone https://github.com/greg-hass/Feeds.git
cd Feeds
# Create environment file
cp .env.example .env
# Edit .env and set required variables (see Configuration section below)
nano .env
# Start with Docker Compose
docker-compose up -dAccess at http://localhost:3080
docker run -d \
--name feeds \
-p 3080:80 \
-v feeds_data:/data \
-e JWT_SECRET=your-secret-key-min-32-characters \
-e CORS_ORIGIN=http://localhost:3080 \
ghcr.io/greg-hass/feeds:latestCreate a .env file with these variables:
| Variable | Required | Description |
|---|---|---|
JWT_SECRET |
Yes | Secret key for authentication (min 32 chars) |
APP_PASSWORD |
No | Optional bootstrap password for first-time setup or password reset |
CORS_ORIGIN |
Production | Your domain (e.g., https://feeds.yourdomain.com) |
GEMINI_API_KEY |
No | Google AI key for feed discovery |
YOUTUBE_API_KEY |
No | YouTube Data API key (improves channel info) |
LOG_LEVEL |
No | debug, info, warn, error (default: info) |
# Option 1: OpenSSL
openssl rand -base64 64
# Option 2: Node.js
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"- Set environment variables (see Configuration above)
- Start the app:
docker-compose up -d - Open
http://localhost:3080 - Create password: On first visit, you'll see a setup screen
- Login: Use the password you just created
Note: APP_PASSWORD is optional. If set, it can be used as a bootstrap password for first-time setup and controlled resets. After setup, authentication uses the password you created in the app.
cd backend
npm install
npm run devcd frontend
npm install
npm run webData is stored in a Docker volume:
- Database:
/data/feeds.db - Icons:
/data/icons/ - Thumbnails:
/data/thumbnails/ - Backups:
/data/backups/(auto-created daily, 7 days retention)
Manual Backup:
# Create backup
docker exec feeds cp /data/feeds.db /data/backups/feeds-manual-$(date +%Y%m%d).db
# Copy to host
docker cp feeds:/data/backups/feeds-manual-YYYYMMDD.db ./Restore from Backup:
# Stop container
docker-compose down
# Restore database (replace YYYYMMDD with backup date)
docker cp ./feeds-backup-YYYYMMDD.db feeds:/data/feeds.db
# Start container
docker-compose up -dAutomated Backups:
The container automatically creates daily backups at /data/backups/ with 7-day retention. To persist backups outside the container:
# docker-compose.yml
volumes:
- feeds_data:/data
- ./backups:/data/backups # Mount backups to host- Open the setup screen and create an initial password
- If you want an environment-controlled bootstrap/reset flow, set
APP_PASSWORDin your.envand restart the container
- Check that
JWT_SECRETis set and hasn't changed - If you changed JWT_SECRET, you'll need to login again (tokens are invalidated)
- Login is rate-limited: 5 attempts per 15 minutes per IP
- Wait 15 minutes or restart container to reset
- Change the port in
docker-compose.yml:- "3081:80" - Access at
http://localhost:3081
docker-compose down -v # Removes volume
docker-compose up -d # Fresh start- Password hashing: bcrypt with 12 rounds
- JWT tokens: 30-day sliding expiration (extends with use)
- Rate limiting: 5 login attempts per 15 minutes
- Path traversal protection: Validated file paths
- SQL injection prevention: Parameterized queries
- XXE protection: Disabled XML entity processing
See API.md for full API documentation.
- Backend: Node.js, Fastify, SQLite (better-sqlite3)
- Frontend: React Native Web, Expo, Zustand
- Deployment: Docker, nginx
- Search: SQLite FTS5
MIT