Pleroma 2.5.0 with Elixir 1.11 on OpenBSD 7.1
Tags: pleroma, fediverse, elixir, phoenix
Published: 2022-12-27 05:40:18

## Summary

[Pleroma](https://pleroma.social/) is one of the fediverse implementations for microblogging like [Twitter](https://twitter.com/) and [Mastodon](https://joinmastodon.org/). It uses [Elixir](https://elixir-lang.org/) which "runs on [Erlang](https://www.erlang.org/) VM known for creating low-latency, distributed, and fault-tolerant systems".

Here is the default landing view:

![pleroma front page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/816ankzo9u4ukuaf9wbd.png)

The new major minor version, [2.5.0](https://pleroma.social/announcements/2022/12/23/pleroma-release-2.5.0/), was released just last week 🎉🎉🎉

This post shows how to install it on [OpenBSD](https://www.openbsd.org/), the solid unix system.

### Why was OpenBSD 7.1 used instead of 7.2, the latest ?

I found it was easier to use OpenBSD [7.1](https://www.openbsd.org/71.html) in order to install Pleroma, for Elixir in [ports](https://www.openbsd.org/faq/ports/ports.html) was available.

On [7.2](https://www.openbsd.org/72.html), the mix task below failed with Elixir in Ports:

```console
$ env LC_ALL=en_US.UTF-8 MIX_ENV=prod \
    mix phx.server
```

with the error:

```console
xx:xx:xx.xxx [notice] Application runtime_tools exited: :runtime_tools.start(:normal, []) returned an error: shutdown: failed to start child: :ttb_autostart
    ** (EXIT) an exception was raised:
        ** (UndefinedFunctionError) function :observer_backend.ttb_resume_trace/0 is undefined (module :observer_backend is not available)
            (runtime_tools 1.19) :observer_backend.ttb_resume_trace()
            (runtime_tools 1.19) ttb_autostart.erl:47: :ttb_autostart.init/1
            (stdlib 4.0.1) gen_server.erl:848: :gen_server.init_it/2
            (stdlib 4.0.1) gen_server.erl:811: :gen_server.init_it/6
            (stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
{"Kernel pid terminated",application_controller,"{application_start_failure,runtime_tools,{{shutdown,{failed_to_start_child,ttb_autostart,{undef,[{observer_backend,ttb_resume_trace,[],[]},{ttb_autostart,init,1,[{file,\"ttb_autostart.erl\"},{line,47}]},{gen_server,init_it,2,[{file,\"gen_server.erl\"},{line,848}]},{gen_server,init_it,6,[{file,\"gen_server.erl\"},{line,811}]},{proc_lib,init_p_do_apply,3,[{file,\"proc_lib.erl\"},{line,240}]}]}}},{runtime_tools,start,[normal,[]]}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,runtime_tools,{{shutdown,{failed_to_start_child,ttb_autostart,{undef,[{observer_backend,ttb_resume_trace,[],[]},{ttb_autostart,init,1,[{file,"ttb_autostart.erl"},{line,47}]},{gen_server,init_it,2,[{file,"gen_server.erl"},{line,848}]},{gen_server,init_it,6,[{file,"gen_server.erl"},{line,811}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]}}},{runtime_tools,start,[normal,[]]}}})

Crash dump is being written to: erl_crash.dump...done
```

It's possibly because of the version of Elixir offered by 7.2's ports, 1.14.

### Environment

- __OpenBSD__ 7.1 (_released on_ Apr 21, 2022)
- [__PostgreSQL__](https://www.postgresql.org/) [14.5](https://www.postgresql.org/docs/release/14.5/) (_released on_ Aug 11, 2022)
- __Erlang/OTP__ 21.2.5 (_released on_ Feb 4, 2019)
- __Elixir__ 1.11.4 (_released on_ May 16, 2021)
- __Pleroma__ (_released on_ Dec 23, 2022)
  - [Backend](https://git.pleroma.social/pleroma/pleroma) version: 2.5.0
  - [Frontend](https://git.pleroma.social/pleroma/pleroma-fe) version: 3a507ba9b 

## Tutorial

### Prepare database / application engine / user

You have to set up the database, the application engine and the service (daemon) user.

#### Install PostgreSQL server (Optional)

Skip this section, if the database server already runs \* _with postgresql-contrib, its extensions_ \* in your environment.

Install PostgreSQL and the extensions:

```console
$ doas pkg_add postgresql-server \
    postgresql-contrib
```

The output was:

```console
quirks-5.5 signed on 2022-10-18T12:24:43Z
postgresql-server-14.5:libxml-2.9.13p2: ok
postgresql-server-14.5:postgresql-client-14.5: ok
useradd: Warning: home directory `/var/postgresql' doesn't exist, and -m was not specified
postgresql-server-14.5: ok
postgresql-contrib-14.5: ok
Running tags: ok
The following new rcscripts were installed: /etc/rc.d/postgresql
See rcctl(8) for details.
New and changed readme(s):
	/usr/local/share/doc/pkg-readmes/postgresql-server
```

Then, let's initialize the database system.
Act as `_postgresql`:

```console
$ doas su _postgresql -
```

to run:

```console
$ initdb -D /var/postgresql/data -U postgres
```

The output was:

```console
The files belonging to this database system will be owned by user "_postgresql".
This user must also own the server process.

The database cluster will be initialized with locale "C".
The default database encoding has accordingly been set to "SQL_ASCII".
The default text search configuration will be set to "english".

Data page checksums are disabled.

creating directory /var/postgresql/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 20
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Tokyo
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    pg_ctl -D /var/postgresql/data -l logfile start

```

Done.

```console
$ exit
```

Finally, activate the daemon and start it:

```console
$ doas rcctl enable postgresql
$ doas rcctl start postgresql
```

#### Install Elixir

Thankfully, the ports system offers the application environment required:

```console
$ doas pkg_add elixir
```

The output was:

```console
quirks-5.5 signed on 2022-10-18T12:24:43Z
elixir-1.11.4p0:erlang-21.2p5v0: ok
elixir-1.11.4p0: ok
--- +erlang-21.2p5v0 -------------------
You may wish to add /usr/local/lib/erlang21/man to /etc/man.conf
```

#### Install the packages for Pleroma

Also, the ports system helps us.

Install the essential packages:

```console
$ doas pkg_add gmake \
    git cmake libmagic
```

Additionally, there are optional ones:

```
$ doas pkg_add ImageMagick \
    ffmpeg p5-Image-ExifTool
```

The output of the former was:

```console
quirks-5.5 signed on 2022-10-18T12:24:43Z
gmake-4.3: ok
git-2.35.1p0:cvsps-2.1p2: ok
(...)
git-2.35.1p0: ok
cmake-3.20.3p6v0:libuv-1.44.1: ok
(...)
cmake-3.20.3p6v0: ok
libmagic-5.43: ok
The following new rcscripts were installed: /etc/rc.d/gitdaemon
See rcctl(8) for details.
New and changed readme(s):
	/usr/local/share/doc/pkg-readmes/git
```

That of the latter was:

```console
quirks-5.5 signed on 2022-10-18T12:24:43Z
ImageMagick-6.9.12.38:x265-3.5p0: ok
(...)
ImageMagick-6.9.12.38: ok
ffmpeg-4.4.1p3v1:fribidi-1.0.11: ok
(...)
ffmpeg-4.4.1p3v1: ok
p5-Image-ExifTool-12.40: ok
Running tags: ok
New and changed readme(s):
	/usr/local/share/doc/pkg-readmes/ffmpeg
	/usr/local/share/doc/pkg-readmes/sdl2
```

#### Create user and project directory

Create the app user used in a part of [`rc.d`](https://man.openbsd.org/rc.d.8), the daemon script:

```console
$ doas useradd \
    -d /var/www/_pleroma -m _pleroma
```

Here, We define `/var/www/_pleroma` as the home directory which is automatically created.

Create the project directory next to it and set the permissions:

```console
$ doas mkdir /var/www/pleroma

$ doas chown -R \
    _pleroma:_pleroma /var/www/pleroma
```

Finally, extend the login class of the user, because the default settings may be too small to run Elixir [Phoenix](https://www.phoenixframework.org/) apps:

```console
$ doas nvim /etc/login.conf
```

like below:

```diff
+ _pleroma:\
+         :datasize-max=1536M:\
+         :datasize-cur=1536M:\
+         :openfiles-max=4096
```

Then apply it:

```console
$ doas cap_mkdb /etc/login.conf
```

As a reference, the results were:

```console
$ ls -l /etc/login*
-rw-r--r--  1 root  wheel   2785 Dec 26 21:02 /etc/login.conf
-rw-r--r--  1 root  wheel  73728 Dec 26 21:02 /etc/login.conf.db
(...)
```

### Install Pleroma

Here is the main part at last.

#### Assume the current directory and get the source

Act as:

```console
$ doas su _pleroma -
```

[`ksh`](https://man.openbsd.org/ksh) will be perhaps used as login shell.

Well, go to the project directory:

```
$ cd /var/www/pleroma
```

Get the latest stable source in the current directory:

```console
$ git clone -b stable \
    https://git.pleroma.social/pleroma/pleroma.git \
    .
```

The output was:

```console
Cloning into '.'...
remote: Enumerating objects: 155563, done.
remote: Counting objects: 100% (1606/1606), done.
remote: Compressing objects: 100% (562/562), done.
remote: Total 155563 (delta 1131), reused 1458 (delta 1040), pack-reused 153957
Receiving objects: 100% (155563/155563), 198.25 MiB | 6.12 MiB/s, done.
Resolving deltas: 100% (118994/118994), done.
```

#### Mix task: deps.get

Get the dependencies:

```console
$ mix deps.get
```

You will be asked to install Hex. Go on with "y" entered:

```console
warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
!!! RUNNING IN LOCALHOST DEV MODE! !!!
FEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs
Could not find Hex, which is needed to build dependency :phoenix
Shall I install Hex? (if running non-interactively, use "mix local.hex --force") [Yn] y
```

The output was:

```console
* creating /var/www/_pleroma/.mix/archives/hex-2.0.0
* Getting gettext (https://github.com/tusooa/gettext.git - 72fb2496b6c5280ed911bdc3756890e7f38a4808)
remote: Enumerating objects: 4046, done.        
remote: Counting objects: 100% (254/254), done.        
remote: Compressing objects: 100% (130/130), done.        
remote: Total 4046 (delta 121), reused 213 (delta 100), pack-reused 3792        
(...)
Resolving Hex dependencies...
Resolution completed in 1.875s
Unchanged:
  accept 0.3.5
(...)
  websockex 0.4.3
* Getting phoenix (Hex package)
(...)
* Getting phoenix_pubsub (Hex package)
You have added/upgraded packages you could sponsor, run `mix hex.sponsor` to learn more
```

#### Fix elixir-captcha which is incompatible with OpenBSD (Optional)

[`elixir-captcha`](https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha) 0.1.0 is brought in the previous section, which incompatible with OpenBSD, for it uses `make` in any envs. If you want to use the native captcha of Pleroma, you have to tell it to use `gmake` instead. Edit:

```console
$ nvim deps/captcha/mix.exs
```

to:

```diff
  defmodule Mix.Tasks.Compile.Make do
    def run(_) do
-     {result, _error_code} = System.cmd("make", [], stderr_to_stdout: true)
+     {result, _error_code} = System.cmd("gmake", [], stderr_to_stdout: true)
      Mix.shell().info(result)
  (...)
  defmodule Mix.Tasks.Clean.Make do
    def run(_) do
-     {result, _error_code} = System.cmd("make", ['clean'], stderr_to_stdout: true)
+     {result, _error_code} = System.cmd("gmake", ['clean'], stderr_to_stdout: true)
      Mix.shell().info(result)
```

Otherwise, you will get the error below, which is not fatal, in running `mix pleroma.instance gen`:

```console
==> captcha
rm -f priv/captcha src/captcha.o 
cc -g  -c src/captcha.c
mkdir -p priv
cc -I src -o priv/captcha  src/captcha.o 
cc: error: no such file or directory: 'src/captcha.o'
cc: error: no input files
*** Error 1 in /var/www/pleroma/deps/captcha (Makefile:10 'priv/captcha')
```

#### Mix task: pleroma.instance gen

Run to generate Pleroma instance:

```console
$ env MIX_ENV=prod \
    mix pleroma.instance gen
```

You will be asked to install rebar3. Go on with "y" entered as well:

```console
warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
warning: `config/prod.secret.exs` not found. You may want to create one by running `mix pleroma.instance gen`

Could not find "rebar3", which is needed to build dependency :parse_trans
I can install a local copy which is just used by Mix
Shall I install rebar3? (if running non-interactively, use "mix local.rebar --force") [Yn] y
```

The output was:

```console
* creating /var/www/_pleroma/.mix/rebar
* creating /var/www/_pleroma/.mix/rebar3
===> Analyzing applications...
===> Compiling parse_trans
(...)
==> pleroma
Compiling 592 files (.ex)
(...)
Generated pleroma app
```

#### Configure app

Then you will be asked as below. It's actually up to you.

```console
What domain will your instance use? (e.g pleroma.soykaf.com) []  pleroma-on-openbsd.com
What is the name of your instance? (e.g. The Corndog Emporium) [pleroma-on-openbsd.com]  
What is your admin email address? []  ciao@pleroma-on-openbsd.com
What email address do you want to use for sending email notifications? [ciao@pleroma-on-openbsd.com]  
Do you want search engines to index your site? (y/n) [n]  
Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n) [n]  
What is the hostname of your database? [localhost]  127.0.0.1
What is the name of your database? [pleroma]  
What is the user used to connect to your database? [pleroma]  
What is the password used to connect to your database? [autogenerated]  
Would you like to use RUM indices? [n]  
What port will the app listen to (leave it if you are using the default setup with nginx)? [4000]  
What ip will the app listen to (leave it if you are using the default setup with nginx)? [127.0.0.1]  
What directory should media uploads go in (when using the local uploader)? [uploads]  
What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)? [instance/static/]  
Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n) [y]  
Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n) [y]  
Do you want to anonymize the filenames of uploads? (y/n) [n]  y
Do you want to deduplicate uploaded files? (y/n) [n]  
Writing config to config/generated_config.exs.
Writing the postgres script to config/setup_db.psql.
Writing /var/www/pleroma/instance/static/robots.txt.

 All files successfully written! Refer to the installation instructions for your platform for next steps.
```

Generating instance is completed !! Prepare your own custom config file:

```console
$ cp config/generated_config.exs \
    config/prod.secret.exs
```

#### Customize app (Optional)

In case, you might need do edit it:

```console
$ nvim config/prod.secret.exs
```

For example, when your PostgreSQL server is in an external host and moreover requires TLS/SSL connections:

```diff
  config :pleroma, Pleroma.Repo,
    (...)
-   hostname: "(your-hostname)"
+   hostname: "(your-hostname)",
+   port: xxx,
+   ssl: true
  (...)
+ config :pleroma, Pleroma.Captcha,
+   enabled: true
```

Also, when you want use [kocaptcha](https://github.com/koto-bank/kocaptcha) instead of native captcha:

```diff
+ config :pleroma, Pleroma.Captcha,
+   enabled: true,
+   method: Pleroma.Captcha.Kocaptcha
```

#### Migrate database

Let's set up the database of Pleroma.
Run:

```console
$ psql -U postgres -f config/setup_db.psql
```

The output was:

```console
CREATE ROLE
CREATE DATABASE
You are now connected to database "pleroma" as user "postgres".
CREATE EXTENSION
CREATE EXTENSION
CREATE EXTENSION
```

Then run to migrate:

```console
$ env MIX_ENV=prod \
    mix ecto.migrate
```

The output was:

```console
warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
Compiling 592 files (.ex)
(...)
xx:xx:xx.xxx [info]  == Migrated 20221103014728 in 0.0s

xx:xx:xx.xxx [info]  == Running 20221111164213 Pleroma.Repo.Migrations.DeprecateQuack.up/0 forward

xx:xx:xx.xxx [info]  == Migrated 20221111164213 in 0.0s
```

#### Mix task: Start Phoenix server

We are almost ready. Start Phoenix:

```console
$ env LC_ALL=en_US.UTF-8 MIX_ENV=prod \
    mix phx.server
```

The output was:

```console
xx:xx:xx.xxx [warning] Description: 'Authenticity is not established by certificate path validation'
     Reason: 'Option {verify, verify_peer} and cacertfile/cacerts is missing'


xx:xx:xx.xxx [info] tzdata release in place is from a file last modified Wed, 21 Oct 2020 18:40:20 GMT. Release file on server was last modified Sat, 29 Oct 2022 01:50:44 GMT.

xx:xx:xx.xxx [warning] The on_load function for module crypt returned:
{:error, {:load, 'Library load-call unsuccessful (1).'}}


xx:xx:xx.xxx [info] Tzdata has updated the release from 2020d to 2022f

xx:xx:xx.xxx [warning] The on_load function for module crypt returned:
{:error, {:load, 'Library load-call unsuccessful (1).'}}


xx:xx:xx.xxx [info] Running Pleroma.Web.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)

xx:xx:xx.xxx [info] Access Pleroma.Web.Endpoint at https://(your-domain)

xx:xx:xx.xxx [info] Gopher server disabled

xx:xx:xx.xxx [info] Transferring embedded hashtags to `hashtags` (from oid: 0)...



xx:xx:xx.xxx [warn] The on_load function for module crypt returned:
{:error, {:load, 'Library load-call unsuccessful (1).'}}

(...)

xx:xx:xx.xxx [info] Running Pleroma.Web.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)

xx:xx:xx.xxx [info] Access Pleroma.Web.Endpoint at https://pleroma-on-openbsd.com

xx:xx:xx.xxx [info] Gopher server disabled

xx:xx:xx.xxx [info] Transferring embedded hashtags to `hashtags` (from oid: 0)...

xx:xx:xx.xxx [info] Deleting context objects from `objects` (from oid: 0)...
```

#### Testing the server

Keep the Phoenix process above (actually acting as Pleroma) running.

Check if the response returns correctly:

```console
$ curl -I http://127.0.0.1:4000/
```

In my case, the response header was:

```
HTTP/1.1 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
access-control-expose-headers: Link,X-RateLimit-Reset,X-RateLimit-Limit,X-RateLimit-Remaining,X-Request-Id,Idempotency-Key
cache-control: max-age=0, private, must-revalidate
content-length: 7880
content-security-policy: upgrade-insecure-requests;script-src 'self' 'wasm-unsafe-eval';connect-src 'self' blob: https://pleroma-on-openbsd.com wss://pleroma-on-openbsd.com;media-src 'self' https:;img-src 'self' data: blob: https:;default-src 'none';base-uri 'self';frame-ancestors 'none';style-src 'self' 'unsafe-inline';font-src 'self';manifest-src 'self';
content-type: text/html; charset=utf-8
date: Mon, 26 Dec 2022 12:35:06 GMT
permissions-policy: interest-cohort=()
referrer-policy: same-origin
server: Cowboy
x-content-type-options: nosniff
x-download-options: noopen
x-frame-options: DENY
x-permitted-cross-domain-policies: none
x-request-id: FzRX_VnDh-YizPQAABaR
x-xss-protection: 1; mode=block
```

Seems sweet :)

When "HTTP/... 200 OK" is printed, the health of your Pleroma is good.

#### Create a Pleroma admin

Note you are in `/var/www/pleroma` __as \_pleroma__.
Run to create a super user:

```
$ env LC_ALL=en_US.UTF-8 MIX_ENV=prod \
    mix pleroma.user new \
    (admin-name) (your@e.mail) --admin
```

You will be asked as below:

```console
A user will be created with the following information:
  - nickname: pleroma_admin
  - email: pleroma_admin@example.com
  - password: [generated; a reset link will be created]
  - name: (admin-name)
  - bio: 
  - moderator: false
  - admin: true

Continue? [n]  y
```

When I proceeded, the output was:

```console
User (admin-name) created
Admin status of (admin-name): true
Generated password reset token for (admin-name)
URL: https://(your-domain))/api/v1/pleroma/password_reset/xxx...
```

#### Start relayd

By default, Pleroma listens to only 127.0.0.1, the localhost.

In order to publish it as service, you have to combine it with some web server. In Pleroma, the cases using [nginx](https://nginx.org/) seem more than [Apache](https://httpd.apache.org/) or [Caddy](https://caddyserver.com/).

OpenBSD develops and supports [relayd](https://man.openbsd.org/relayd.8), their native relay daemon which "runs as a load-balancer, application layer gateway, or transparent proxy".
In order for relayd to relay the requests/responses between the outside and Pleroma inside, edit:

```console
$ doas nvim /etc/relayd.conf
```

The simplest configuration is below:

```conf
table <pleroma_server> { 127.0.0.1 }

relay pleroma {
    listen on egress port 4000

    forward to <pleroma_server> port 4000 check http "/" code 200
}
```

Activate and start it:

```console
$ doas rcctl enable relayd
$ doas rcctl start relayd
```

#### rc.d script

There is another issue left to publish Pleroma as service.
It is not registered as daemon.

To solve it, create the rc.d script:

```console
$ doas nvim /etc/rc.d/pleroma
```

Write the below in it:

```sh
#!/bin/ksh

daemon="cd /var/www/pleroma; env LC_ALL=en_US.UTF-8 MIX_ENV=prod mix"
daemon_user="_pleroma"
daemon_flags="phx.server --no-compile"

. /etc/rc.d/rc.subr

rc_cmd $1
```

Besides, I tried `daemon_execdir="/var/www/pleroma"` instead of `cd ...;` but it didn't work.

Set the permissions:

```console
$ doas chmod a+x /etc/rc.d/pleroma
```

Then activate and run it:

```console
$ doas rcctl enable pleroma
$ doas rcctl start pleroma
```

Well, there is an alternative way. If you don't want to enable it now, you can start it temporarily with `-f` option: The whole command line is `doas rcctl -f start pleroma`.

## Conclusion

Access with your browser to `http://127.0.0.1:4000/`. You will see the front page !!

After signing in, the dashboard must be there 🐡

![pleroma user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jirhgqw1vowp73xiqbow.png)

Enjoy swimming across the fediverse :)

### Reference

- https://docs.pleroma.social/backend/installation/openbsd_en/