Profile picture of Thomas Preece

Thomas Preece

Configuring Caddy for Mastodon

My web server of choice is Caddy. As a Go programmer, it seems an obvious choice - but I especially like its flexibility of configuration, speed, stability and automatic HTTPS feature. However, as it is a less common choice than something like Nginx, it can be hard to find pre-written configuration that just works for some common applications. So here I’m going to share how I use Caddy with Mastodon to serve my instance.

This isn’t a general-purpose Caddy primer; if you just need a web server to serve Mastodon, you should probably go with Nginx, as it’s assumed as the default choice by pretty much every guide to configuring Mastodon. This article is more about how to set up Mastodon when you already have a working Caddy installation, and assumes that you use a Caddyfile to configure it. These instructions are specifically for Caddy 2; I’ve not included any Caddy 1 equivalents, as I assume very few people are still running that version.

Basic Configuration

Firstly, install Mastodon. The official guide for installing from source is excellent, although it does assume that you are running Ubuntu or Debian. If you’re using a different distro or operating system, you’ll have to figure this part out for yourself.

The only major change you need to make during the installation is to not install Nginx. Make sure to remove it from the list of packages to be installed in the System Packages section. If you forget to remove it from this list, it’s easy enough to remove afterwards:

# apt-get remove nginx

If you leave Nginx installed, then the next time you reboot your server it will start before Caddy does and bind to ports 80 and 443, meaning that Caddy won’t start. This is easily fixed by removing (or stopping or disabling) Nginx and then restarting Caddy.

You can then safely skip over the sections Setting up ngnix and Acquiring a SSL certificate, as we are now going to configure Caddy.

The following is a basic Caddyfile configuration for Mastodon. Assuming that you have installed Mastodon using the default settings (specifically, that it is installed in /home/mastodon and that mastodon-web is running on port 3000 and mastodon-streaming on port 4000), the only change you need to make is to the domain name on the first line:

mastodon.example.com {
        @local {
                file
                not path /
        }
        @streaming {
                path /api/v1/streaming /api/v1/streaming/*
        }
        @cache_control {
                path_regexp ^/(emoji|packs|/system/accounts/avatars|/system/media_attachments/files)
        }

        root * /home/mastodon/live/public

        log {
                output file /var/log/caddy/mastodon.log
        }

        handle_errors {
                rewrite 500.html
                file_server
        }

        header {
                Strict-Transport-Security "max-age=31536000"
        }
        header /sw.js Cache-Control "public, max-age=0"
        header @cache_control Cache-Control "public, max-age=31536000, immutable"

        handle @local {
                file_server
        }

        reverse_proxy @streaming {
                to http://localhost:4000
                header_up X-Forwarded-Port 443
                header_up X-Forwarded-Proto https

                transport http {
                        keepalive 5s
                        keepalive_idle_conns 10
                }
        }

        reverse_proxy {
                to http://localhost:3000
                header_up X-Forwarded-Port 443
                header_up X-Forwarded-Proto https

                transport http {
                        keepalive 5s
                        keepalive_idle_conns 10
                }
        }
}

This configuration file is based on yukimochi on GitHub, with a bug fix to get streaming to work correctly.

Because of Caddy’s automatic HTTPS feature, there’s no need to manually acquire an SSL certificate. Just ensure that your domain name is pointed at your server, then reload or restart Caddy, and Mastodon should start working.

Media Files

Broadly speaking, there are two options for serving uploaded media files in Mastodon:

  1. Directly from Mastodon, under public/system
  2. From a storage provider, typically Amazon S3 or similar

If you start off serving media directly, you will probably want to migrate to S3 at some point, so it is worth making provision for that right from the start. A good way to do this is to configure media to be served from a custom subdomain; you can then change the way this subdomain handles media requests at any time, without breaking all of your existing media links.

Firstly, add the following to your .env.production, changing the domain name in both lines to be whatever you want to set up:

S3_ALIAS_HOST=assets.example.com
S3_CLOUDFRONT_HOST=assets.example.com

Now add the following to your Caddyfile:

assets.example.com {
	root * /home/mastodon/live/public/system
	file_server
}

Add the relevant DNS records, then restart Mastodon and Caddy.

Then, once you have eventually set up S3 storage, you can reconfigure assets.example.com to point to your S3 media instead. How you configure this will depend somewhat on your provider and whether you need to cache media on your own server to prevent excessive access charges. As an example, Jortage Communal Cloud is very straightforward - you don’t need to do anything clever, so your Caddyfile would simply look something like this:

assets.example.com {
        rewrite * /examplecom{path}
        reverse_proxy https://pool.jortage.com {
                header_up Host {upstream_hostport}
        }
}

If you don’t set up a dedicated subdomain for media right from the start, and serve files directly from public/system, then when you eventually do set up S3 you will want to add a redirect by adding something like the following to your Caddyfile for your main Mastodon domain:

	@local_media {
		path_regexp /system/(.*)
	}
	
	redir @local_media https://assets.example.com/{http.regexp.1} permanent

Configuring a separate LOCAL_DOMAIN and WEB_DOMAIN

My Mastodon username is @thomas@thomaspreece.net, but my Mastodon instance is at https://mastodon.thomaspreece.net. This is configured by setting LOCAL_DOMAIN and WEB_DOMAIN in .env.production, as explained in Federation and display section of “Configuring your environment”.

An important point that is easy to miss is that you have to set up redirects for webfinger and host-meta on your WEB_DOMAIN. The documentation provides an example for how to do this on Nginx; for the Caddy equivalent, suppose you want to set up Mastodon so that your username domain (WEB_DOMAIN) is example.com, with the web frontend for your instance (LOCAL_DOMAIN) at mastodon.example.com. You will need to add the following to your Caddyfile:

example.com {
	redir /.well-known/host-meta https://mastodon.example.com{uri} permanent
	redir /.well-known/webfinger https://mastodon.example.com{uri} permanent
	
	# the rest of the configuration for the website at example.com goes here
}

Conclusion

And that’s how to set up Caddy for Mastodon. These instructions are based on my practical experience of running Mastodon and Caddy on my server - you may need to tweak them slightly for your own setup, but they should work as a starting point.

If you have any suggestions for any changes or improvements to the configuration, please get in touch!


[ Blog Index | Home ]