289 lines
15 KiB
HTML
289 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>The Internet Vagabond :: Funkwhale On Linode with Object Storage</title>
|
||
<link type="application/atom+xml" rel="alternate" href="https://www.theinternetvagabond.com/feed.xml" title="The Internet Vagabond" />
|
||
<meta name="description"
|
||
content="Rants of a wandering techy, in search of truth, knowledge, and a decent ping." />
|
||
<meta name="author" content="Bill Niblock" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<link rel="canonical" href="https://www.theinternetvagabond.com/2022/09/27/linode_funkwhale.html" />
|
||
<link rel="stylesheet" type="text/css"
|
||
href="https://www.theinternetvagabond.com/src/styles/corrupt_layout.css" />
|
||
<link rel="stylesheet" type="text/css"
|
||
href="https://www.theinternetvagabond.com/src/styles/corrupt_typog.css" />
|
||
<link rel="icon" type="image/x-icon"
|
||
href="https://www.theinternetvagabond.com/src/images/favicon.ico" />
|
||
<link rel="stylesheet"
|
||
href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css"
|
||
integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY="
|
||
crossorigin="anonymous">
|
||
<script data-goatcounter="https://theinternetvagabond.goatcounter.com/count"
|
||
async src="https://www.theinternetvagabond.com/src/scripts/goatcounter.js"></script>
|
||
</head>
|
||
<body>
|
||
<div class="cor_page">
|
||
<header>
|
||
<a href="/">
|
||
<div>
|
||
<span class="first">T</span>he
|
||
<span class="first">I</span>nternet
|
||
<span class="first">V</span>agabond
|
||
</div>
|
||
</a>
|
||
</header>
|
||
<main>
|
||
<article>
|
||
<h1 id="funkwhale-on-linode-with-object-storage">Funkwhale on Linode with Object Storage</h1>
|
||
|
||
<h2 id="funkwhale-setup">Funkwhale Setup</h2>
|
||
|
||
<p><a href="https://funkwhale.audio/">Funkwhale</a> is a decentralized music service,
|
||
connecting to the <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a> using the
|
||
ActivityPub protocol. It is a web-based application, allowing users to upload,
|
||
listen, and share music and podcasts. I think it’s a cool project, and I can
|
||
self-host it, so I did. For a while, Funkwhale offered an all-in-one Docker
|
||
container, but they shifted focus to a multi-container approach. I had delayed
|
||
my transition from all-in-one to multi-container, but finally this past weekend
|
||
I found myself with time and motivation to get it done. The installation of
|
||
Funkwhale using Docker is very straight forward. The community has developed a
|
||
series of templates that can be fetched, modified, and used to get started very
|
||
quickly and easily. Those instructions are
|
||
<a href="https://docs.funkwhale.audio/installation/docker.html#multi-container-installation">here</a>.
|
||
The only significant modification I made was using <code class="language-plaintext highlighter-rouge">/opt/funkwhale</code> as my
|
||
default data and media root. I keep all my Docker configuration in directories
|
||
in my home directory as well. Much of these changes can be established in the
|
||
<code class="language-plaintext highlighter-rouge">.env</code> file discussed in the installation instructions, but I also scrubbed the
|
||
template files created and used during installation to make sure the directories
|
||
were as I wanted them. I also proxy Funkwhale and many other services behind
|
||
nginx, and there were a few <a href="https://docs.funkwhale.audio/installation/index.html#nginx">additional
|
||
steps</a> I had to
|
||
take. With all that complete, I had transitioned successfully. I already had SSL
|
||
certificates, but if that’s also a requirement, they can easily be provisioned
|
||
using <a href="https://certbot.eff.org/">Certbot</a>.</p>
|
||
|
||
<h2 id="object-storage-setup">Object Storage Setup</h2>
|
||
|
||
<p>In my old setup, I leveraged Funkwhale’s ability to <a href="https://docs.funkwhale.audio/admin/importing-music.html">in-place import
|
||
music</a>. I transferred
|
||
about 70GB worth of music to my VPS (using Syncthing <3), filling the disk
|
||
almost entirely (<em>98%</em>). It was a temporary solution for a road trip, and I
|
||
knew I couldn’t keep it that way for long. Funkwhale has the ability to leverage
|
||
S3-compatible object storage, and Linode, the provider I already use for my VPS,
|
||
offers object storage. Any of the other major cloud providers will also do the
|
||
trick; I just went with what was easiest. On the Linode side, there’s not much
|
||
to it. I created a new bucket, labeled it accordingly, created an access key,
|
||
and that was it. The Funkwhale side proved to be a bit challenging, but not, it
|
||
turns out, due to configuration. Well, <em>technically</em> it was.</p>
|
||
|
||
<p>The relevant configuration options on the Funkwhale side, in the <code class="language-plaintext highlighter-rouge">.env</code> file:</p>
|
||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>## External storages configuration
|
||
# Funkwhale can store uploaded files on Amazon S3 and S3-compatible storages (such as Minio)
|
||
# Uncomment and fill the variables below
|
||
|
||
AWS_ACCESS_KEY_ID=
|
||
AWS_SECRET_ACCESS_KEY=
|
||
AWS_STORAGE_BUCKET_NAME=
|
||
# 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
|
||
# AWS_LOCATION=
|
||
|
||
# If you use a S3-compatible storage such as minio, set the following variable
|
||
# the full URL to the storage server. Example:
|
||
# AWS_S3_ENDPOINT_URL=https://minio.mydomain.com
|
||
AWS_S3_ENDPOINT_URL=
|
||
|
||
# If you want to serve media directly from your S3 bucket rather than through a proxy,
|
||
# set this to false
|
||
# PROXY_MEDIA=false
|
||
|
||
# If you are using Amazon S3 to serve media directly, you will need to specify your region
|
||
# name in order to access files. Example:
|
||
# AWS_S3_REGION_NAME=eu-west-2
|
||
# AWS_S3_REGION_NAME=
|
||
|
||
# If you are using Amazon S3, use this setting to configure how long generated URLs should stay
|
||
# valid. The default value is 3600 (60 minutes). The maximum accepted value is 604800 (7 days)
|
||
|
||
# AWS_QUERYSTRING_EXPIRE=
|
||
|
||
# If you are using an S3-compatible object storage provider, and need to provide a default
|
||
# ACL for object uploads that is different from the default applied by boto3, you may
|
||
# override it here. Example:
|
||
# AWS_DEFAULT_ACL=public-read
|
||
# Available options can be found here: https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl
|
||
|
||
AWS_DEFAULT_ACL=
|
||
</code></pre></div></div>
|
||
|
||
<p>I’ve included the comments. The entire file is commented, and generally easy
|
||
enough to figure out. On the Linode side, when I generated the access key, it
|
||
provided me an <em>Access Key</em> and a <em>Secret Key</em>. I had already created a bucket,
|
||
and so I had the <em>Bucket Name</em>. The challenge for me was what the <em>Endpoint URL</em>
|
||
was, and if I needed to set a <em>Region Name</em> and <em>ACL</em>. Linode’s documentation on
|
||
their object storage offering is a bit anemic, and so I made use of their setup
|
||
instructions for <a href="https://www.linode.com/docs/products/storage/object-storage/guides/s3cmd/">using s3cmd with Linode object
|
||
storage</a>.
|
||
From this guide, I was able to both setup <code class="language-plaintext highlighter-rouge">s3cmd</code>, and also determine what the
|
||
<em>Endpoint URL</em> would be. I also set the <em>Region Name</em> and <em>ACL</em> to match what
|
||
the UI was showing, but I’m still not certain their necessary. Here’s the trick,
|
||
and the cause of a few hours-worth of confusion: restarting the Docker
|
||
containers wasn’t re-reading the <code class="language-plaintext highlighter-rouge">.env</code> file; I had to completely stop and
|
||
re-create them. It wasn’t until I ran <code class="language-plaintext highlighter-rouge">docker inspect funkwhale-docker_api_1</code>
|
||
and noticed the environment variables weren’t set that I figured this out. Could
|
||
be this is common knowledge for Docker-gurus; now I know. With the
|
||
configuration in place, and the containers recreated, I was able to upload files
|
||
through Funkwhale, and watch them be stored in my Linode bucket. My final
|
||
configuration options were as follows:</p>
|
||
|
||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AWS_ACCESS_KEY_ID=<stuff>
|
||
AWS_SECRET_ACCESS_KEY=<secret stuff>
|
||
AWS_STORAGE_BUCKET_NAME=funkwhale-music-bucket-name
|
||
AWS_S3_ENDPOINT_URL=https://us-southeast-1.linodeobjects.com
|
||
AWS_S3_REGION_NAME=us-southeast-1
|
||
AWS_DEFAULT_ACL=public-read
|
||
</code></pre></div></div>
|
||
|
||
<h2 id="next-steps">Next Steps</h2>
|
||
|
||
<p>The downside of Funkwhale’s S3-compatible object storage integration lies in how
|
||
files are uploaded. One cannot simply <del>walk into</del> upload files to the bucket;
|
||
music must be uploaded through Funkwhale’s API (via web or other means), and
|
||
then Funkwhale stores it accordingly (like for local uploads). I have a lot of
|
||
music, and I can’t be asked to manually upload it all. That’s, like, 2 hours of
|
||
half-hearted work. No, instead, the obvious solution is to build a script that
|
||
can automatically upload any new music from my local music directory to
|
||
Funkwhale automatically. What I’m considering now is how I want to do that. I
|
||
could leverage systemd to watch my local music directory, and run the upload
|
||
script whenever new music is uploaded. Could even expand it to remove music
|
||
whenever I delete it locally, though that seems a bit odd. I could instead setup
|
||
a cron or systemd-timer to run at a set interval, and check for any new files
|
||
since the last run, and upload them. Regardless of the trigger, the upload
|
||
functionality should ideally avoid duplicates, run in a non-blocking fashion,
|
||
maybe batch upload files, and be low impact on my desktop. So that’s next.</p>
|
||
|
||
<div class="author_info">
|
||
Bill Niblock
|
||
<a href="https://unlicense.org/"
|
||
aria-label="Code dedicated to the public domain under Unlicense">
|
||
<span class="fa fa-cc-pd" aria-hidden="true"
|
||
title="Code dedicated to the public domain under Unlicense"</span>
|
||
</a>
|
||
<a href="https://creativecommons.org/publicdomain/zero/1.0/"
|
||
aria-label="Published to the public domain under CC0">
|
||
<span class="fa fa-cc-zero" aria-hidden="true"
|
||
title="Content dedicated to the public domain under CC0"</span>
|
||
</a>
|
||
2022-09-27
|
||
<br />
|
||
[
|
||
|
||
<a href="/topics/technology">technology</a>
|
||
|
||
]
|
||
</div>
|
||
</article>
|
||
</main>
|
||
<footer>
|
||
<nav>
|
||
<div><a href="/">home</a></div>
|
||
|
||
<div><a href="/topics/all">all</a></div>
|
||
|
||
<div><a href="/topics/gaming">gaming</a></div>
|
||
|
||
<div><a href="/topics/life">life</a></div>
|
||
|
||
<div><a href="/topics/philosophy">philosophy</a></div>
|
||
|
||
<div><a href="/topics/technology">technology</a></div>
|
||
|
||
<div><a href="/topics/writing">writing</a></div>
|
||
|
||
</nav>
|
||
|
||
<hr />
|
||
|
||
<div><a href="https://www.theinternetvagabond.com/now">Life In Progress</a></div>
|
||
|
||
<hr />
|
||
|
||
<section class="h-card">
|
||
<section class="footer_about" id="about">
|
||
<div>The Site</div>
|
||
<div>
|
||
<a href="https://www.theinternetvagabond.com/feed.xml"
|
||
aria-label="RSS feed for the site">
|
||
<span class="fa fa-rss" aria-hidden="true"
|
||
title="RSS Feed"</span>
|
||
</a> |
|
||
<a href="https://theinternetvagabond.goatcounter.com/"
|
||
aria-label="GoatCounter statistics for the site">
|
||
<span class="fa fa-bar-chart" aria-hidden="true"
|
||
title="GoatCounter Statistics"</span>
|
||
</a> |
|
||
<a href="https://codeberg.org/VagabondAzulien/the-internet-vagabond-dot-com"
|
||
aria-label="Source code repository for the site">
|
||
<span class="fa fa-code" aria-hidden="true"
|
||
title="Site Source Code"</span>
|
||
</a>
|
||
</div>
|
||
<a class="u-url u-uid" href="https://theinternetvagabond.com"></a>
|
||
<p>
|
||
This site is a small slice of internet real-estate that I use for
|
||
occasional writing. Nothing I say is visionary or profound. I
|
||
focus on technology, gaming, and philosophy. All opinions my
|
||
own.
|
||
</p>
|
||
<div>The Vagabond</div>
|
||
<div>
|
||
<a rel="me"
|
||
href="mailto:bill@theinternetvagabond.com"
|
||
aria-label="Email Bill at The Internet Vagabond dot com">
|
||
<span class="fa fa-envelope-o" aria-hidden="true"
|
||
title="Email bill at theinternetvagabond.com"</span>
|
||
</a> |
|
||
<a class="u-url" rel="me"
|
||
href="https://matrix.to/#/@vagabondazulien:matrix.org"
|
||
aria-label="Speak with me on Matrix">
|
||
<span class="fa fa-matrix-org" aria-hidden="true"
|
||
title="Speak with me on Matrix"</span>
|
||
</a> |
|
||
<a class="u-url" rel="me"
|
||
href="https://mastodon.social/@azulien"
|
||
aria-label="Find me on the Fediverse">
|
||
<span class="fa fa-mastodon" aria-hidden="true"
|
||
title="Find me on the Fediverse"</span>
|
||
</a> |
|
||
<a class="u-url" rel="me" href="https://www.twitch.tv/vagabondazulien/profile"
|
||
aria-label="Link to my Twitch channel">
|
||
<span class="fa fa-twitch " aria-hidden="true"
|
||
title="My Twitch channel"</span>
|
||
</a>
|
||
</div>
|
||
<p>
|
||
My name is <span class="p-name">Bill Niblock</span>. <span
|
||
class="p-note">I'm a computer scientist by education, a technologist
|
||
by trade, a gamer by hobby, and a philosopher by accident. I
|
||
live in <span class="p-locality">Buffalo</span>, <span class="p-region">
|
||
New York</span>, <span class="p-country-name">USA</span>.<br />
|
||
<br />
|
||
My PGP Key is <span class="u-key" id="key">CCE7 3682 331B 5614 9FAB
|
||
7383 7359 80B2 6381 C91E</span>.
|
||
</p>
|
||
</section>
|
||
<section style="display: none;">
|
||
<span class="p-category">Gaming</span>
|
||
<span class="p-category">Technology</span>
|
||
<span class="p-category">Philosophy</span>
|
||
<span class="p-category">Open Source Software</span>
|
||
<span class="p-category">Self-Hosting</span>
|
||
<span class="p-category">Coffee</span>
|
||
<span class="u-email">bill@theinternetvagabond.com</span>
|
||
</section>
|
||
</section>
|
||
</footer>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|