Skip to content

Cloudron package for Funkwhale v2 - federated music streaming platform

Notifications You must be signed in to change notification settings

rmdes/funkwhale-cloudron

Repository files navigation

Funkwhale for Cloudron

Cloudron Funkwhale

Cloudron package for Funkwhale v2 — a self-hosted, federated music streaming platform.

Funkwhale lets you listen, upload, and share music and audio within a decentralized network using ActivityPub federation. It supports the Subsonic API for compatibility with existing music apps.

Overview

This package runs Funkwhale v2 inside a single Cloudron container with four processes:

Process Role Details
nginx Reverse proxy Routes requests to the API, serves frontend SPA and static/media files
gunicorn ASGI web server Django REST API with Uvicorn workers on port 5000
celery worker Task processor Background jobs (imports, federation, transcoding)
celery beat Task scheduler Periodic tasks (federation polling, cleanup)

Cloudron Addons Used

Addon Purpose
PostgreSQL Primary database for users, music metadata, playlists
Redis Cache and Celery message broker
Local Storage Persistent storage for media, music files, and Django static files
Sendmail Outgoing email (password resets, notifications)

Installation

Prerequisites

Build and Install

git clone git@github.com:rmdes/funkwhale-cloudron.git
cd funkwhale-cloudron

# Build the Docker image (requires Docker)
cloudron build

# Install on your Cloudron (replace 'fw' with your desired subdomain)
cloudron install --location fw

After the initial install, use cloudron update for subsequent deployments:

cloudron build
cloudron update --app fw.example.com

Set the memory limit to 1GB in the Cloudron UI under Resource Limits after installation.

Create Admin Account

After installation, open a Web Terminal for the app from the Cloudron dashboard and run:

source /app/code/venv/bin/activate
funkwhale-manage fw users create --superuser --username yourname --email you@example.com --password yourpassword

Note: Funkwhale does not allow "admin" as a username. Change your password after first login.

Architecture

Cloudron (handles TLS, DNS, backups, authentication)
└── Container
    ├── nginx (:8000)           ← Cloudron routes traffic here
    │   ├── /                   → Frontend SPA (Vue.js)
    │   ├── /api/               → gunicorn (:5000)
    │   ├── /federation/        → gunicorn (:5000)
    │   ├── /rest/              → gunicorn (:5000) [Subsonic API]
    │   ├── /.well-known/       → gunicorn (:5000) [WebFinger/nodeinfo]
    │   ├── /media/             → /app/data/media/ (direct serve)
    │   ├── /_protected/media/  → /app/data/media/ (auth via X-Accel-Redirect)
    │   ├── /_protected/music/  → /app/data/music/ (auth via X-Accel-Redirect)
    │   └── /staticfiles/       → /app/data/static/
    ├── gunicorn (:5000)        ← Django ASGI app (main process, PID 1)
    ├── celery worker           ← Background task processing
    └── celery beat             ← Periodic task scheduling

Directory Layout

/app/code/              # Immutable application code (rebuilt on updates)
├── api/                # Django REST API (Python)
├── front/dist/         # Vue.js frontend (pre-built static files)
└── venv/               # Python virtual environment

/app/data/              # Persistent data (survives updates, backed up by Cloudron)
├── config/.secret_key  # Django secret key (auto-generated on first run)
├── media/              # User uploads (avatars, playlist covers, attachments)
├── music/              # Music library files
└── static/             # Django collectstatic output

/app/pkg/               # Package scripts
├── start.sh            # Startup script
├── manage.sh           # Wrapper for funkwhale-manage (used by scheduler)
└── nginx.conf          # nginx config template

How It Works

On every container start, start.sh:

  1. Creates persistent directories under /app/data/ if they don't exist
  2. Generates a Django secret key on first run (persisted in /app/data/config/.secret_key)
  3. Maps Cloudron addon environment variables (CLOUDRON_*) to Funkwhale equivalents (DATABASE_URL, CACHE_URL, FUNKWHALE_HOSTNAME, etc.)
  4. Runs collectstatic and database migrations (idempotent)
  5. Substitutes the app domain into the nginx config
  6. Starts nginx, celery worker, and celery beat as background processes
  7. Starts gunicorn as PID 1 (Cloudron monitors this for health)

Environment Variable Mapping

