Skip to content

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

  1. Create a New Project

    Log in to your Klutch.sh dashboard and create a new project for your Bubo Reader RSS feed aggregator.

  2. 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.md

    Here’s a Dockerfile for Bubo Reader:

    FROM node:18-alpine
    WORKDIR /app
    # Install system dependencies
    RUN apk add --no-cache \
    curl \
    git \
    build-base
    # Clone Bubo Reader repository
    RUN git clone https://github.com/georgemandis/bubo-rss.git /tmp/bubo && \
    cp -r /tmp/bubo/* .
    # Install dependencies
    RUN npm install
    # Create config directory
    RUN mkdir -p /app/config
    # Copy feeds configuration
    COPY config/feeds.json /app/config/feeds.json
    # Create output directory
    RUN mkdir -p /app/public
    # Expose port
    EXPOSE 3000
    # Build and start server
    ENTRYPOINT ["sh", "-c"]
    CMD ["npm run build:bubo && npx http-server public -p 3000 -c-1"]

    Create a config/feeds.json file 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 .gitignore file:

    node_modules/
    .next/
    build/
    dist/
    out/
    public/
    .env
    .env.local
    *.log
    logs/
    .DS_Store
    .idea/
    *.swp
    *.swo
    .cache/

    Commit and push to your GitHub repository:

    Terminal window
    git init
    git add .
    git commit -m "Initial Bubo Reader RSS feed deployment"
    git remote add origin https://github.com/yourusername/bubo-reader-deploy.git
    git push -u origin main
  3. Create a New App

    In the Klutch.sh dashboard:

    • Click β€œCreate New App”
    • Select your GitHub repository containing the Dockerfile
    • Choose the branch (typically main or master)
    • Klutch.sh will automatically detect the Dockerfile in the root directory
  4. Configure Environment Variables

    Set up environment variables in your Klutch.sh dashboard (optional for basic setup):

    VariableDescriptionExample
    NODE_ENVEnvironment typeproduction
    BUILD_COMMANDCustom build commandnpm run build:bubo
  5. Set 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
  6. 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)
  7. 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:

  1. Edit config/feeds.json in your repository
  2. Add feed details:
    • Category: Group feeds by topic
    • Name: Display name for the feed
    • URL: Direct link to RSS/Atom/JSON feed
  3. Commit and push changes to GitHub
  4. Klutch.sh automatically rebuilds and deploys
  5. New feeds appear in your reader within minutes
  6. Verify feeds load properly
  7. 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:

  1. Visit website and look for RSS icon
  2. Right-click RSS icon and copy link
  3. Or look for /feed, /rss, or /atom URLs
  4. Check website footer for feed links
  5. Use FeedFinder to locate feeds
  6. Test URL in browser to verify
  7. Common patterns: /rss.xml, /feed.json, /atom.xml

Organizing Feeds by Category

Create logical feed categories:

  1. Technology: Programming, development, tech news
  2. News: World news, politics, current events
  3. Personal Blogs: Individual bloggers you follow
  4. Industry: Industry-specific publications
  5. Entertainment: Media, culture, entertainment
  6. Science: Scientific research and discoveries
  7. Business: Business news and analysis

Keep categories focused and manageable.

Feed Updates and Rebuilds

Bubo Reader refreshes feeds on rebuild:

  1. Automatic Rebuild: GitHub push triggers new build
  2. Manual Rebuild: Use Klutch.sh dashboard to redeploy
  3. Scheduled Updates: Use GitHub Actions for regular rebuilds
  4. Build Frequency: Determines content freshness
  5. Performance: Builds take 2-5 minutes typically

Customizing Feed Reader Appearance

Modify feed reader styling:

  1. Access your repository
  2. Edit src/index.html or public/index.html
  3. Modify CSS for colors and layout
  4. Update templates in src/ directory
  5. Commit changes
  6. Klutch.sh automatically rebuilds
  7. Customizations appear in live reader

Removing Problematic Feeds

Handle feeds that no longer work:

  1. Edit config/feeds.json
  2. Remove feed entry or comment it out
  3. Commit and push changes
  4. Klutch.sh rebuilds without that feed
  5. Verify feed removed from reader
  6. Look for alternative feed sources
  7. 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 example
const 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=5
ERROR_COUNT=0
SUCCESS_COUNT=0
echo "πŸ¦‰ Bubo Reader Feed Validator"
echo "=============================="
echo ""
# Check if feeds.json exists
if [ ! -f "$FEEDS_FILE" ]; then
echo "❌ Error: $FEEDS_FILE not found"
exit 1
fi
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 1
fi
echo ""
echo "Checking feed URLs..."
# Extract all feed URLs from JSON
feeds=$(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))
fi
done <<< "$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 Reader
echo ""
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 1
fi

cURL - Feed Testing Examples

Terminal window
# Bubo Reader Feed Testing Examples
# Test feed accessibility
curl -I https://news.ycombinator.com/rss
curl -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 XML
curl -s https://news.ycombinator.com/rss | xmllint --noout - && echo "Valid feed"
# Get feed metadata
curl -s https://dev.to/feed | grep -E "<title>|<description>" | head -5
# Test multiple feeds
for 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 time
curl -s -w "Response time: %{time_total}s\n" -o /dev/null https://news.ycombinator.com/rss
# Extract RSS items count
curl -s https://news.ycombinator.com/rss | grep -c "<item>"

Feed Management Best Practices

Feed Selection

Choose quality feeds:

  1. Relevance: Select feeds matching your interests
  2. Frequency: Balance between fresh content and volume
  3. Quality: Prefer well-maintained sources
  4. Reliability: Test feeds before adding
  5. Diversity: Mix different perspectives
  6. Source Size: Manage total feed count (10-50 is good)

Feed Organization

Organize feeds effectively:

  1. Logical Categories: Group related feeds
  2. Consistent Names: Use clear, descriptive names
  3. Category Limits: 5-15 feeds per category
  4. Priority Order: Order feeds by importance
  5. Regular Review: Remove or update inactive feeds
  6. Backup Config: Keep feeds.json in version control

Feed Monitoring

Monitor feed health:

  1. Check Regularly: Verify feeds still work monthly
  2. Look for Changes: Feed URLs and formats can change
  3. Monitor Performance: Track build times
  4. Watch for Errors: Keep deployment logs
  5. Update Feeds: Remove broken sources
  6. Document Changes: Note why feeds are added/removed

Automation with GitHub Actions

Automate feed refreshes (optional):

name: Refresh Feeds
on:
schedule:
- cron: '0 */4 * * *' # Every 4 hours
jobs:
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: public

Troubleshooting

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:

  1. Update Dockerfile with latest release version
  2. Commit and push to GitHub
  3. Klutch.sh automatically rebuilds
  4. Test feed aggregation after update
  5. Verify all feeds still work
  6. 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

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.