| .github/workflows | ||
| app | ||
| bootstrap | ||
| config | ||
| database | ||
| docker | ||
| public | ||
| resources | ||
| routes | ||
| storage | ||
| tests | ||
| .dockerignore | ||
| .editorconfig | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| artisan | ||
| components.json | ||
| composer.json | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.js | ||
| package.json | ||
| phpstan.neon | ||
| phpunit.xml | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.js | ||
| vite.config.ts | ||
Fedi Feed Router
ffr is a self-hosted tool for routing content from RSS/Atom feeds to the fediverse.
It watches feeds, matches entries based on keywords or rules, and publishes them to platforms like Lemmy, Mastodon, or anything ActivityPub-compatible.
Features
- Keyword-based routing from any RSS/Atom feed
- Publish to Lemmy, Mastodon, or other fediverse services
- YAML or JSON route configs
- CLI and/or daemon mode
- Self-hosted, privacy-first, no SaaS dependencies
Docker Deployment
Building the Image
docker build -t your-registry/lemmy-poster:latest .
docker push your-registry/lemmy-poster:latest
Docker Compose
Create a docker-compose.yml file:
services:
app-web:
image: your-registry/lemmy-poster:latest
command: ["web"]
ports:
- "8000:8000"
environment:
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- LEMMY_INSTANCE=${LEMMY_INSTANCE}
- LEMMY_USERNAME=${LEMMY_USERNAME}
- LEMMY_PASSWORD=${LEMMY_PASSWORD}
- LEMMY_COMMUNITY=${LEMMY_COMMUNITY}
depends_on:
- mysql
volumes:
- storage_data:/var/www/html/storage/app
restart: unless-stopped
app-queue:
image: your-registry/lemmy-poster:latest
command: ["queue"]
environment:
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- LEMMY_INSTANCE=${LEMMY_INSTANCE}
- LEMMY_USERNAME=${LEMMY_USERNAME}
- LEMMY_PASSWORD=${LEMMY_PASSWORD}
- LEMMY_COMMUNITY=${LEMMY_COMMUNITY}
depends_on:
- mysql
volumes:
- storage_data:/var/www/html/storage/app
restart: unless-stopped
mysql:
image: mysql:8.0
command: --host-cache-size=0 --innodb-use-native-aio=0 --sql-mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION --log-error-verbosity=1
environment:
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USERNAME}
- MYSQL_PASSWORD=${DB_PASSWORD}
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- TZ=UTC
volumes:
- mysql_data:/var/lib/mysql
restart: unless-stopped
volumes:
mysql_data:
storage_data:
Environment Variables
Create a .env file with:
# Database Settings
DB_DATABASE=lemmy_poster
DB_USERNAME=lemmy_user
DB_PASSWORD=your-password
# Lemmy Settings
LEMMY_INSTANCE=your-lemmy-instance.com
LEMMY_USERNAME=your-lemmy-username
LEMMY_PASSWORD=your-lemmy-password
LEMMY_COMMUNITY=your-target-community
Deployment
- Build and push the image to your registry
- Copy the docker-compose.yml to your server
- Create the .env file with your environment variables
- Run:
docker compose up -d
The application will automatically:
- Wait for the database to be ready
- Run database migrations on first startup
- Start the queue worker after migrations complete
- Handle race conditions between web and queue containers
Initial Setup
After deployment, the article refresh will run every hour. To trigger the initial article fetch manually:
docker compose exec app-web php artisan article:refresh
The application will then automatically:
- Fetch new articles every hour
- Publish valid articles every 5 minutes
- Sync community posts every 10 minutes
The web interface will be available on port 8000.
Architecture
The application uses a multi-container setup:
- app-web: Serves the Laravel web interface and handles HTTP requests
- app-queue: Processes background jobs (article fetching, Lemmy posting)
- mysql: Database storage for articles, logs, and application data
Both app containers use the same Docker image but with different commands (web or queue). Environment variables are passed from your .env file to configure database access and Lemmy integration.