Cloudron Addon Funkwhale Variable
CLOUDRON_APP_DOMAIN FUNKWHALE_HOSTNAME
CLOUDRON_POSTGRESQL_* DATABASE_URL (constructed as connection string)
CLOUDRON_REDIS_* CACHE_URL, CELERY_BROKER_URL
CLOUDRON_MAIL_SMTP_* EMAIL_CONFIG
CLOUDRON_MAIL_FROM DEFAULT_FROM_EMAIL

Data Migration

If you have an existing Funkwhale instance and want to migrate to Cloudron, follow these steps.

1. Export from your existing instance

# Database dump (on your existing server)
sudo -u postgres pg_dump -Fc funkwhale > funkwhale.dump

# Copy media and music files
tar czf funkwhale-media.tar.gz -C /srv/funkwhale/data media/
tar czf funkwhale-music.tar.gz -C /srv/funkwhale/data music/

2. Transfer files to Cloudron

Copy funkwhale.dump, funkwhale-media.tar.gz, and funkwhale-music.tar.gz to a location accessible from the Cloudron app's terminal (e.g., /tmp/ inside the container).

3. Import into Cloudron

Open a Web Terminal for the Funkwhale app in the Cloudron dashboard:

# Stop background services first
pkill -f celery || true

# Restore the database
pg_restore -h "${CLOUDRON_POSTGRESQL_HOST}" \
  -p "${CLOUDRON_POSTGRESQL_PORT}" \
  -U "${CLOUDRON_POSTGRESQL_USERNAME}" \
  -d "${CLOUDRON_POSTGRESQL_DATABASE}" \
  --clean --if-exists /tmp/funkwhale.dump

# Extract media and music
tar xzf /tmp/funkwhale-media.tar.gz -C /app/data/
tar xzf /tmp/funkwhale-music.tar.gz -C /app/data/

# Fix permissions
chown -R cloudron:cloudron /app/data

# Run migrations in case versions differ
source /app/code/venv/bin/activate
funkwhale-manage migrate --noinput

Restart the app from the Cloudron dashboard after the import.

4. Verify

  • Existing users can log in
  • Music library appears intact
  • Media files (avatars, covers) display correctly
  • Playlists, favorites, and listening history are preserved

Upgrading Funkwhale

To update to a new Funkwhale version:

  1. Edit the FUNKWHALE_VERSION ARG in the Dockerfile
  2. Update upstreamVersion in CloudronManifest.json
  3. Bump version in CloudronManifest.json
  4. Add a changelog entry in CHANGELOG.md
  5. Rebuild and update:
cloudron build
cloudron update --app fw.example.com

Database migrations run automatically on startup.

Features

  • Music Streaming — Upload, organize, and stream your music library
  • Federation — Share and discover music across Funkwhale instances via ActivityPub
  • Subsonic API — Use existing Subsonic-compatible apps (DSub, Ultrasonic, Clementine, etc.)
  • Podcasts — Subscribe to and manage podcast feeds
  • Channels — Publish audio content with RSS feeds
  • Playlists & Radio — Create playlists, favorites, and auto-generated radio stations

Troubleshooting

Check service status

From the Cloudron web terminal:

# Check which processes are running
ps aux

# Check nginx logs
cat /var/log/nginx/error.log

# Check Funkwhale API logs (gunicorn output goes to Cloudron logs)
# View from Cloudron dashboard → App → Logs

Scheduler Tasks

The Cloudron manifest includes a Scheduler/Cron dropdown in the app terminal with 17 Funkwhale management tasks. Click any task in the dropdown to populate the terminal command.

Safe tasks (run automatically on schedule):

Task Schedule Description
clear_sessions Daily 3:23 AM Remove expired Django sessions
clear_expired_tokens Daily 3:43 AM Remove expired OAuth tokens
collect_static Weekly (Sun 4 AM) Rebuild Django static files

On-demand tasks (yearly schedule — use via dropdown, not auto-run):

Task Description
check_preferences Verify instance preferences
rebuild_music_permissions Rebuild library access permissions
run_migrations Apply database migrations

Destructive tasks (split into check/apply pairs):

Check task (dry-run) Apply task (real) Description
prune_library_check prune_library_apply Remove orphaned artists/albums/tracks
prune_non_mbid_check prune_non_mbid_apply Remove tracks without MusicBrainz IDs
prune_skipped_uploads_check prune_skipped_uploads_apply Remove failed/skipped uploads
check_inplace_files_check check_inplace_files_apply Verify in-place imported files
fix_uploads_check fix_uploads_apply Fix upload metadata (mimetype, size, checksum)

