Funkwhale

Updated 20 January 2022

Installing and configuring Funkwhale

Introduction

Funkwhale is a community project that allows you to listen to and share music on a distributed, open network.

Preparing an LXC container

We suggest that you install Funkwhale in a separate container, after having set it up according to the manual.

Installing and configuring PostgreSQL

Install and configure PostgreSQL according to the manual. In the example, replace dbtest with funkwhale, and the test user with funkwhale.

Add extensions for the database under postgres:

psql -U postgres funkwhale -c 'CREATE EXTENSION "unaccent";'

psql -U postgres funkwhale -c 'CREATE EXTENSION "citext";'

Installing system packages

Install the necessary software:

emerge -a dev-db/redis app-arch/unzip media-video/ffmpeg media-libs/libjpeg-turbo dev-libs/libpqxx net-nds/openldap sys-devel/gettext

Launching Redis

Launch Redis and add it to autostart:

/etc/init.d/redis start

rc-update add redis

Adding a system user

Add a user named funkwhale:

mkdir -p /var/calculate/www

useradd -r -s /sbin/nologin -d /var/calculate/www/funkwhale -m funkwhale

Log in as funkwhale:

su - -s /bin/bash funkwhale

Create the necessary directories:

mkdir -p config api data/static data/media data/music front

Fetching Funkwhale sources

Download and extract the latest version of the API:

export FUNKWHALE_VERSION="1.2.1"

curl -L -o "api-$FUNKWHALE_VERSION.zip" "https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/artifacts/$FUNKWHALE_VERSION/download?job=build_api"

unzip "api-$FUNKWHALE_VERSION.zip" -d extracted

