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.