Instance configuration

General configuration is achieved using two type of settings: environment variables and instance settings.

Environment variables

Those are located in your .env file, which you should have created during installation. A full list of available variables can be seen below.

Options from this file are heavily commented, and usually target lower level and technical aspects of your instance, such as database credentials.

Note

You should restart all Funkwhale processes when you change the values on environment variables.

Note

Some characters are unsafe to use in configuration variables that are URLs, such as the user and password in the database and SMTP sections. If those variables contain such characters, they must be urlencoded, for instance using the following command: python3 -c 'import urllib.parse; print(urllib.parse.quote_plus("p@ssword"))

cf. https://github.com/joke2k/django-environ#using-unsafe-characters-in-urls

Instance settings

These settings are stored in the database and do not require a restart of your instance after modification. They typically relate to higher level configuration, such your instance description, signup policy and so on.

You can edit those settings directly from the web application, assuming you have the required permissions. The URL is /manage/settings, and you will also find a link to this page in the sidebar.

If you plan to use acoustid and external imports (e.g. with the YouTube backends), you should edit the corresponding settings in this interface.

Note

If you have any issue with the web application, a management interface is also available for those settings from Django’s administration interface. It’s less user friendly, though, and we recommend you use the web app interface whenever possible.

The URL should be /api/admin/dynamic_preferences/globalpreferencemodel/ (prepend your domain in front of it, of course).

Configuration reference

Pod

FUNKWHALE_HOSTNAME

Hostname of your Funkwhale pod, e.g mypod.audio

FUNKWHALE_PROTOCOL = ''

Protocol end users will use to access your pod, either http or https.

Database and redis

DATABASE_URL

URL to connect to the PostgreSQL database. Examples:

  • postgresql://funkwhale@:5432/funkwhale

  • postgresql://<user>:<password>@<host>:<port>/<database>

  • postgresql://funkwhale:passw0rd@localhost:5432/funkwhale_database

DB_CONN_MAX_AGE = 300

Max time, in seconds, before database connections are closed.

CACHE_URL

URL to your redis server. Examples:

  • redis://<host>:<port>/<database>

  • redis://127.0.0.1:6379/0

  • redis://:password@localhost:6379/0 for password auth (the extra semicolon is important)

  • redis:///run/redis/redis.sock?db=0 over unix sockets

Note

If you want to use Redis over unix sockets, you’ll also need to update CELERY_BROKER_URL

CELERY_BROKER_URL

URL to celery’s task broker. Defaults to CACHE_URL, so you shouldn’t have to tweak this, unless you want to use a different one, or use Redis sockets to connect.

Exemple:

  • redis://127.0.0.1:6379/0

  • redis+socket:///run/redis/redis.sock?virtual_host=0

Accounts and registration

ACCOUNT_EMAIL_VERIFICATION_ENFORCE

Determine wether users need to verify their email address before using the service. Enabling this can be useful to reduce spam or bots accounts, however, you’ll need to configure a mail server so that your users can receive the verification emails, using EMAIL_CONFIG.

Note that regardless of the setting value, superusers created through the command line will never require verification.

USERS_INVITATION_EXPIRATION_DAYS

Expiration delay in days, for user invitations.

DISABLE_PASSWORD_VALIDATORS

Wether to disable password validators (length, common words, similarity with username…) used during regitration.

ACCOUNT_USERNAME_BLACKLIST

List of usernames that will be unavailable during registration.

AUTH_LDAP_ENABLED

Wether to enable LDAP authentication. See LDAP configuration for more information.

Media storage and serving

MEDIA_URL = https://mypod.audio/media/

URL where media files are served. The default value should work fine on most configurations, but could can tweak this if you are hosting media files on a separate domain, or if you host Funkwhale on a non-standard port.

MEDIA_ROOT = /srv/funkwhale/data/media

Where media files (such as album covers or audio tracks) should be stored on your system? (Ensure this directory actually exists)

PROXY_MEDIA = true

Wether to proxy audio files through your reverse proxy. It’s recommended to keep this on, as a way to enforce access control, however, if you’re using S3 storage with AWS_QUERYSTRING_AUTH, it’s safe to disable it.

EXTERNAL_MEDIA_PROXY_ENABLED = True

