1---
2title: "Running firefly3 on alpine"
3date: 2022-07-18
4tags: ['alpine', 'linux', 'php', 'nginx']
5---
6
7**Disclaimer:** before starting be aware that I'm not a sysadmin nor I have a
8deep knowledge in security. This is me reporting the steps I did as a learning
9experiment, so take this tutorial as your own risk.
10
11I have a pretty decent knowledge in container technology, I maintain several
12container on my local server for many applications. However I've decided to take
13a step back and learn a bit more how those applications are really deployed and
14kept without containers, and first candidate being Firefly[^1]. I have it
15currently running on container but let's install in a distribution.
16
17For the distro of choice I'll pick alpine, for its small footprint and the use
18of OpenRC (nothing against systemd though), and it will help me later to better
19understand how to properly setup an alpine image on container environment.
20
21*I don't want to extend this tutorial to cover every single part, so for the
22next steps I'll assume that you have a running instance of PostgreSQL and
23Alpine.*
24
25## Dependencies
26
27First we need to install all the necessary packages to get firefly running.
28Let's go through them and check what they are used for.
29
30```shell
31apk add curl tar gzip
32```
33
34cURL is needed to download the source code from Github and tar gzip are for
35extracting the compressed code.
36
37```shell
38apk add composer
39```
40
41 Composer is a dependency manager for PHP. It is required to download the
42 dependencies of the project, as the source code from tar ball does have all its
43 dependencies included.
44
45 Now we need to download the dependencies listed in the site[^2].
46
47```
48 Extra packages
49 Install the following PHP modules:
50 PHP BCMath Arbitrary Precision Mathematics
51 PHP Internationalization extension
52 PHP Curl
53 PHP Zip
54 PHP Sodium
55 PHP GD
56 PHP XML
57 PHP MBString
58 PHP whatever database you're gonna use.
59```
60
61And for those I could gather the following alpine packages:
62
63```shell
64apk add \
65 php8 \
66 php8-curl \
67 php8-zip \
68 php8-sodium \
69 php8-gd \
70 php8-xml \
71 php8-mbstring \
72 php8-bcmath \
73 php8-pgsql
74```
75
76But that is not everything. I don't know if I lack knowledge in the PHP stack
77but the application will later complain about some other missing dependencies.
78Those being:
79
80```shell
81apk add \
82 php8-fileinfo \
83 php8-intl \
84 php8-session \
85 php8-simplexml \
86 php8-tokenizer \
87 php8-xmlwriter \
88 php8-dom \
89 php8-pdo_pgsql \
90 php8-shmop
91```
92
93A tip that may as well help you later. Some of those not listed packages are
94described in their docker repository[^3] and its base image[^4]. It can also
95help with describing some other necessary steps.
96
97As the next step we need to install the pieces of software that will actually
98run the project:
99
100```shell
101apk add nginx php8-fpm
102```
103
104Nginx will act as reverse proxy and php8-fpm will actually run the project. You
105can use lighttpd as well as some others.
106
107## Deploying the code
108
109Now we have all necessary packages, lets download the project into on server,
110grab the latest release from Github, at the time of this writing is `5.7.9`.
111Download into the `/var/www/firefly`. The folder location is kinda up you, I
112think nginx itself has another default folder for its sites, but I always use
113www folder to store the projects.
114
115```shell
116mkdir -p /var/www/firefly
117```
118
119Create the folder then download/extract the source code:
120
121```shell
122curl -SL https://github.com/firefly-iii/firefly-iii/archive/refs/tags/5.7.9.tar.gz | \
123 tar zxC /var/www/firefly --strip-components 1
124```
125
126This piece of code was taken from the dockerfile[^5].
127
128Now move to the `/var/www/firefly` and install its dependencies with composer:
129```shell
130cd /var/www/firefly
131composer install --prefer-dist --no-dev --no-scripts
132```
133
134## Configurations
135
136### Firefly
137
138Firefly makes the process of setting up the connection strings and other
139configuration quite easy. We'll only need to create an `.env` file with all the
140information needed. Fill the information according with your setup:
141
142```ini
143# /var/wwww/firefly/.env
144
145DB_CONNECTION=pgsql
146DB_HOST=localhost
147DB_PORT=5432
148DB_DATABASE=firefly
149DB_USERNAME=admin
150DB_PASSWORD=admin
151APP_KEY=<RANDON_KEY>
152```
153
154To generate a random key just run:
155
156```shell
157head /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 32 && echo
158```
159
160Once you have set it up we need to bootstrap the project. First we need to
161update the cached configuration.
162
163```shell
164php artisan config:cache
165```
166
167Second we need to migrate and seed the database:
168
169```shell
170php artisan firefly-iii:create-database
171php artisan migrate:refresh --seed
172php artisan firefly-iii:upgrade-database
173```
174
175If everything is setup properly the processes finish successfully.
176
177
178### Permission
179
180Now comes the part where we should be careful. So far we (or at least I) have
181been setting up everything as root but that is not ideal. Usually we want to
182restrict as much as possible the permissions of processes, it should only see do
183what it meant to. So to minimize the area of effect of the process we will make
184it run as a user with almost no permission, and for purpose of running the
185php-fpm we will create a `www-data` user. Quite often that user is already
186created and if it is not, run the following command:
187
188```shell
189adduser www-data --disabled-password
190```
191
192Add `--ingroup www-data` if it complains if the groups exists.
193`--disabled-password` is given so we don't allow login with password, because it
194is not meant to be logged with.
195
196Once the user is created we need to change the which user the process runs on.
197By default it uses a `nobody` which is a user with no permission except those
198which every other user has. Update the user given in the
199`/etc/php8/php-fpm.d/www.conf` file.
200
201From:
202```shell
203user = nobody
204group = nobody
205```
206
207To:
208```shell
209user = www-data
210group = www-data
211```
212
213If the `php-fpm8` is running restart it:
214
215```shell
216rc-service php-fpm8 restart
217```
218
219At last we need to recursively update the permission of www folder because
220probably it is owned by root.
221
222```shell
223chown -R www-data:www-data /var/www/
224```
225
226### Nginx
227
228We will need to edit the nginx config file to find and run the project. Add the
229following server inside of `/etc/nginx/http.d/`, by default nginx will read all
230`.conf` inside of that folder. Just like the www folder this is more of a
231personal choice, you have some room to choose where you want to store the config
232file.
233
234```nginx
235# /etc/nginx/http.d/firefly.conf
236
237server {
238 listen 8080;
239 server_name localhost;
240
241
242 root /var/www/firefly/public;
243
244 location ~ \.php$ {
245 try_files $uri $uri/ =404;
246 fastcgi_split_path_info ^(.+\.php)(/.+)$;
247 fastcgi_pass 127.0.0.1:9000;
248 include fastcgi.conf;
249 }
250
251 location / {
252 try_files $uri /index.php$is_args$args;
253 }
254}
255```
256
257This will set up the process in the port 8080. It is just an exemple, adapt it
258to your needs.
259
260### Services
261
262Now that we have everything set up we can start the service to serve firefly:
263
264```shell
265rc-service php-fpm8 start
266rc-service nginx start
267```
268
269`http://localhot:8080/` (or your server's hostname) should be up and running.
270
271And to make autostart:
272```shell
273rc-update add php-fpm8 default
274rc-update add nginx default
275```
276
277## Debugging
278
279In case of error you can add debugging setting to your env file so it will
280nicely return the error.
281
282```ini
283# /var/wwww/firefly/.env
284APP_DEBUG=true
285APP_LOG_LEVEL=debug
286```
287
288## Bonus config with socket
289
290Another thing to look at is where php-fpm is running the service. I think by
291default on alpine it runs on `http://127.0.0.1:9000` but it can also be running
292on a socket, check the `www.conf` file for the `listen` property:
293
294Config for http
295```
296listen = 127.0.0.1:9000
297```
298
299Config for socket
300```
301listen = /run/php-fpm8/fpm.sock
302```
303
304If you want you can set it up to run on socket. You will need to change two
305things. First, update the www.conf file to run the process on a socket, and to
306change the owner of the socket file. This is important so later nginx is capable
307of reading/writing the file. On the `/etc/php8/php-fpm.d/www.conf` update it:
308
309```shell
310listen = /run/php-fpm8/fpm.sock
311listen.owner = nginx
312listen.group = nginx
313listen.mode = 0660
314```
315
316Second, change the nginx to connect to socket instead of an tcp connection,
317update the following property:
318
319```nginx
320fastcgi_pass unix:/run/php-fpm8/fpm.sock;
321```
322
323[^1]: https://www.firefly-iii.org/
324[^2]: https://docs.firefly-iii.org/firefly-iii/installation/self_hosted/?mtm_campaign=docu-internal&mtm_kwd=self_hosted
325[^3]: https://dev.azure.com/Firefly-III/_git/MainImage
326[^4]: https://dev.azure.com/firefly-iii/_git/BaseImage
327[^5]: [https://dev.azure.com/Firefly-III/MainImage/_git/MainImage...](https://dev.azure.com/Firefly-III/MainImage/_git/MainImage?path=/Dockerfile&version=GC520b8f865ea623a8625fe64e9f583406849be91a&line=14&lineEnd=15&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents)