content/blog/2022-06-07-self-hosting-freshrss.org

e8577d98faff1bed72ba6f2d204b8b44c6827c23
cleberg.net/content/blog/2022-06-07-self-hosting-freshrss.org rendered · source · history · blame · raw

244 lines · 8028 bytes

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