Wether to proxy attachment files hosted on third party pods and and servers. Keeping this to true is recommended, to reduce leaking browsing information of your users, and reduce the bandwidth used on remote pods.

ATTACHMENTS_UNATTACHED_PRUNE_DELAY = true

Delay in seconds before uploaded but unattached attachements are pruned from the system.

REVERSE_PROXY_TYPE = 'nginx'

Depending on the reverse proxy used in front of your funkwhale instance, the API will use different kind of headers to serve audio files

Allowed values: nginx, apache2

PROTECT_FILES_PATH = '/_protected'

Which path will be used to process the internal redirection to the reverse proxy DO NOT put a slash at the end.

You shouldn’t have to tweak this.

Audio acquisition

MUSIC_DIRECTORY_PATH = None

The path on your server where Funkwhale can import files using in-place import. It must be readable by the webserver and Funkwhale api and worker processes.

On docker installations, we recommend you use the default of /music for this value. For non-docker installation, you can use any absolute path. /srv/funkwhale/data/music is a safe choice if you don’t know what to use.

Note

This path should not include any trailing slash

Warning

You need to adapt your reverse-proxy configuration to serve the directory pointed by MUSIC_DIRECTORY_PATH on /_protected/music URL.

MUSIC_DIRECTORY_SERVE_PATH = None

Default: MUSIC_DIRECTORY_PATH

When using Docker, the value of MUSIC_DIRECTORY_PATH in your containers may differ from the real path on your host. Assuming you have the following directive in your docker-compose.yml file:

volumes:
  - /srv/funkwhale/data/music:/music:ro

Then, the value of MUSIC_DIRECTORY_SERVE_PATH should be /srv/funkwhale/data/music. This must be readable by the webserver.

On non-docker setup, you don’t need to configure this setting.

Note

This path should not include any trailing slash

S3 Storage

AWS_QUERYSTRING_AUTH = False

Whether to include signatures in S3 urls, as a way to enforce access-control.

Defaults to the inverse of PROXY_MEDIA.

AWS_QUERYSTRING_EXPIRE = 3600

Expiration delay, in seconds, of signatures generated when AWS_QUERYSTRING_AUTH is enabled.

AWS_ACCESS_KEY_ID = 'my_access_key'

Access-key ID for your S3 storage.

AWS_SECRET_ACCESS_KEY = 'my_secret_key'

Secret access key for your S3 storage.

AWS_STORAGE_BUCKET_NAME = 'my_bucket'

Bucket name of your S3 storage.

AWS_S3_CUSTOM_DOMAIN = None

Custom domain to use for your S3 storage.

AWS_S3_ENDPOINT_URL = None

If you use a S3-compatible storage such as minio, set the following variable to the full URL to the storage server. Example:

  • https://minio.mydomain.com

  • https://s3.wasabisys.com

AWS_S3_REGION_NAME = None

If you are using Amazon S3 to serve media directly, you will need to specify your region name in order to access files. Example:

  • eu-west-2

AWS_LOCATION = ''

An optional bucket subdirectory were you want to store the files. This is especially useful if you plan to use share the bucket with other services

API configuration

THROTTLING_ENABLED = True

Wether to enable throttling (also known as rate-limiting). Leaving this enabled is recommended especially on public pods, to improve the quality of service.

