├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── Dockerfile ├── README.md ├── root ├── etc │ ├── cont-init.d │ │ ├── 10-users │ │ ├── 20-directories │ │ ├── 30-database │ │ ├── 40-funkwhale │ │ └── 50-webserver │ ├── logrotate.d │ │ └── funkwhale │ ├── nginx │ │ ├── funkwhale_proxy.conf │ │ └── funkwhale_proxy_nested.conf │ └── services.d │ │ ├── celery-beat │ │ └── run │ │ ├── celery-worker │ │ └── run │ │ ├── funkwhale │ │ └── run │ │ ├── nginx │ │ └── run │ │ ├── postgres │ │ ├── finish │ │ └── run │ │ └── redis │ │ └── run └── usr │ └── local │ └── bin │ ├── manage │ └── setupdb ├── scripts ├── download-artifact.sh └── download-nginx-template.sh └── src └── .gitkeep /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve this container 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Logs** 14 | Please submit relevant logs between three backticks. 15 | 16 | ``` 17 | # Stdout 18 | docker logs ${CONTAINER_ID} 19 | 20 | # Logs are located in the `/var/log/funkwhale/` directory. 21 | docker exec -it ${CONTAINER_ID} cat /var/log/funkwhale/{gunicorn,celery-worker,celery-beat}.log 22 | ``` 23 | 24 | **Options** 25 | Submit your command options and arguments like volumes, environment variables, and ports. 26 | 27 | **System information** 28 | - OS: (Ubuntu 18.10, Unraid 6, ...) 29 | - Run tool: (just Dockerfile, docker-compose, web GUI, ...) 30 | - Docker version: ... 31 | - Tag: (master, latest, 0.17, ...) 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | src/*.zip 3 | src/api 4 | src/front 5 | src/*.template 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 2 | MAINTAINER thetarkus 3 | 4 | EXPOSE 80 5 | 6 | # 7 | # Installation 8 | # 9 | 10 | ARG arch=amd64 11 | RUN \ 12 | echo 'installing dependencies' && \ 13 | apk add --no-cache \ 14 | shadow \ 15 | gettext \ 16 | git \ 17 | postgresql \ 18 | postgresql-contrib \ 19 | postgresql-dev \ 20 | python3-dev \ 21 | py3-psycopg2 \ 22 | py3-pillow \ 23 | redis \ 24 | nginx \ 25 | make \ 26 | musl-dev \ 27 | gcc \ 28 | unzip \ 29 | libldap \ 30 | libsasl \ 31 | ffmpeg \ 32 | libpq \ 33 | libmagic \ 34 | libffi-dev \ 35 | zlib-dev \ 36 | openldap-dev && \ 37 | \ 38 | \ 39 | echo 'creating users' && \ 40 | adduser -s /bin/false -D -H funkwhale funkwhale && \ 41 | \ 42 | \ 43 | echo 'creating directories' && \ 44 | mkdir -p /app/api /run/nginx /run/postgresql /var/log/funkwhale && \ 45 | chown funkwhale:funkwhale /app/api /var/log/funkwhale && \ 46 | \ 47 | \ 48 | echo 'downloading archives' && \ 49 | wget https://github.com/just-containers/s6-overlay/releases/download/v1.21.7.0/s6-overlay-$arch.tar.gz -O /tmp/s6-overlay.tar.gz && \ 50 | \ 51 | \ 52 | echo 'extracting archives' && \ 53 | cd /app && \ 54 | tar -C / -xzf /tmp/s6-overlay.tar.gz && \ 55 | \ 56 | \ 57 | echo 'setting up nginx' && \ 58 | rm /etc/nginx/conf.d/default.conf && \ 59 | \ 60 | \ 61 | echo 'removing temp files' && \ 62 | rm /tmp/*.tar.gz 63 | 64 | COPY ./src/api/requirements.txt /app/api/requirements.txt 65 | COPY ./src/api/requirements/ /app/api/requirements/ 66 | 67 | RUN \ 68 | ln -s /usr/bin/python3 /usr/bin/python && \ 69 | echo 'fixing requirements file for alpine' && \ 70 | sed -i '/Pillow/d' /app/api/requirements/base.txt && \ 71 | \ 72 | \ 73 | echo 'installing pip requirements' && \ 74 | pip3 install --upgrade pip && \ 75 | pip3 install setuptools wheel && \ 76 | pip3 install -r /app/api/requirements.txt && \ 77 | pip3 install gunicorn uvicorn && \ 78 | pip3 install service_identity 79 | 80 | COPY ./src/api/ /app/api/ 81 | COPY ./src/front /app/front 82 | 83 | 84 | # 85 | # Environment 86 | # https://dev.funkwhale.audio/funkwhale/funkwhale/blob/develop/deploy/env.prod.sample 87 | # (Environment is at the end to avoid busting build cache on each ENV change) 88 | # 89 | 90 | ENV FUNKWHALE_HOSTNAME=yourdomain.funkwhale \ 91 | FUNKWHALE_PROTOCOL=http \ 92 | DJANGO_SETTINGS_MODULE=config.settings.production \ 93 | DJANGO_SECRET_KEY=funkwhale \ 94 | DJANGO_ALLOWED_HOSTS='127.0.0.1,*' \ 95 | DATABASE_URL=postgresql://funkwhale@:5432/funkwhale \ 96 | MEDIA_ROOT=/data/media \ 97 | MUSIC_DIRECTORY_PATH=/music \ 98 | NGINX_MAX_BODY_SIZE=100M \ 99 | STATIC_ROOT=/app/api/staticfiles \ 100 | FUNKWHALE_SPA_HTML_ROOT=/app/front/dist/index.html \ 101 | FUNKWHALE_WEB_WORKERS=1 \ 102 | CELERYD_CONCURRENCY=0 103 | # 104 | # Entrypoint 105 | # 106 | 107 | COPY ./root / 108 | COPY ./src/funkwhale_nginx.template /etc/nginx/funkwhale_nginx.template 109 | ENTRYPOINT ["/init"] 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Funkwhale Docker Container 2 | 3 | [Funkwhale](https://funkwhale.audio/) is a modern, self-hosted, free and open-source music server. 4 | 5 | 6 | ## Usage 7 | ```sh 8 | docker run \ 9 | --name=funkwhale \ 10 | -e FUNKWHALE_HOSTNAME= \ 11 | -e NESTED_PROXY=0 \ 12 | -v :/data \ 13 | -v :/music:ro \ 14 | -p 3030:80 \ 15 | thetarkus/funkwhale 16 | ``` 17 | 18 | 19 | ## Parameters 20 | + `-e PUID` - Optional user ID for volume ownership. 21 | + `-e PGID` - Optional group ID for volume ownership. 22 | + `-e FUNKWHALE_HOSTNAME` - Hostname of your Funkwhale instance. 23 | + `-e NESTED_PROXY` - Set to 1 when container is behind a reverse proxy. 24 | + `-v /data` - Volume to save media files and database. 25 | + `-v /music` - Path to your music. 26 | + `-p 3030:80` - Access Funkwhale on port 3030. 27 | 28 | 29 | ## Instructions 30 | ### Creation 31 | Creation of the container will take a minute or two. The commands in the sections below will not work until the initialization is complete. For any subsequent runs (assuming you are re-using the `/data` volume), there will be no wait time. 32 | 33 | ### Create an initial superuser account 34 | On the creation of your container, you will need to create an account. Running the following command will prompt you for a username, email, and password for your new account. 35 | ```sh 36 | docker exec -it funkwhale manage createsuperuser 37 | ``` 38 | 39 | all this does is run `/usr/local/bin/manage createsuperuser` on the docker 40 | 41 | if you are running on synology docker, in the funkwhale docker window go to Terminal, click the drop-down arrow by create and enter `/bin/sh` 42 | in the terminal prompt, enter `/usr/local/bin/manage createsuperuser` 43 | 44 | 45 | ### Importing Music 46 | To import your music, open your Funkwhale instance in your browser and find the libraries page under "Add content" and create a library. Click the "details" button on your newly created library and get the library ID from the URL bar. It will look similar to the format of: `b8756c0d-839b-461f-b334-583983dc9ead`. 47 | Set the `LIBRARY_ID` environment variable (or replace it inside of the command) with your library ID, then run the command below. 48 | ```sh 49 | # For file structures similar to ./Artist/Album/Track.mp3 50 | docker exec -it funkwhale manage import_files $LIBRARY_ID "/music/**/**/*.mp3" --in-place --async 51 | ``` 52 | For more information see the [Funkwhale docs on importing music](https://docs.funkwhale.audio/importing-music.html). 53 | 54 | ### Running behind a proxy 55 | 56 | In more involved deployments, you may have a reverse proxy in front of the container. 57 | 58 | If it is the case, add the `-e NESTED_PROXY=1` flag to the docker run command, or ensure 59 | `NESTED_PROXY=1` is available in the container environment. 60 | 61 | ### Build this image 62 | This image is built and pushed automatically on `funkwhale/all-in-one`, for all Funkwhale releases and for the development version as well (using the `develop` tag). 63 | 64 | If you want to build it manually, you can run the following: 65 | ```bash 66 | image_name='mycustomimage' # choose a name for the image 67 | version='develop' # replace 'develop' with any tag or branch name 68 | arch='amd64' # set your cpu architecture 69 | 70 | # download Funkwhale front and api artifacts and nginx configuration 71 | ./scripts/download-artifact.sh src/ $version build_front 72 | ./scripts/download-artifact.sh src/ $version build_api 73 | ./scripts/download-nginx-template.sh src/ $version 74 | docker build --build-arg $arch -t $image_name:$version . 75 | ``` 76 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/10-users: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | # uid/gid modification 4 | [[ "$PUID" ]] && usermod -o -u $PUID funkwhale 5 | [[ "$PGID" ]] && groupmod -o -g $PGID funkwhale 6 | 7 | # groups 8 | usermod -aG funkwhale nginx 9 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/20-directories: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | mkdir -p /data 3 | mkdir -p /run/postgresql 4 | mkdir -p /run/nginx 5 | mkdir -p /var/log/funkwhale 6 | mkdir -p /app/api/staticfiles 7 | 8 | # ownership 9 | chown funkwhale:funkwhale /data 10 | chown -R funkwhale:funkwhale /app 11 | chown -R funkwhale:funkwhale /var/log/funkwhale 12 | chown -R funkwhale:funkwhale /run/postgresql 13 | 14 | # permission 15 | chmod -R 0700 /app 16 | chmod -R 0750 /app/api/staticfiles 17 | chmod -R 0750 /app/front 18 | chmod 0750 /app 19 | chmod 0750 /app/api 20 | 21 | chmod -R 0755 /var/log/funkwhale 22 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/30-database: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | s6-setuidgid funkwhale setupdb 3 | chmod 0700 /data/data 4 | chown -R funkwhale:funkwhale /var/log/postgresql 5 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/40-funkwhale: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | echo 'collecting static files' 3 | manage collectstatic --no-input 4 | 5 | echo 'running data migration' 6 | [[ -z $PGDATA ]] && export PGDATA='/data/data' 7 | s6-setuidgid funkwhale pg_ctl start || exit 1 8 | manage migrate 9 | s6-setuidgid funkwhale pg_ctl stop 10 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/50-webserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | if [ "$NESTED_PROXY" = "1" ]; then 3 | echo "Setting up nested proxy conf…" 4 | cp /etc/nginx/funkwhale_proxy_nested.conf /etc/nginx/funkwhale_proxy.conf 5 | fi 6 | _=$ envsubst < /etc/nginx/funkwhale_nginx.template > /etc/nginx/conf.d/funkwhale.conf 7 | -------------------------------------------------------------------------------- /root/etc/logrotate.d/funkwhale: -------------------------------------------------------------------------------- 1 | /var/log/funkwhale/*.log { 2 | notifempty 3 | missingok 4 | } 5 | -------------------------------------------------------------------------------- /root/etc/nginx/funkwhale_proxy.conf: -------------------------------------------------------------------------------- 1 | # global proxy conf 2 | proxy_set_header Host $http_host; 3 | proxy_set_header X-Real-IP $remote_addr; 4 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 5 | proxy_set_header X-Forwarded-Proto $scheme; 6 | proxy_set_header X-Forwarded-Host $http_host; 7 | proxy_set_header X-Forwarded-Port $server_port; 8 | proxy_redirect off; 9 | 10 | # websocket support 11 | proxy_http_version 1.1; 12 | proxy_set_header Upgrade $http_upgrade; 13 | proxy_set_header Connection $connection_upgrade; 14 | -------------------------------------------------------------------------------- /root/etc/nginx/funkwhale_proxy_nested.conf: -------------------------------------------------------------------------------- 1 | # when the container is run behind another proxy, we need different X-Forwarded 2 | # instructions, see https://github.com/thetarkus/docker-funkwhale/issues/19 for 3 | # more info 4 | proxy_set_header Host $host; 5 | proxy_set_header X-Real-IP $remote_addr; 6 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 7 | proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; 8 | proxy_set_header X-Forwarded-Host $http_x_forwarded_host; 9 | proxy_set_header X-Forwarded-Port $http_x_forwarded_port; 10 | proxy_redirect off; 11 | 12 | # websocket support 13 | proxy_http_version 1.1; 14 | proxy_set_header Upgrade $http_upgrade; 15 | proxy_set_header Connection $connection_upgrade; 16 | -------------------------------------------------------------------------------- /root/etc/services.d/celery-beat/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | cd /app/api 3 | exec s6-setuidgid funkwhale celery -f /var/log/funkwhale/celery-beat.log -A funkwhale_api.taskapp beat --pidfile= 4 | -------------------------------------------------------------------------------- /root/etc/services.d/celery-worker/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | cd /app/api 3 | exec s6-setuidgid funkwhale celery -f /var/log/funkwhale/celery-worker.log -A funkwhale_api.taskapp worker --concurrency=$CELERYD_CONCURRENCY 4 | -------------------------------------------------------------------------------- /root/etc/services.d/funkwhale/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | echo 4 | echo '............NNNNNNNNNN............................NNNNNNNNN.............' 5 | echo '............NmmmmmmmmmmmmNN..................NNmmmmmmmmmmmmN............' 6 | echo '............NmmmmmmmmmmmmmmmmmNN.......NNNmmmmmmmmmmmmmmmmm.............' 7 | echo '.............NmmmmmmmmmmmmmmmmmmmmN.NmmmmmmmmmmmmmmmmmmmmmN.............' 8 | echo '..............NmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmN...............' 9 | echo '................NNmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmN.................' 10 | echo '...................NNmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmNN...................' 11 | echo '........................NNNmmmmmmmmmmmmmmmmmmNNN........................' 12 | echo '............................NmmmmmmmmmmmmmNN............................' 13 | echo '..............................NmmmmmmmmmmN..............................' 14 | echo '...............................Nmmmmmmmm................................' 15 | echo '................................mmmmmmmm................................' 16 | echo '................................mmmmmmmN................................' 17 | echo '................................NmmmmmmN................................' 18 | echo '.hsssssm....dsssssh.....ssssss...NNmmN....ssssss.....hsssssd....Nsssssh.' 19 | echo '.hsssssd....Nsssssy.....ysssssd..........dssssss.....ysssssm....msssssh.' 20 | echo '.dsssssh.....ssssssm....msssssshm......Nhssssssm....Nssssss.....hsssssd.' 21 | echo '.Nssssss.....dsssssy.....mssssssssyhhyssssssssm.....ysssssh....NssssssN.' 22 | echo '..hsssssh.....ysssssyN....NhsssssssssssssssshN.....ysssssy.....hsssssy..' 23 | echo '..Nssssssm....Nsssssssm.....NdhssssssssssydN.....myssssssN....mssssssN..' 24 | echo '...dssssssN....NysssssshN.......NmmddmmN.......NhssssssyN....Nssssssd...' 25 | echo '....hssssssm.....dsssssssydN................Ndysssssssd.....mssssssh....' 26 | echo '.....hssssssd.....NhssssssssshdmN......NmdhssssssssshN.....dssssssh.....' 27 | echo '......dssssssym.....NdssssssssssssssssssssssssssssdN.....Nyssssssd......' 28 | echo '.......mssssssshN......mdyssssssssssssssssssssydN......Nhsssssssm.......' 29 | echo '.........dsssssssym........mmdyyyssssssyyhdmN........mysssssssh.........' 30 | echo '..........Nhsssssssshm............................mhsssssssshN..........' 31 | echo '............NhsssssssssydmN..................NmdyssssssssshN............' 32 | echo '...............mhsssssssssssyyhddmmmmmmddhyyssssssssssshm...............' 33 | echo '..................mhyssssssssssssssssssssssssssssssyhm..................' 34 | echo '.....................NmdyssssssssssssssssssssssydmN.....................' 35 | echo '...........................NmmddhhhyyhhhhdmmN...........................' 36 | echo 37 | 38 | cd /app/api 39 | exec s6-setuidgid funkwhale gunicorn config.asgi:application -w ${FUNKWHALE_WEB_WORKERS} -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000 --access-logfile /var/log/funkwhale/gunicorn.log 40 | -------------------------------------------------------------------------------- /root/etc/services.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | exec nginx -g "daemon off;" 3 | -------------------------------------------------------------------------------- /root/etc/services.d/postgres/finish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | exec s6-svscanctl -t /var/run/s6/services 3 | -------------------------------------------------------------------------------- /root/etc/services.d/postgres/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | exec s6-setuidgid funkwhale postgres -D /data/data >> /var/log/postgresql/postgresql.log 2>&1 3 | -------------------------------------------------------------------------------- /root/etc/services.d/redis/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | exec redis-server 3 | -------------------------------------------------------------------------------- /root/usr/local/bin/manage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | cd /app/api 3 | exec s6-setuidgid funkwhale python3 manage.py "$@" 4 | -------------------------------------------------------------------------------- /root/usr/local/bin/setupdb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | [[ "$(whoami)" != 'funkwhale' ]] && echo 'Must be run as `funkwhale` user.' && exit 1 3 | 4 | [[ -z $PGDATA ]] && export PGDATA='/data/data' 5 | [[ -w "$PGDATA/PG_VERSION" ]] && exit 0 6 | 7 | initdb 8 | pg_ctl start 9 | createdb -E 'UTF-8' 10 | psql -c 'CREATE EXTENSION unaccent;' 11 | pg_ctl stop 12 | -------------------------------------------------------------------------------- /scripts/download-artifact.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | dest_dir=$1 5 | artifact_version=$2 6 | artifact_name=$3 7 | repo_url="${REPO_URL-https://dev.funkwhale.audio/funkwhale/funkwhale}" 8 | default_artifact_url="$repo_url/-/jobs/artifacts/$artifact_version/download?job=$artifact_name" 9 | artifact_url=${ARTIFACT_URL-$default_artifact_url} 10 | 11 | mkdir -p $dest_dir 12 | dest_file="$dest_dir/$artifact_version-$artifact_name.zip" 13 | echo "Downloading $artifact_url to $dest_file…" 14 | wget "$artifact_url" -O $dest_file 15 | echo "Unzipping $dest_file to $dest_dir" 16 | unzip -o $dest_file -d $dest_dir 17 | echo 'Done!' 18 | -------------------------------------------------------------------------------- /scripts/download-nginx-template.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | dest_dir=$1 5 | artifact_version=$2 6 | repo_url="${REPO_URL-https://dev.funkwhale.audio/funkwhale/funkwhale}" 7 | default_artifact_url="$repo_url/raw/$artifact_version/deploy/docker.nginx.template" 8 | artifact_url=${ARTIFACT_URL-$default_artifact_url} 9 | 10 | mkdir -p $dest_dir 11 | dest_file="$dest_dir/funkwhale_nginx.template" 12 | echo "Downloading $artifact_url to $dest_file…" 13 | wget "$artifact_url" -O $dest_file 14 | echo 'Fixing file content for use in our docker image…' 15 | sed -i -e 's/api:5000/127.0.0.1:8000/' \ 16 | -e 's/listen 80;/listen 80 default_server;/' \ 17 | -e 's/server_name \${FUNKWHALE_HOSTNAME};/server_name _;/' \ 18 | -e 's/\$http_upgrade/\${_}http_upgrade/' \ 19 | -e 's/\$connection_upgrade/\${_}connection_upgrade/' \ 20 | -e 's|/frontend|/app/front/dist|' $dest_file 21 | 22 | echo 'Done!' 23 | -------------------------------------------------------------------------------- /src/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelmob/docker-funkwhale/a36339198d49f66769d8eebfd247bbcf1aa736af/src/.gitkeep --------------------------------------------------------------------------------