Safety note: All _check tasks default to dry-run mode — they show what would change without modifying the database. Only _apply tasks make real changes. Add --help to any command before running it to see available options.

A 00_read_me_first entry is included at the top of the dropdown as a quick reference for this safety model.

Running management commands

Use the manage.sh wrapper to run funkwhale-manage commands with the correct environment. This is what the scheduler tasks use:

/app/pkg/manage.sh <command> [args...]

For example:

/app/pkg/manage.sh prune_library --help
/app/pkg/manage.sh createsuperuser

The wrapper maps all CLOUDRON_* environment variables to their Funkwhale equivalents (DATABASE_URL, CACHE_URL, FUNKWHALE_HOSTNAME, etc.) so you don't need to set them manually.

Alternatively, you can set up the environment by hand in the web terminal:

source /app/code/venv/bin/activate
export DJANGO_SETTINGS_MODULE=config.settings.production
export DJANGO_SECRET_KEY=$(cat /app/data/config/.secret_key)
export DATABASE_URL="postgresql://${CLOUDRON_POSTGRESQL_USERNAME}:${CLOUDRON_POSTGRESQL_PASSWORD}@${CLOUDRON_POSTGRESQL_HOST}:${CLOUDRON_POSTGRESQL_PORT}/${CLOUDRON_POSTGRESQL_DATABASE}"
export CACHE_URL="redis://:${CLOUDRON_REDIS_PASSWORD}@${CLOUDRON_REDIS_HOST}:${CLOUDRON_REDIS_PORT}/0"
funkwhale-manage <command>

Health check fails

The health check endpoint is /api/v2/instance/nodeinfo/2.1/. If the app shows as unhealthy:

  1. Check that gunicorn is running: ps aux | grep gunicorn
  2. Check that nginx is running: ps aux | grep nginx
  3. Test the API directly: curl -s http://localhost:5000/api/v2/instance/nodeinfo/2.1/
  4. Test through nginx: curl -s http://localhost:8000/api/v2/instance/nodeinfo/2.1/

OOM kills / container restarts

Funkwhale runs multiple Python processes, each loading the full Django app (~150MB). The default configuration (2 gunicorn + 2 celery workers + beat + nginx) fits within 1GB. If you experience OOM kills:

  1. Set the memory limit to at least 1GB in Cloudron UI (Resource Limits)
  2. Reduce worker counts via environment variables in start.sh:
    • FUNKWHALE_WEB_WORKERS=1 (gunicorn workers, default: 2)
    • CELERYD_CONCURRENCY=1 (celery workers, default: 2)

Important: Never set CELERYD_CONCURRENCY=0 — Celery will auto-detect CPU cores and spawn too many workers for the container's memory budget.

Music uploads fail

Large file uploads (up to 2GB) are supported. If uploads fail:

  1. Check available disk space: df -h /app/data/
  2. Verify permissions: ls -la /app/data/music/
  3. Check nginx body size limit is applied: grep client_max_body_size /run/nginx.conf

Federation not working

Ensure your Cloudron domain has proper DNS and that /.well-known/ endpoints are accessible:

curl -s https://your-domain/.well-known/nodeinfo
curl -s https://your-domain/.well-known/webfinger?resource=acct:user@your-domain

Development

Building locally with Docker

docker build -t funkwhale-cloudron .
docker run --rm -it funkwhale-cloudron /bin/bash
# Inspect: venv exists, funkwhale-manage available, front/dist has files

Project structure

funkwhale-cloudron/
├── CloudronManifest.json   # Cloudron app metadata, addons, scheduler, health check
├── Dockerfile              # Build: cloudron/base + Funkwhale artifacts + venv
├── start.sh                # Startup: env mapping, migrations, 4-process launch
├── manage.sh               # Wrapper: runs funkwhale-manage with Cloudron env vars
├── nginx.conf              # Internal routing (no TLS — Cloudron handles that)
├── DESCRIPTION.md          # App store listing
├── POSTINSTALL.md          # Post-install instructions
├── CHANGELOG.md            # Version history
└── logo.png                # App icon

Roadmap

  • LDAP/SSO integration (Funkwhale has native LDAP support)
  • OIDC single sign-on via Cloudron
  • Dynamic worker count based on container memory
  • Stable release packaging (currently tracking v2 release candidates)

References

License

MIT

About

Cloudron package for Funkwhale v2 - federated music streaming platform

Topics

Resources

Stars

Watchers

Forks

Contributors 2

  •  
  •