{"id":327,"date":"2020-11-12T19:44:56","date_gmt":"2020-11-12T18:44:56","guid":{"rendered":"https:\/\/vdna.be\/blog\/?p=327"},"modified":"2020-11-12T19:44:56","modified_gmt":"2020-11-12T18:44:56","slug":"hosting-your-own-mastodon-instance-via-docker-compose","status":"publish","type":"post","link":"https:\/\/vdna.be\/site\/index.php\/2020\/11\/hosting-your-own-mastodon-instance-via-docker-compose\/","title":{"rendered":"Hosting your own mastodon instance via docker-compose"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-medium\" src=\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/b\/b4\/Mastodon_Mascot.png\/220px-Mastodon_Mascot.png\" alt=\"Mastodon mascotte\" width=\"220\" height=\"225\" \/>Recently I&#8217;ve setup an invite-only mastodon instance as a private space for<\/p>\n<p>friends and family. While I am still evaluating whether mastodon is the right tool for such a private space, I&#8217;d like to share a number of issues I encountered during setup.<!--more--><\/p>\n<h2>Careful with non-official guides<\/h2>\n<p>The <a href=\"https:\/\/docs.joinmastodon.org\/user\/run-your-own\/\">mastodon documentation<\/a> focuses on hosting mastodon on a dedicated machine. It is thorough and I would recommend reading through it, even if you do not want to dedicate a separate machine to hosting mastodon. Mastodon also maintains a <code>docker-compose<\/code> <a href=\"https:\/\/github.com\/tootsuite\/mastodon\/blob\/master\/docker-compose.yml\">config file<\/a>, unfortunately the documentation on setting it up appears lackluster. The best guides I&#8217;ve found details are a <a href=\"https:\/\/www.alibabacloud.com\/blog\/how-to-install-mastodon-using-docker-on-alibaba-cloud-ecs_595049\">alibaba cloud<\/a> guide and a <a href=\"https:\/\/www.howtoforge.com\/how-to-install-mastodon-social-network-with-docker-on-ubuntu-1804\/\">howtoforge guide<\/a>. Note these guides aren&#8217;t perfect: the official docs recommend running the latest tagged releases whereas both guides run from the master branch, generating the necessary secrets is done differently than in the official documentation and I also don&#8217;t like how they automate certificate renewal.<\/p>\n<h2>Issues related to the db service<\/h2>\n<p>After initial tweaking of the <code>.env.production<\/code> file I encountered two issues related to the db service. Fortunately I stumbled upon this <a href=\"https:\/\/stackoverflow.com\/questions\/63126698\/postgres-wont-start-when-installing-docker-mastodon\">stackoverflow answer<\/a>. I recap my findings belows.<\/p>\n<h3>Issue #1<\/h3>\n<p><code>rails aborted! PG::ConnectionBad: could not connect to server: No such file or directory<\/code><\/p>\n<p>This is caused by the web service being unable to connect to the database. In my case this was due to an invalid value for <code>DB_HOST<\/code>\u00a0 in <code>.env.production<\/code>. Setting <code>DB_HOST<\/code> to <code>mastodon_db_1<\/code> solved this issue for me. The resulting postgres section looks as follows:<\/p>\n\n\n<pre class=\"wp-block-code\"><code># PostgreSQL\n# ----------\nDB_HOST=mastodon_db_1\nDB_USER=mastodon\nDB_NAME=mastodon_production\nDB_PASS=xyz\nDB_PORT=5432<\/code><\/pre>\n\n\n\n<p>In the same vain you&#8217;ll want to change <code>REDIS_HOST<\/code> from <code>localhost<\/code> to <code>mastodon_redis_1<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Issue #2:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">Error: Database is uninitialized and superuser password is not specified.\nYou must specify POSTGRES_PASSWORD to a non-empty value for the\nsuperuser<\/pre>\n\n\n\n<p>This is caused by the <code>pg_isready<\/code> healthcheck failing and can be fixed by setting the necessary env variables in <code>docker-compose.yml<\/code>. The resulting <code>db<\/code> compose service looks as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>db:\n  restart: always\n  image: postgres:9.6-alpine\n  shm_size: 256mb\n  networks:\n    - internal_network\n  healthcheck:\n    test: &#91;\"CMD\", \"pg_isready\", \"-U\", \"mastodon\", \"-d\", \"mastodon_production\"]\n  volumes:\n    - .\/postgres:\/var\/lib\/postgresql\/data\n  environment:\n    POSTGRES_PASSWORD: xyz\n    POSTGRES_DB: mastodon_production\n    POSTGRES_USER: mastodon\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Optional features enabled by default<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">s3 object storage<\/h3>\n\n\n\n<p>This is enabled by default in the sample environment file. If you don&#8217;t configure it correctly (or forget to turn it off), you&#8217;ll notice that media uploads are failing. The failures are accompanied by the following log message: <code>method=POST path=\/api\/v2\/media format=html controller=Api::V2::MediaController action=create status=500 error='Aws::Sigv4::Errors::MissingCredentialsError: missing credentials,provide credentials with one of the following options<\/code>. <\/p>\n\n\n\n<p>If you want to store media on local storage, just comment the lines under File Storage in the env file. Do check to ensure that <code>\/opt\/mastodon\/public\/system\/<\/code> in the web container is persisted on the host system (this should be case via the volume defined in the <code>docker-compose.yml<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">elastic search<\/h3>\n\n\n\n<p>Elastic search is also enabled by default in the sample env file, while it is commented out in the <code>docker-compose.yml<\/code> file.  If you don&#8217;t have the <code>es<\/code> containter running, you&#8217;ll notice a large number of sidekiq jobs starting to accumulate in the &#8216;Retry&#8217; state with the following error message: <code>Faraday::ConnectionFailed: Failed to open TCP connection to localhost:9200 (Cannot assign requested address - connect(2) for \"localhost\" port 9200)<\/code>. Either enable the <code>es<\/code> container or comment out <code>ES_ENABLED=true<\/code> in the ENV file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">My entire ENV file<\/h2>\n\n\n\n<p>For completeness you can find my entire <code>.env.production<\/code> file below, where sensitive entries have been redacted. Note I am currently using mailgun as an SMTP server, as I found sendgrid to be blocked by Spam filters. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># This is a sample configuration file. You can generate your configuration\n# with the `rake mastodon:setup` interactive setup wizard, but to customize\n# your setup even further, you'll need to edit it manually. This sample does\n# not demonstrate all available configuration options. Please look at\n# https:\/\/docs.joinmastodon.org\/admin\/config\/ for the full documentation.\n\n# Federation\n# ----------\n# This identifies your server and cannot be changed safely later\n# ----------\nLOCAL_DOMAIN=social.vdna.be\n\n# Redis\n# -----\nREDIS_HOST=mastodon_redis_1\nREDIS_PORT=6379\n\n# PostgreSQL\n# ----------\nDB_HOST=mastodon_db_1\nDB_USER=mastodon\nDB_NAME=mastodon_production\nDB_PASS=xyz\nDB_PORT=5432\n\n# ElasticSearch (optional)\n# ------------------------\n# ES_ENABLED=true\n# ES_HOST=localhost\n# ES_PORT=9200\n\n# Secrets\n# -------\n# Make sure to use `rake secret` to generate secrets\n# -------\nSECRET_KEY_BASE=xyz\nOTP_SECRET=xyz\n\n# Web Push\n# --------\n# Generate with `rake mastodon:webpush:generate_vapid_key`\n# --------\nVAPID_PRIVATE_KEY=xyz\nVAPID_PUBLIC_KEY=xyz\n\n# Sending mail\n# ------------\nSMTP_SERVER=smtp.eu.mailgun.org\nSMTP_PORT=587\nSMTP_LOGIN=postmaster@mg.social.vdna.be\nSMTP_PASSWORD=xyz\nSMTP_FROM_ADDRESS=social@mg.social.vdna.be\n\n# File storage (optional)\n# -----------------------\n#S3_ENABLED=true\n#S3_BUCKET=files.example.com\n#AWS_ACCESS_KEY_ID=\n#AWS_SECRET_ACCESS_KEY=\n#S3_ALIAS_HOST=files.example.com<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;ve setup an invite-only mastodon instance as a private space for friends and family. While I am still evaluating whether mastodon is the right tool for such a private space, I&#8217;d like to share a number of issues I encountered during setup.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,4],"tags":[],"class_list":["post-327","post","type-post","status-publish","format-standard","hentry","category-linux","category-software","entry"],"_links":{"self":[{"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/posts\/327","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/comments?post=327"}],"version-history":[{"count":5,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/posts\/327\/revisions"}],"predecessor-version":[{"id":338,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/posts\/327\/revisions\/338"}],"wp:attachment":[{"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/media?parent=327"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/categories?post=327"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vdna.be\/site\/index.php\/wp-json\/wp\/v2\/tags?post=327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}