Deploying Bubo Reader
Bubo Reader is an irrationally minimalist static RSS feed reader that helps you stay updated with your favorite content sources. Built with TypeScript and designed for simplicity, Bubo Reader generates a fast, lightweight webpage displaying the latest articles from multiple RSS, Atom, and JSON feeds organized by category. Whether youβre a researcher staying on top of industry news, a developer following tech blogs, a content enthusiast tracking multiple publications, or someone seeking privacy with zero-database architecture, Bubo Reader provides a clean, distraction-free way to consume feed content without the complexity of traditional feed readers.
Why Bubo Reader?
Bubo Reader stands out as the premier choice for minimalist feed reading with exceptional features:
- Multiple Feed Formats: Support for RSS, Atom, and JSON feeds
- Category Organization: Organize feeds into logical categories
- Lightning-Fast Performance: Static site generation for instant loading
- Zero-Database Architecture: No database required, pure static files
- Feed Aggregation: Automatic feed fetching and aggregation
- Responsive Design: Works seamlessly on desktop, tablet, and mobile
- Minimal Dependencies: Lightweight technology stack
- Easy Configuration: Simple JSON-based feed configuration
- Privacy-First: Complete control over your data
- Self-Hosted Control: Your feeds, your server, your rules
- No Ads or Tracking: Keep your reading habits private
- Open Source: MIT licensed with active community
- Content Extraction: Display feed titles, descriptions, and links
- Customizable Templates: Simple HTML templates for styling
- RSS Standards Compliant: Works with standard-compliant feeds
- Favicon Support: Display feed source favicons
- Sorting Options: Organize feeds by publish date or category
- Lightweight HTML Output: Minimal CSS and JavaScript
- Easy Deployment: Works on any static hosting platform
- Feed Management: Easy feed addition and removal
Bubo Reader is ideal for developers and tech enthusiasts staying current with programming blogs, researchers tracking multiple information sources, journalists following news feeds, academics monitoring publication feeds, professionals staying informed in their field, and anyone wanting a private, ad-free feed reading experience. With deployment on Klutch.sh, your feed reader is always available and completely under your control.
Prerequisites
Before deploying Bubo Reader, ensure you have:
- A Klutch.sh account
- A GitHub repository with your Bubo Reader deployment configuration
- Basic familiarity with Docker and Git
- A list of RSS/Atom/JSON feeds you want to monitor
- A text editor for configuring feeds.json
- Minimal storage requirements (typically less than 1GB)
- Custom domain for your feed reader (recommended)
Important Considerations
Deploying Bubo Reader
Create a New Project
Log in to your Klutch.sh dashboard and create a new project for your Bubo Reader RSS feed aggregator.
Prepare Your Repository
Create a GitHub repository with the following structure for your Bubo Reader deployment:
bubo-reader-deploy/ββ Dockerfileββ config/β ββ feeds.jsonββ .gitignoreββ README.mdHereβs a Dockerfile for Bubo Reader:
FROM node:18-alpineWORKDIR /app# Install system dependenciesRUN apk add --no-cache \curl \git \build-base# Clone Bubo Reader repositoryRUN git clone https://github.com/georgemandis/bubo-rss.git /tmp/bubo && \cp -r /tmp/bubo/* .# Install dependenciesRUN npm install# Create config directoryRUN mkdir -p /app/config# Copy feeds configurationCOPY config/feeds.json /app/config/feeds.json# Create output directoryRUN mkdir -p /app/public# Expose portEXPOSE 3000# Build and start serverENTRYPOINT ["sh", "-c"]CMD ["npm run build:bubo && npx http-server public -p 3000 -c-1"]Create a
config/feeds.jsonfile with your feed sources:{"feeds": [{"category": "Technology","feeds": [{"name": "Hacker News","url": "https://news.ycombinator.com/rss"},{"name": "CSS-Tricks","url": "https://css-tricks.com/feed/"},{"name": "Dev.to","url": "https://dev.to/feed"}]},{"category": "News","feeds": [{"name": "BBC News","url": "https://feeds.bbci.co.uk/news/rss.xml"},{"name": "Reuters","url": "https://feeds.reuters.com/reuters/businessNews"}]},{"category": "Personal Blogs","feeds": [{"name": "Blog Name","url": "https://example.com/feed"}]}]}Create a
.gitignorefile:node_modules/.next/build/dist/out/public/.env.env.local*.loglogs/.DS_Store.idea/*.swp*.swo.cache/Commit and push to your GitHub repository:
Terminal window git initgit add .git commit -m "Initial Bubo Reader RSS feed deployment"git remote add origin https://github.com/yourusername/bubo-reader-deploy.gitgit push -u origin mainCreate a New App
In the Klutch.sh dashboard:
- Click βCreate New Appβ
- Select your GitHub repository containing the Dockerfile
- Choose the branch (typically
mainormaster) - Klutch.sh will automatically detect the Dockerfile in the root directory
Configure Environment Variables
Set up environment variables in your Klutch.sh dashboard (optional for basic setup):
Variable Description Example NODE_ENVEnvironment type productionBUILD_COMMANDCustom build command npm run build:buboSet Network Configuration
Configure your appβs network settings:
- Select traffic type: HTTP
- Recommended internal port: 3000 (default web server port)
- Klutch.sh will handle HTTPS termination via reverse proxy
- Application will be accessible via your custom domain
Configure Custom Domain
Bubo Reader benefits from a custom domain:
- Navigate to your appβs βDomainsβ section in Klutch.sh
- Add custom domain (e.g.,
feeds.yourdomain.com) - Configure DNS with CNAME record pointing to your Klutch.sh app
- Klutch.sh will automatically provision SSL certificate
- Wait for DNS propagation (typically 5-15 minutes)
Deploy Your App
- Review all settings and configuration carefully
- Verify feeds.json contains valid feed URLs
- Verify custom domain is configured
- Click βDeployβ
- Klutch.sh will build the Docker image and start your Bubo Reader instance
- Wait for deployment to complete (typically 5-10 minutes)
- Access your feed reader at your configured domain
- Verify feeds are loading correctly
Initial Setup and Configuration
After deployment completes, configure and customize your feed reader.
Accessing Bubo Reader
Navigate to your domain: https://feeds.yourdomain.com
Your Bubo Reader instance displays all configured feeds organized by category with the latest articles from each source.
Understanding the Feed Configuration
The feeds.json file structure:
{ "feeds": [ { "category": "Category Name", "feeds": [ { "name": "Feed Display Name", "url": "https://example.com/feed" } ] } ]}Adding New Feeds
To add new feeds to your reader:
- Edit
config/feeds.jsonin your repository - Add feed details:
- Category: Group feeds by topic
- Name: Display name for the feed
- URL: Direct link to RSS/Atom/JSON feed
- Commit and push changes to GitHub
- Klutch.sh automatically rebuilds and deploys
- New feeds appear in your reader within minutes
- Verify feeds load properly
- Monitor feed health
Example addition:
{ "category": "Technology", "feeds": [ { "name": "New Tech Blog", "url": "https://newtechblog.com/feed" } ]}Finding Valid Feed URLs
Locate RSS/Atom/JSON feed URLs:
- Visit website and look for RSS icon
- Right-click RSS icon and copy link
- Or look for
/feed,/rss, or/atomURLs - Check website footer for feed links
- Use FeedFinder to locate feeds
- Test URL in browser to verify
- Common patterns:
/rss.xml,/feed.json,/atom.xml
Organizing Feeds by Category
Create logical feed categories:
- Technology: Programming, development, tech news
- News: World news, politics, current events
- Personal Blogs: Individual bloggers you follow
- Industry: Industry-specific publications
- Entertainment: Media, culture, entertainment
- Science: Scientific research and discoveries
- Business: Business news and analysis
Keep categories focused and manageable.
Feed Updates and Rebuilds
Bubo Reader refreshes feeds on rebuild:
- Automatic Rebuild: GitHub push triggers new build
- Manual Rebuild: Use Klutch.sh dashboard to redeploy
- Scheduled Updates: Use GitHub Actions for regular rebuilds
- Build Frequency: Determines content freshness
- Performance: Builds take 2-5 minutes typically
Customizing Feed Reader Appearance
Modify feed reader styling:
- Access your repository
- Edit
src/index.htmlorpublic/index.html - Modify CSS for colors and layout
- Update templates in
src/directory - Commit changes
- Klutch.sh automatically rebuilds
- Customizations appear in live reader
Removing Problematic Feeds
Handle feeds that no longer work:
- Edit
config/feeds.json - Remove feed entry or comment it out
- Commit and push changes
- Klutch.sh rebuilds without that feed
- Verify feed removed from reader
- Look for alternative feed sources
- Add replacement feed
Feed Configuration Examples
Basic Feed Configuration
{ "feeds": [ { "category": "Tech News", "feeds": [ { "name": "Hacker News", "url": "https://news.ycombinator.com/rss" }, { "name": "Slashdot", "url": "https://slashdot.org/feed" } ] } ]}Comprehensive Feed Configuration
{ "feeds": [ { "category": "Technology", "feeds": [ { "name": "Hacker News", "url": "https://news.ycombinator.com/rss" }, { "name": "CSS-Tricks", "url": "https://css-tricks.com/feed/" }, { "name": "Dev.to", "url": "https://dev.to/feed" }, { "name": "Web Standards", "url": "https://www.w3.org/blog/feed/" }, { "name": "JavaScript Weekly", "url": "https://javascriptweekly.com/rss/56f5f3b8d" } ] }, { "category": "News", "feeds": [ { "name": "BBC News", "url": "https://feeds.bbci.co.uk/news/rss.xml" }, { "name": "Reuters", "url": "https://feeds.reuters.com/reuters/businessNews" }, { "name": "The Guardian", "url": "https://www.theguardian.com/world/rss" } ] }, { "category": "Science & Nature", "feeds": [ { "name": "NASA", "url": "https://www.nasa.gov/news/nasa-news-rss-feeds/" }, { "name": "Nature Magazine", "url": "https://www.nature.com/nature.rss" }, { "name": "Scientific American", "url": "https://feeds.scientificamerican.com/feeds/sa-news" } ] }, { "category": "Personal Blogs", "feeds": [ { "name": "Blog One", "url": "https://example1.com/feed" }, { "name": "Blog Two", "url": "https://example2.com/atom.xml" } ] } ]}Sample Code and Getting Started
TypeScript - Feed Management Integration
// Bubo Reader Feed Management
interface FeedItem { title: string; description: string; link: string; pubDate: Date; category: string; source: string;}
interface FeedSource { name: string; url: string;}
interface FeedCategory { category: string; feeds: FeedSource[];}
interface FeedsConfig { feeds: FeedCategory[];}
class BuboFeedManager { private config: FeedsConfig; private feedItems: FeedItem[] = [];
constructor(configPath: string) { // Load configuration from feeds.json const fs = require('fs'); const rawConfig = fs.readFileSync(configPath, 'utf-8'); this.config = JSON.parse(rawConfig); }
async fetchFeed(feedUrl: string, feedName: string, category: string): Promise<FeedItem[]> { try { const Parser = require('rss-parser'); const parser = new Parser({ customFields: { item: [ ['media:thumbnail', 'thumbnail'], ['content:encoded', 'content'] ] } });
const feed = await parser.parseURL(feedUrl);
return feed.items.map((item: any) => ({ title: item.title || 'Untitled', description: item.contentSnippet || item.summary || '', link: item.link || '', pubDate: new Date(item.pubDate || Date.now()), category: category, source: feedName })); } catch (error) { console.error(`Error fetching feed ${feedName}:`, error); return []; } }
async aggregateFeeds(): Promise<FeedItem[]> { const allItems: FeedItem[] = [];
for (const category of this.config.feeds) { for (const feed of category.feeds) { const items = await this.fetchFeed(feed.url, feed.name, category.category); allItems.push(...items); } }
// Sort by publish date (newest first) allItems.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime());
this.feedItems = allItems; return allItems; }
getFeedsByCategory(category: string): FeedItem[] { return this.feedItems.filter(item => item.category === category); }
getLatestItems(limit: number = 50): FeedItem[] { return this.feedItems.slice(0, limit); }
getItemsBySource(sourceName: string): FeedItem[] { return this.feedItems.filter(item => item.source === sourceName); }
getCategories(): string[] { return this.config.feeds.map(cat => cat.category); }
getFeedsBySource(sourceName: string): FeedSource | undefined { for (const category of this.config.feeds) { const feed = category.feeds.find(f => f.name === sourceName); if (feed) return feed; } return undefined; }
generateHtml(): string { let html = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bubo Reader</title> <style> body { font-family: system-ui, -apple-system, sans-serif; padding: 20px; max-width: 800px; margin: 0 auto; } h1 { color: #333; } .category { margin: 20px 0; } .feed-item { margin: 10px 0; padding: 10px; border-left: 3px solid #007bff; } .feed-title { font-weight: bold; } .feed-source { font-size: 0.9em; color: #666; } a { color: #007bff; text-decoration: none; } a:hover { text-decoration: underline; } </style> </head> <body> <h1>Bubo Reader</h1> `;
const categories = this.getCategories(); for (const category of categories) { const items = this.getFeedsByCategory(category); html += `<div class="category"><h2>${category}</h2>`;
for (const item of items) { html += ` <div class="feed-item"> <div class="feed-title"><a href="${item.link}" target="_blank">${item.title}</a></div> <div class="feed-source">${item.source}</div> <p>${item.description}</p> </div> `; } html += `</div>`; }
html += `</body></html>`; return html; }}
// Usage exampleconst manager = new BuboFeedManager('./config/feeds.json');await manager.aggregateFeeds();const html = manager.generateHtml();console.log('Feed reader HTML generated');Bash - Feed Validation and Deployment Script
#!/bin/bash
# Bubo Reader Feed Validation and Deployment Script
FEEDS_FILE="config/feeds.json"TIMEOUT=5ERROR_COUNT=0SUCCESS_COUNT=0
echo "π¦ Bubo Reader Feed Validator"echo "=============================="echo ""
# Check if feeds.json existsif [ ! -f "$FEEDS_FILE" ]; then echo "β Error: $FEEDS_FILE not found" exit 1fi
echo "Validating JSON syntax..."if jq empty "$FEEDS_FILE" 2>/dev/null; then echo "β JSON syntax is valid"else echo "β JSON syntax is invalid" exit 1fi
echo ""echo "Checking feed URLs..."
# Extract all feed URLs from JSONfeeds=$(jq -r '.feeds[].feeds[].url' "$FEEDS_FILE")
while read -r url; do if [ -z "$url" ]; then continue fi
echo -n "Testing $url ... "
# Check if URL is accessible http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout $TIMEOUT --max-time $TIMEOUT "$url")
if [ "$http_code" = "200" ] || [ "$http_code" = "301" ] || [ "$http_code" = "302" ]; then echo "β OK (HTTP $http_code)" SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) else echo "β Failed (HTTP $http_code)" ERROR_COUNT=$((ERROR_COUNT + 1)) fidone <<< "$feeds"
echo ""echo "=============================="echo "Results:"echo "β Working feeds: $SUCCESS_COUNT"echo "β Failed feeds: $ERROR_COUNT"echo ""
if [ $ERROR_COUNT -gt 0 ]; then echo "β οΈ Warning: Some feeds failed to load" echo "Consider updating or removing broken feed URLs"fi
# Build Bubo Readerecho ""echo "Building Bubo Reader..."if npm install && npm run build:bubo; then echo "β Build completed successfully" echo "" echo "Ready for deployment!"else echo "β Build failed" exit 1ficURL - Feed Testing Examples
# Bubo Reader Feed Testing Examples
# Test feed accessibilitycurl -I https://news.ycombinator.com/rsscurl -I https://css-tricks.com/feed/curl -I https://dev.to/feed
# Get feed content (first 100 lines)curl -s https://news.ycombinator.com/rss | head -100
# Check feed is valid XMLcurl -s https://news.ycombinator.com/rss | xmllint --noout - && echo "Valid feed"
# Get feed metadatacurl -s https://dev.to/feed | grep -E "<title>|<description>" | head -5
# Test multiple feedsfor feed in \ "https://news.ycombinator.com/rss" \ "https://css-tricks.com/feed/" \ "https://dev.to/feed"; do echo "Testing: $feed" curl -s -o /dev/null -w "HTTP Status: %{http_code}\n" "$feed"done
# Monitor feed response timecurl -s -w "Response time: %{time_total}s\n" -o /dev/null https://news.ycombinator.com/rss
# Extract RSS items countcurl -s https://news.ycombinator.com/rss | grep -c "<item>"Feed Management Best Practices
Feed Selection
Choose quality feeds:
- Relevance: Select feeds matching your interests
- Frequency: Balance between fresh content and volume
- Quality: Prefer well-maintained sources
- Reliability: Test feeds before adding
- Diversity: Mix different perspectives
- Source Size: Manage total feed count (10-50 is good)
Feed Organization
Organize feeds effectively:
- Logical Categories: Group related feeds
- Consistent Names: Use clear, descriptive names
- Category Limits: 5-15 feeds per category
- Priority Order: Order feeds by importance
- Regular Review: Remove or update inactive feeds
- Backup Config: Keep feeds.json in version control
Feed Monitoring
Monitor feed health:
- Check Regularly: Verify feeds still work monthly
- Look for Changes: Feed URLs and formats can change
- Monitor Performance: Track build times
- Watch for Errors: Keep deployment logs
- Update Feeds: Remove broken sources
- Document Changes: Note why feeds are added/removed
Automation with GitHub Actions
Automate feed refreshes (optional):
name: Refresh Feedson: schedule: - cron: '0 */4 * * *' # Every 4 hoursjobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - run: npm install - run: npm run build:bubo - uses: actions/upload-artifact@v3 with: name: reader path: publicTroubleshooting
Common Issues and Solutions
Issue: Feed not appearing in reader
Solutions:
- Verify feed URL is correct
- Check feed URL is accessible from internet
- Ensure JSON syntax is valid
- Verify feed format (RSS/Atom/JSON)
- Check Bubo logs for parsing errors
Issue: Build failing
Troubleshooting:
- Check feeds.json for syntax errors
- Verify all feed URLs are valid
- Check Node.js version compatibility
- Review build logs for errors
- Try building locally first
Issue: Slow feed loading
Solutions:
- Reduce number of feeds
- Remove feeds with slow servers
- Limit items per feed if possible
- Check network connectivity
- Consider using CDN for static files
Issue: Feed content not updating
Troubleshooting:
- Feeds only update on build/rebuild
- Trigger new deployment manually
- Check feed source is still active
- Verify feed has new content
- Monitor feed for updates
Issue: Images or media not loading
Solutions:
- Some feeds may not include media
- Check feed format supports images
- Verify media URLs are accessible
- Some feeds require authentication
- Consider alternative feeds with better media
Updating Bubo Reader
To update Bubo Reader to a newer version:
- Update Dockerfile with latest release version
- Commit and push to GitHub
- Klutch.sh automatically rebuilds
- Test feed aggregation after update
- Verify all feeds still work
- Monitor logs for any issues
Use Cases
Developer News Aggregator
- Tech blogs and programming news
- Framework and language updates
- Development tool announcements
- Industry trends and discussions
Researcher Information Gathering
- Academic publication feeds
- Research paper announcements
- Academic conference updates
- Specialist publication feeds
News and Information Hub
- General news sources
- Industry-specific news
- Market and economic updates
- Local and international news
Content Curation
- Blog aggregation
- Content collection by topic
- Personal research material
- Competitive intelligence
Additional Resources
- Bubo Reader GitHub Repository - Source code and releases
- Bubo Reader Official Site - Project information
- Introducing Bubo RSS Blog Post - Project background
- FeedFinder Tool - Locate feed URLs
- W3C Feed Validator - Validate feed formats
- Klutch.sh Getting Started Guide
- Klutch.sh Custom Domains Guide
Conclusion
Deploying Bubo Reader on Klutch.sh provides you with a lightweight, privacy-first RSS feed aggregator that displays the latest content from your favorite sources in a clean, distraction-free interface. With support for multiple feed formats, flexible category organization, zero-database architecture, and minimal resource requirements, Bubo Reader enables you to build a personal information hub tailored to your interests. Klutch.shβs managed infrastructure ensures your feed reader is always available and performant, allowing you to stay informed without the complexity and data harvesting of traditional feed readers.
Start aggregating your feeds today by deploying Bubo Reader on Klutch.sh and experience the joy of minimalist, private feed reading.