Polls RSS/Atom feeds on a cron schedule and saves new items to your Instapaper account. Runs as a Docker container, configured via a single YAML file.
- Supports any RSS or Atom feed
- Configurable cron schedule (per-instance, in
config.yml) - Deduplicates via SQLite — never submits the same item twice
- Backfill guard — ignores items older than a configurable number of days
- Per-feed error isolation — one broken feed never stops the others
- Automatic retry with exponential backoff on Instapaper API errors
- Graceful shutdown on
SIGTERM(Docker-safe) - Published to GHCR via GitHub Actions; multi-arch (
amd64+arm64)
Run once — the container writes config/config.yml then exits:
docker compose upinstapaper:
username: "your@email.com"
password: "yourpassword"
schedule: "*/30 * * * *" # every 30 minutes
feeds:
- name: "Hacker News Best"
url: "https://hnrss.org/best"
enabled: true
settings:
max_items_per_run: 20
backfill_days: 7
request_timeout: 30
log_level: "INFO"docker compose up -dLogs:
docker compose logs -f| Key | Default | Description |
|---|---|---|
instapaper.username |
— | Your Instapaper account email |
instapaper.password |
— | Your Instapaper password (use any value if your account has none) |
schedule |
*/30 * * * * |
Standard 5-field cron expression |
feeds[].name |
— | Display name (used in logs) |
feeds[].url |
— | RSS or Atom feed URL |
feeds[].enabled |
true |
Set to false to skip a feed without removing it |
settings.max_items_per_run |
20 |
Cap on new submissions per feed per run |
settings.backfill_days |
7 |
Items older than this are marked seen but not submitted |
settings.request_timeout |
30 |
HTTP timeout in seconds (feeds + Instapaper API) |
settings.log_level |
INFO |
DEBUG, INFO, WARNING, or ERROR |
| Variable | Description |
|---|---|
CONFIG_PATH |
Path to config.yml inside the container (default: /app/config/config.yml) |
DB_PATH |
Path to the SQLite database (default: /app/data/instarss.db) |
LOG_LEVEL |
Overrides settings.log_level; takes effect if more verbose than config value |
docker build -t instarss-local .
# Run with local image
docker run --rm \
-v "$(pwd)/config:/app/config" \
-v "$(pwd)/data:/app/data" \
instarss-localOr swap the image in docker-compose.yml:
services:
instarss:
build: . # replace the image: line with thisPush to main or tag a release — GitHub Actions handles the rest:
git push origin main # builds and pushes :latest + :sha-<hash>
git tag v1.0.0 && git push --tags # also pushes :v1.0.0The workflow (.github/workflows/docker-publish.yml) runs tests first and only pushes on success. No extra secrets are needed; it uses GITHUB_TOKEN.
| Host path | Container path | Contents |
|---|---|---|
./config/config.yml |
/app/config/config.yml |
Your configuration |
./data/instarss.db |
/app/data/instarss.db |
SQLite state (processed item GUIDs) |
The entrypoint chowns both directories to PUID:PGID on startup, so Docker-created root-owned directories are fixed automatically.
To reset state and reprocess all feeds:
rm data/instarss.db && docker compose restartpip install -r requirements.txt pytest pytest-mock responses
pytest tests/ -v