THROTTLING_RATES = {'anonymous-create': {'description': 'Anonymous POST requests', 'rate': '1000/day'}, 'anonymous-destroy': {'description': 'Anonymous DELETE requests on resource detail', 'rate': '1000/day'}, 'anonymous-list': {'description': 'Anonymous GET requests on resource lists', 'rate': '10000/day'}, 'anonymous-oauth-app': {'description': 'Anonymous OAuth app creation', 'rate': '10/day'}, 'anonymous-reports': {'description': 'Anonymous report submission', 'rate': '10/day'}, 'anonymous-retrieve': {'description': 'Anonymous GET requests on resource detail', 'rate': '10000/day'}, 'anonymous-update': {'description': 'Anonymous PATCH and PUT requests on resource detail', 'rate': '1000/day'}, 'anonymous-wildcard': {'description': 'Anonymous requests not covered by other limits', 'rate': '1000/h'}, 'authenticated-create': {'description': 'Authenticated POST requests', 'rate': '1000/hour'}, 'authenticated-destroy': {'description': 'Authenticated DELETE requests on resource detail', 'rate': '500/hour'}, 'authenticated-list': {'description': 'Authenticated GET requests on resource lists', 'rate': '10000/hour'}, 'authenticated-oauth-app': {'description': 'Authenticated OAuth app creation', 'rate': '10/hour'}, 'authenticated-reports': {'description': 'Authenticated report submission', 'rate': '100/day'}, 'authenticated-retrieve': {'description': 'Authenticated GET requests on resource detail', 'rate': '10000/hour'}, 'authenticated-update': {'description': 'Authenticated PATCH and PUT requests on resource detail', 'rate': '1000/hour'}, 'authenticated-wildcard': {'description': 'Authenticated requests not covered by other limits', 'rate': '2000/h'}, 'fetch': {'description': 'Fetch remote objects', 'rate': '200/d'}, 'jwt-login': {'description': 'JWT token creation', 'rate': '30/hour'}, 'jwt-refresh': {'description': 'JWT token refresh', 'rate': '30/hour'}, 'login': {'description': 'Login', 'rate': '30/hour'}, 'oauth-authorize': {'description': 'OAuth app authorization', 'rate': '100/hour'}, 'oauth-revoke-token': {'description': 'OAuth token deletion', 'rate': '100/hour'}, 'oauth-token': {'description': 'OAuth token creation', 'rate': '100/hour'}, 'password-change': {'description': 'Password change (when authenticated)', 'rate': '20/h'}, 'password-reset': {'description': 'Password reset request', 'rate': '20/h'}, 'password-reset-confirm': {'description': 'Password reset confirmation', 'rate': '20/h'}, 'signup': {'description': 'Account creation', 'rate': '10/day'}, 'subsonic': {'description': 'All subsonic API requests', 'rate': '2000/hour'}, 'verify-email': {'description': 'Email address confirmation', 'rate': '20/h'}}

Throttling rates for specific endpoints and features of the app. You can tweak this if you are encountering to severe rate limiting issues or, on the contrary, if you want to reduce the consumption on some endpoints.

Example:

  • signup=5/d,password-reset=2/d,anonymous-reports=5/d

ADMIN_URL = '^api/admin/'

Path to the Django admin area.

Exemples:

  • ^api/admin/

  • ^api/mycustompath/

EXTERNAL_REQUESTS_VERIFY_SSL = True

Wether to enforce HTTPS certificates verification when doing outgoing HTTP requests (typically with federation). Disabling this is not recommended.

EXTERNAL_REQUESTS_TIMEOUT = 10

Default timeout for external requests.

Federation

FEDERATION_OBJECT_FETCH_DELAY = 4320

Number of minutes before a remote object will be automatically refetched when accessed in the UI.

FEDERATION_DUPLICATE_FETCH_DELAY = 3000

Delay, in seconds, between two manual fetch of the same remote object.

Metadata

TAGS_MAX_BY_OBJ = 30

Maximum number of tags that can be associated with an object. Extra tags will be ignored.

MUSICBRAINZ_HOSTNAME = 'musicbrainz.org'

Use this setting to change the musicbrainz hostname, for instance to use a mirror. The hostname can also contain a port number.

Example:

  • mymusicbrainz.mirror

  • localhost:5000

MUSICBRAINZ_CACHE_DURATION = 300

How long to cache MusicBrainz results, in seconds

Channels and podcasts

PODCASTS_RSS_FEED_REFRESH_DELAY = 86400

Delay in seconds between to fetch of RSS feeds. Reducing this mean you’ll receive new episodes faster, but will require more resources.

PODCASTS_RSS_FEED_MAX_ITEMS = 250

Maximum number of RSS items to load in each podcast feed.

PODCASTS_THIRD_PARTY_VISIBILITY = 'me'

By default, only people who subscribe to a podcast RSS will have access to their episodes. switch to “instance” or “everyone” to change that.

Changing it only affect new podcasts.

Subsonic

SUBSONIC_DEFAULT_TRANSCODING_FORMAT = 'mp3'

Default format for transcoding when using Subsonic API.

Other settings

INSTANCE_SUPPORT_MESSAGE_DELAY = 15

Delay in days after signup before we show the “support your pod” message

FUNKWHALE_SUPPORT_MESSAGE_DELAY = 15

