My RSS feed reader setup#
I run a CommaFeed instance on a free-tier Google Cloud Platform virtual machine. It’s a Google Reader-inspired self-hosted personal RSS reader.
CommaFeed service runs as a SystemD Unit under a separate system user with a home directory.
User setup#
$ lslogins commafeed
Username: commafeed
UID: 112
Gecos field:
Home directory: /home/commafeed
Shell: /usr/sbin/nologin
No login: yes
Primary group: commafeed
GID: 117
Hushed: no
Running processes: 1
Configuration#
Most of the configuration is the default, with the database pointing to an H2 file. I have Redis configured, but I’m not running Redis itself. Go figure.
# ~commafeed/config.yml
# CommaFeed settings
# ------------------
app:
# url used to access commafeed
publicUrl: https://feed.demin.dev/
# wether to allow user registrations
allowRegistrations: false
# create a demo account the first time the app starts
createDemoAccount: false
# put your google analytics tracking code here
googleAnalyticsTrackingCode:
# put your google server key (used for youtube favicon fetching)
googleAuthKey:
# number of http threads
backgroundThreads: 3
# number of database updating threads
databaseUpdateThreads: 1
# settings for sending emails (password recovery)
smtpHost:
smtpPort:
smtpTls: false
smtpUserName:
smtpPassword:
smtpFromAddress:
# wether this commafeed instance has a lot of feeds to refresh
# leave this to false in almost all cases
heavyLoad: false
# minimum amount of time commafeed will wait before refreshing the same feed
refreshIntervalMinutes: 5
# wether to enable pubsub
# probably not needed if refreshIntervalMinutes is low
pubsubhubbub: false
# if enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser
# useful if commafeed is usually accessed through a restricting proxy
imageProxyEnabled: false
# database query timeout (in milliseconds), 0 to disable
queryTimeout: 0
# time to keep unread statuses (in days), 0 to disable
keepStatusDays: 0
# entries to keep per feed, old entries will be deleted, 0 to disable
maxFeedCapacity: 500
# cache service to use, possible values are 'noop' and 'redis'
cache: noop
# announcement string displayed on the main page
announcement:
# Database connection
# -------------------
# for MySQL
# driverClass is com.mysql.jdbc.Driver
# url is jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true
#
# for PostgreSQL
# driverClass is org.postgresql.Driver
# url is jdbc:postgresql://localhost:5432/commafeed
#
# for Microsoft SQL Server
# driverClass is net.sourceforge.jtds.jdbc.Driver
# url is jdbc:jtds:sqlserver://localhost:1433/commafeed;instance=<instanceName, remove if not needed>
database:
driverClass: org.h2.Driver
url: jdbc:h2:/home/commafeed/db;mv_store=false
user: sa
password: sa
properties:
charSet: UTF-8
validationQuery: "/* CommaFeed Health Check */ SELECT 1"
minSize: 1
maxSize: 50
maxConnectionAge: 30m
server:
applicationConnectors:
- type: http
port: 8082
adminConnectors:
- type: http
port: 8084
logging:
level: WARN
loggers:
com.commafeed: INFO
liquibase: INFO
io.dropwizard.server.ServerFactory: INFO
appenders:
- type: console
- type: file
currentLogFilename: log/commafeed.log
threshold: ALL
archive: true
archivedLogFilenamePattern: log/commafeed-%d.log
archivedFileCount: 5
timeZone: EST
# Redis pool configuration
# (only used if app.cache is 'redis')
# -----------------------------------
redis:
host: localhost
port: 6379
password:
timeout: 2000
database: 0
maxTotal: 500
Reverse proxy#
Nginx has a section dedicated to the feed subdomain.
It is nothing fancy, just a proxy pass to a localhost port defined under applicationConnectors
:
server {
server_name feed.demin.dev;
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/demin.dev-0003/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/demin.dev-0003/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
location / {
proxy_pass http://127.0.0.1:8082;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
}
}
You may notice that adminConnectors
are not even exposed.
I don’t think I ever saw what’s there.
SystemD Unit#
The runtime doesn’t use Docker, in part because the VM has only 0.1 CPU and, in part, because I like simple things.
It’s just a commafeed.jar
downloaded from the official release page.
sudo ln -s ~commafeed/commafeed.service /etc/systemd/system/commafeed.service
[Unit]
Description=A bloat-free feed reader
After=local-fs.target network.target
[Service]
User=commafeed
Group=commafeed
WorkingDirectory=/home/commafeed
ExecStart=/usr/bin/java -Djava.net.preferIPv4Stack=true -server -jar commafeed.jar server config.yml
SyslogIdentifier=commafeed
Restart=always
[Install]
WantedBy=multi-user.target
Backup#
I don’t care about the list of links I haven’t read yet enough to automate the database backup. I wouldn’t want to lose the extensive library of feeds I aggregated over many years, though. So my solution is to just export the OPML file whenever I think about how I don’t want to lose it.
The treasure#
Here’s the list of feeds from the OPML file in alphabetical order: