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