cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

main: content/blog/2022-06-07-self-hosting-freshrss.org · raw

  1#+date:        [2022-06-07 Tue 00:00:00]
  2#+title:       Self-Hosting Guide: FreshRSS
  3#+description: How to install FreshRSS with Docker and Nginx.
  4#+slug:        self-hosting-freshrss
  5#+filetags:    :linux:self-hosting:
  6
  7* Why Use Really Simple Syndication (RSS)?
  8
  9After noticing that I have collected 50+ blogs as bookmarks, I decided to
 10migrate back to using RSS feeds to stay up-to-date with my favorite websites.
 11Using RSS allows me to read all of these posts in a single app (on both mobile &
 12desktop) and allows me to be notified when new posts are available.
 13
 14However, I ran into one issue: syncing subscriptions and read/unread posts
 15across devices. Since I want to be able to easily read on both mobile and
 16desktop, I decided to look for a self-hosted RSS solution.
 17
 18Thus, I found [[https://www.freshrss.org/][FreshRSS]] and was able to successfully install it on my server in
 19about 30 minutes.
 20
 21* Documentation
 22
 23While it's certainly not robust, the [[https://freshrss.github.io/FreshRSS/][FreshRSS documentation]] is helpful for
 24figuring out basic information about the service.
 25
 26However, I wanted to install this service as a Docker container and stumbled
 27across the [[https://github.com/FreshRSS/FreshRSS/tree/edge/Docker][Docker README]] within the GitHub repository.
 28
 29This README was the documentation I actually needed. However, as you'll see
 30below, I still had to manually edit one file (=config.php=) to access the API
 31externally via my RSS apps.
 32
 33* Installation
 34
 35** DNS
 36
 37The first step, as required by any external web service, was assigning a domain
 38name to use. I chose to use a subdomain, like =rss.example.com=.
 39
 40To assign this, I created an =A= record in my DNS settings with the IPv4
 41(Internet Protocol version 4) address of the server and an =AAAA= record with
 42the IPv6 (Internet Protocol version 6) address of the server. Note: assigning an
 43IPv6 (=AAAA=) record is optional, but I like to enable IPV6 for my services.
 44
 45#+begin_src config
 46rss.example.com     A       xxx.xxx.xxx.xxx
 47rss.example.com     AAAA    xxxx:xxxx: ... :xxxx
 48#+end_src
 49
 50** Docker
 51
 52I initially tried to set up a =docker-compose.yml= file with a =.env= file
 53because I prefer to have a file I can look back at later to see how I initially
 54started the container, but it simply wouldn't work for me. I'm not sure why, but
 55I assume I wasn't telling =docker-compose= where the =.env= file was.
 56
 57Regardless, I chose to simply run the service with =docker run=. See the
 58following command for my =docker run= configuration:
 59
 60#+begin_src sh
 61sudo docker run -d --restart unless-stopped --log-opt max-size=10m \
 62  -p 8080:80 \
 63  -e TZ=America/Chicago \
 64  -e 'CRON_MIN=1,31' \
 65  -v freshrss_data:/var/www/FreshRSS/data \
 66  -v freshrss_extensions:/var/www/FreshRSS/extensions \
 67  --name freshrss \
 68  freshrss/freshrss
 69#+end_src
 70
 71This started the container successfully and allowed me to visit the FreshRSS
 72instance at =localhost:8080=.
 73
 74** Fresh RSS Set-Up
 75
 76I *HIGHLY* suggest that you set up your user account prior to exposing this
 77service to the public. It's unlikely that someone is trying to access the exact
 78domain or IP/port you're assigning here, but as soon as you expose this service,
 79the first person to open the URL will be able to create the administrative user.
 80
 81In order to set up your FreshRSS service, open the =localhost:8080= URL in your
 82browser (you may need to use a local internet protocol (IP) instead of
 83=localhost= if you're accessing the page from a different machine on the
 84network - e.g., =192.168.1.20:8080=).
 85
 86Once the page loads, set up your default user with a strong username and
 87password. You may also choose to configure other settings prior to exposing this
 88service.
 89
 90** Nginx Reverse-Proxy
 91
 92In order to access this service outside my home, I needed to set up a
 93reverse-proxy to connect =localhost:8080= to =rss.example.com=.
 94
 95First, I created a new Nginx configuration file:
 96
 97#+begin_src sh
 98sudo nano /etc/nginx/sites-available/rss.example.com
 99#+end_src
100
101Within the configuration file, I pasted the following code:
102
103#+begin_src config
104upstream freshrss {
105        server 127.0.0.1:8080;
106        keepalive 64;
107}
108
109server {
110        server_name rss.example.com;
111        listen 80;
112
113        location / {
114                # The final `/` is important.
115                proxy_pass http://localhost:8080/;
116                add_header X-Frame-Options SAMEORIGIN;
117                add_header X-XSS-Protection "1; mode=block";
118                proxy_redirect off;
119                proxy_buffering off;
120                proxy_set_header Host $host;
121                proxy_set_header X-Real-IP $remote_addr;
122                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
123                proxy_set_header X-Forwarded-Proto $scheme;
124                proxy_set_header X-Forwarded-Port $server_port;
125                proxy_read_timeout 90;
126
127                # Forward the Authorization header for the Google Reader API.
128                proxy_set_header Authorization $http_authorization;
129                proxy_pass_header Authorization;
130        }
131}
132#+end_src
133
134Finally, restart Nginx and you will be able to access your service via HTTP (Hypertext Transfer Protocol):
135
136#+begin_src sh
137sudo systemctl restart nginx.service
138#+end_src
139
140** HTTPS
141
142However, I don't want to access my RSS feeds via HTTP. I want it available only
143via HTTPS (Hypertext Transfer Protocol Secure). In order to do this, I ran the
144[[https://certbot.eff.org/][certbot]] program to generate SSL (Secure Socket Layer) certificates for me:
145
146#+begin_src sh
147sudo certbot --nginx
148#+end_src
149
150This process will automatically generate an SSL certificate for you and modify
151the Nginx configuration file to include a redirect from HTTP to HTTPS.
152
153* Post-Installation Fixes
154
155At this point, we have a functional FreshRSS website, available from anywhere
156and secured with HTTPS. However, attempting to connect this service to an RSS
157app resulted in many errors regarding unavailable URLs and incorrect
158credentials.
159
160** API Set-Up
161
162First, you need to open your user profile in FreshRSS (=Settings= > =Profile=)
163and set an API password in the field at the bottom. This is the password you
164will need to provide to your RSS apps.
165
166Once that is set and saved, click the link below the API password field to open
167the API (application programming interface) check tool. It should look something
168like =https://localhost:8080/api/= or =https://rss.example.com/api/=.
169
170Within this page, you /should/ see your correct external URL and "PASS" at the
171bottom of each API type. This would mean everything is set up correctly, and you
172can now move on and login to any RSS apps that support self-hosted options.
173
174In my case, the URL showed an internal URL and I had a warning that the
175=base_url= variable may be misconfigured. If this is the case, see the next
176section for a fix.
177
178** Base URL Fix
179
180In order to fix the =base_url= for the API, I opened up my docker container with
181the following command:
182
183#+begin_src sh
184sudo docker exec -it freshrss bash
185#+end_src
186
187Within this container, update the packages and install an editor:
188
189#+begin_src sh
190apt-get update
191apt-get install nano
192#+end_src
193
194Finally, open up =config.php= in the =data= directory:
195
196#+begin_src sh
197nano data/config.php
198#+end_src
199
200Within =config.php=, you will need to update the =base_url= variable and update
201it to match your external URL (uniform resource locator). In my case, I simply
202commented-out the incorrect URL with =//= and added the correct one on a new
203line:
204
205#+begin_src php
206<?php
207    return array (
208        ...
209        //  'base_url' => 'http://localhost:8080',
210        'base_url' => 'https://rss.example.com',
211        ...
212    )
213>
214#+end_src
215
216You can now exit the file with =Ctrl + x=, press =y= to save the file, and then
217click =Enter= to keep the same file name.
218
219Finally, just exit out of the docker container:
220
221#+begin_src sh
222exit
223#+end_src
224
225Next, just restart the container:
226
227#+begin_src sh
228sudo docker restart freshrss
229#+end_src
230
231Voilà! Your API check should now "PASS" and you should be able to use one of the
232API URLs in your RSS apps.
233
234In my case, I use [[https://netnewswire.com][NetNewsWire]] on my desktop and phone.