mv extracted/api/* api/

rm -rf extracted

rm api-${FUNKWHALE_VERSION}.zip

Download and extract the latest version of the front-end:

curl -L -o "front-$FUNKWHALE_VERSION.zip" "https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/artifacts/$FUNKWHALE_VERSION/download?job=build_front"

unzip "front-$FUNKWHALE_VERSION.zip" -d extracted

mv extracted/front .

rm -rf extracted

rm front-${FUNKWHALE_VERSION}.zip

Installing Python dependencies

Create a virtual environment:

python3 -m venv virtualenv

echo 'source ~/virtualenv/bin/activate' >> ~/.bashrc

source virtualenv/bin/activate

Install the Python dependencies:

pip install wheel

pip install -r api/requirements.txt

Configuring Funkwhale

Download the configuration file and set the privileges:

curl -L -o ~/config/.env "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/master/deploy/env.prod.sample"

chmod 600 ~/config/.env

Create a private key for Django:

openssl rand -base64 45
ev5pFTw2gV4TEOeAmCwDZB87qGeFgvsHxUJt5A7c4SNfgP1HEZMxM5hz/mw+

Edit the configuration file for music.example.org:

~/config/.env
# Replace this by the definitive, public domain you will use for
# your instance
FUNKWHALE_HOSTNAME=music.example.org
FUNKWHALE_PROTOCOL=https
...
EMAIL_CONFIG=smtp://mail.example.org:25
# The email address to use to send system emails.
DEFAULT_FROM_EMAIL=support@example.org
...
# API/Django configuration

# Database configuration
# Examples:
#  DATABASE_URL=postgresql://<user>:<password>@<host>:<port>/<database>
#  DATABASE_URL=postgresql://funkwhale:passw0rd@localhost:5432/funkwhale_database
# Use the next one if you followed Debian installation guide
DATABASE_URL=postgresql://funkwhale:secret@localhost:5432/funkwhale

# Cache configuration
# Examples:
#  CACHE_URL=redis://<host>:<port>/<database>
#  CACHE_URL=redis://localhost:6379/0c
#  With a password:
#  CACHE_URL=redis://:password@localhost:6379/0
#  (the extra semicolon is important)
# Use the next one if you followed Debian installation guide
#
CACHE_URL=redis://127.0.0.1:6379/0

# Where media files (such as album covers or audio tracks) should be stored
# on your system?
# (Ensure this directory actually exists)
MEDIA_ROOT=/var/calculate/www/funkwhale/data/media

# Where static files (such as API css or icons) should be compiled
# on your system?
# (Ensure this directory actually exists)
STATIC_ROOT=/var/calculate/www/funkwhale/data/static

# Generate one using `openssl rand -base64 45`, for example
DJANGO_SECRET_KEY=ev5pFTw2gV4TEOeAmCwDZB87qGeFgvsHxUJt5A7c4SNfgP1HEZMxM5hz/mw+

# In-place import settings
# You can safely leave those settings uncommented if you don't plan to use
# in place imports.
# Typical docker setup:
#   MUSIC_DIRECTORY_PATH=/music  # docker-only
#   MUSIC_DIRECTORY_SERVE_PATH=/srv/funkwhale/data/music
# Typical non-docker setup:
#   MUSIC_DIRECTORY_PATH=/srv/funkwhale/data/music
#   # MUSIC_DIRECTORY_SERVE_PATH= # stays commented, not needed

MUSIC_DIRECTORY_PATH=/var/calculate/www/funkwhale/data/music
MUSIC_DIRECTORY_SERVE_PATH=/var/calculate/www/funkwhale/data/music
...
FUNKWHALE_FRONTEND_PATH=/var/calculate/www/funkwhale/front/dist

Initialize the database:

python api/manage.py migrate

Update the static files:

python api/manage.py collectstatic

Create the superuser:

python api/manage.py createsuperuser
Username: user
Email address: user@example.org
Password: 
Password (again): 
Superuser created successfully.

Quit as funkwhale:

exit

Getting Let's Encrypt certificate

Get the music.example.org domain certificate for Nginx, as described in the manual.

Installing and configuring Nginx

Install and configure the Nginx web server as a reverse proxy according to the manual.

Get the Funkwhale configuration template for Nginx:

export FUNKWHALE_VERSION="1.2.1"

curl -L -o /etc/nginx/funkwhale_proxy.conf "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/funkwhale_proxy.conf"

curl -L -o /etc/nginx/sites-enabled/funkwhale.template "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/nginx.template"

Use the template to configure Nginx:

set -a && source /var/calculate/www/funkwhale/config/.env && set +a

envsubst "`env | awk -F = '{printf \" $%s\", $$1}'`" \
< /etc/nginx/sites-enabled/funkwhale.template \
> /etc/nginx/sites-enabled/funkwhale.conf

Starting Funkwhale

Create OpenRC scripts to manage services.

Here is the script for running the Funkwhale server:

/etc/init.d/funkwhale-server
#!/sbin/openrc-run
# Copyright 2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

name="Funkwhale application server"
description="Funkwhale application server"
command_user=funkwhale
command=/var/calculate/www/funkwhale/virtualenv/bin/gunicorn
FUNKWHALE_API_IP=127.0.0.1
FUNKWHALE_API_PORT=5000
FUNKWHALE_WEB_WORKER=4
command_args="config.asgi:application -w ${FUNKWHALE_WEB_WORKER} -k uvicorn.workers.UvicornWorker -b ${FUNKWHALE_API_IP}:${FUNKWHALE_API_PORT}"
directory=/var/calculate/www/funkwhale/api
pidfile="/run/funkwhale-server.pid"
command_background=true
output_log=/var/log/funkwhale-server.log
error_log=/var/log/funkwhale-server.log

depend() {
        use net postgresql redis
}

start_pre() {
        checkpath -f -o funkwhale -m 0600 /var/log/funkwhale-server.log
}

Here is the Funkwhale celery worker script:

/etc/init.d/funkwhale-worker
#!/sbin/openrc-run
# Copyright 2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

name="Funkwhale celery worker"
description="Funkwhale celery worker"
command_user=funkwhale
command=/var/calculate/www/funkwhale/virtualenv/bin/celery
command_args="-A funkwhale_api.taskapp worker -l INFO"
directory=/var/calculate/www/funkwhale/api
pidfile="/run/funkwhale-worker.pid"
command_background=true
output_log=/var/log/funkwhale-worker.log
error_log=/var/log/funkwhale-worker.log

depend() {
        use net postgresql redis
}

start_pre() {
        checkpath -f -o funkwhale -m 0600 /var/log/funkwhale-worker.log
}

Here is the Funkwhale beat script:

/etc/init.d/funkwhale-beat
#!/sbin/openrc-run
# Copyright 2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

name="Funkwhale celery beat process"
description="Funkwhale celery beat process"
command_user=funkwhale
command=/var/calculate/www/funkwhale/virtualenv/bin/celery
command_args="-A funkwhale_api.taskapp beat -l INFO"
directory=/var/calculate/www/funkwhale/api
pidfile="/run/funkwhale-beat.pid"
command_background=true
output_log=/var/log/funkwhale-beat.log
error_log=/var/log/funkwhale-beat.log

depend() {
        use net postgresql redis
}

start_pre() {
        checkpath -f -o funkwhale -m 0600 /var/log/funkwhale-beat.log
}

Set privileges for running Funkwhale services:

chmod 0755 /etc/init.d/funkwhale-*

Launch Funkwhale services:

/etc/init.d/funkwhale-beat start

/etc/init.d/funkwhale-server start

/etc/init.d/funkwhale-worker start

Add the Funkwhale services to autostart:

rc-update add funkwhale-beat

rc-update add funkwhale-server

rc-update add funkwhale-worker

Updating Funkwhale

First the Funkwhale services:

/etc/init.d/funkwhale-beat stop

/etc/init.d/funkwhale-server stop

/etc/init.d/funkwhale-worker stop

If necessary, switch manually between versions:

Moving to version 1.1.4

To move to version 1.1.4, replace the WSGI server name from daphne to gunicron:

sed -i 's/daphne/gunicorn/;s/command_args=.*/FUNKWHALE_WEB_WORKER=4\ncommand_args="config.asgi:application -w ${FUNKWHALE_WEB_WORKER} -k uvicorn.workers.UvicornWorker -b ${FUNKWHALE_API_IP}:${FUNKWHALE_API_PORT}"/' /etc/init.d/funkwhale-server