Delay in days after signup before we show the “support Funkwhale” message

MIN_DELAY_BETWEEN_DOWNLOADS_COUNT = 21600

Minimum required period, in seconds, for two downloads of the same track by the same IP or user to be recorded in statistics.

MARKDOWN_EXTENSIONS = ['nl2br', 'extra']

List of markdown extensions to enable.

Cf https://python-markdown.github.io/extensions/

LINKIFIER_SUPPORTED_TLDS = ['audio']

Additional TLDs to support with our markdown linkifier.

User permissions

Funkwhale’s permission model works as follows:

  • Anonymous users cannot do anything unless configured specifically

  • Logged-in users can use the application, but cannot do things that affect the whole instance

  • Superusers can do anything

To make things more granular and allow some delegation of responsibility, superusers can grant specific permissions to specific users. Available permissions are:

  • Manage instance-level settings: users with this permission can edit instance settings as described in Instance settings

  • Manage library: users with this permission can import new music in the instance

  • Manage library federation: users with this permission can ask to federate with other instances, and accept/deny federation requests from other instances

There is no dedicated interface to manage users permissions, but superusers can login on the Django’s admin at /api/admin/ and grant permissions to users at /api/admin/users/user/.

Front-end settings

We offer a basic mechanism to customize the behavior and look and feel of Funkwhale’s Web UI. To use any of the options below, you will need to create a custom JSON configuration file and serve it on https://yourinstanceurl/settings.json.

On typical deployments, this url returns a 404 error, which is simply ignored.

Set-up

First, create the settings file:

cd /srv/funkwhale/

# create a directory for your configuration file
# you can use a different name / path of course
mkdir custom

# populate the configuration file with default values
cat <<EOF > custom/settings.json
{
  "additionalStylesheets": [],
  "defaultServerUrl": null
}
EOF

Once the settings.json file is created, you will need to serve it from your reverse proxy.

If you are using nginx, add the following snippet to your vhost configuration:

location /settings.json {
    alias /srv/funkwhale/custom/settings.json;
}

On apache, add the following to your vhost configuration:

Alias /settings.json /srv/funkwhale/custom/settings.json

Then reload your reverse proxy.

At this point, visiting https://yourinstanceurl/settings.json should serve the content of the settings.json file.

Warning

The settings.json file must be a valid JSON file. If you have any issue, try linting the file with a tool such as https://github.com/zaach/jsonlint to detect potential syntax issues.

Available configuration options

Your settings.json can contain the following options:

Name

Type

Example value

Description

additionalStylesheets

Array of URLs

["https://test/theme.css"] (default: [])

A list of stylesheets URL (absolute or relative) that the web UI should load. see the “Theming” section below for a detailed explanation

defaultServerUrl

URL

"https://api.yourdomain.com" (default: null)

The URL of the API server this front-end should connect with. If null, the UI will use the value of VUE_APP_INSTANCE_URL (specified during build) or fallback to the current domain

Missing options or options with a null value in the settings.json file are ignored.

Theming

To theme your Funkwhale instance, you need:

  1. A CSS file for your theme, that can be loaded by the front-end

  2. To update the value of additionalStylesheets in your settings.json file to point to your CSS file URL

cd /srv/funkwhale/custom
nano settings.json
# append
# "additionalStylesheets": ["/front/custom/custom.css"]
# to the configuration or replace the existing value, if any

# create a basic theming file changing the background to red
cat <<EOF > custom.css
body {
  background-color: red;
}
EOF

The last step to make this work is to ensure your CSS file is served by the reverse proxy.

On nginx, add the following snippet to your vhost config:

location /custom {
    alias /srv/funkwhale/custom;
}

On apache, use the following:

Alias /custom /srv/funkwhale/custom

<Directory "/srv/funkwhale/custom">
  Options FollowSymLinks
  AllowOverride None
  Require all granted
</Directory>

Once done, reload your reverse proxy, refresh Funkwhale in your web browser, and you should see a red background.

Note

You can reference external urls as well in additionalStylesheets, simply use the full urls. Be especially careful with external urls as they may affect your users privacy.

Warning

Loading additional stylesheets and CSS rules can affect the performance and usability of your instance. If you encounter issues with the interfaces and use custom stylesheets, try to disable those to ensure the issue is not caused by your customizations.