Updating the front-end

Log in as funkwhale.

su - -s /bin/bash funkwhale

Download the tarball of the latest version of the front-end (1.2.1):

export FUNKWHALE_VERSION="1.2.1"

curl -L -o "front-$FUNKWHALE_VERSION.zip" "https://dev.funkwhale.audio/funkwhale/funkwhale/builds/artifacts/$FUNKWHALE_VERSION/download?job=build_front"

Delete the current front-end and replace it with the new one, to be extracted:

rm -r front

unzip -o front-${FUNKWHALE_VERSION}.zip

rm front-${FUNKWHALE_VERSION}.zip

Updating the API

Download the tarball of the latest version of the API (1.2.1):

curl -L -o "api-$FUNKWHALE_VERSION.zip" "https://dev.funkwhale.audio/funkwhale/funkwhale/-/jobs/artifacts/$FUNKWHALE_VERSION/download?job=build_api"

Delete the current API and extract the new one to the project directory:

unzip "api-$FUNKWHALE_VERSION.zip" -d extracted

rm -rf api/ && mv extracted/api .

rm -rf extracted

rm api-${FUNKWHALE_VERSION}.zip

Update the dependencies of Python

virtualenv/bin/pip install -r api/requirements.txt

Update the static files of the project:

virtualenv/bin/python api/manage.py collectstatic --no-input

Perform database migration:

virtualenv/bin/python api/manage.py migrate

If necessary, switch manually between versions:

Migrating to version 1.0

When updating to 1.0, rescan the thumbnails:

rm -r data/media/__sized__

virtualenv/bin/python api/manage.py fw media generate-thumbnails

Log out as funkwhale and launch the Funkwhale services.

exit

/etc/init.d/funkwhale-beat start

/etc/init.d/funkwhale-server start

/etc/init.d/funkwhale-worker start

Updating Nginx configuration

Get the latest Funkwhale configuration template (1.2.1) for Nginx:

export FUNKWHALE_VERSION="1.2.1"

curl -L -o /etc/nginx/funkwhale_proxy.conf "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/${FUNKWHALE_VERSION}/deploy/funkwhale_proxy.conf"

curl -L -o /etc/nginx/sites-enabled/funkwhale.template "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/${FUNKWHALE_VERSION}/deploy/nginx.template"

Use the template to configure Nginx:

set -a && source /var/calculate/www/funkwhale/config/.env && set +a

envsubst "`env | awk -F = '{printf \" $%s\", $$1}'`" \
< /etc/nginx/sites-enabled/funkwhale.template \
> /etc/nginx/sites-enabled/funkwhale.conf

For certificates from /etc/nginx/ssl, replace /etc/letsencrypt/live with the newly created funkwhale.conf:

sed -i 's,/etc/letsencrypt/live,/etc/nginx/ssl,g' /etc/nginx/sites-enabled/funkwhale.conf

Reload the Nginx parameters for the modifications to take effect:

/etc/init.d/nginx reload

Recovering Python environment after upgrade

Log in as funkwhale:

su - -s /bin/bash funkwhale

Disable the old virtual environment and delete it:

deactivate

rm -rf ~/virtualenv

Create a new virtual environment:

python3 -m venv virtualenv

source virtualenv/bin/activate

Install the Python dependencies:

pip install wheel

pip install -r api/requirements.txt