Ramblings from Roy

My personal take on anything that interests (or irritates) me

So, you’ve decided to host Mastodon yourself. If you are reading this post, you obviously know what Mastodon is, so I will not waste your time in explaining the things you already know. What I would like to point out, is something that I wish I knew myself before starting on this self-hosted route.

Why should you NOT host Mastodon yourself?

Don’t get me wrong, I love that people are joining Mastodon. I love that decentralized social network is now mainstream. But, there is a difference between joining Mastodon, and hosting a Mastodon instance. If you want to experience Mastodon like any other social networks, just join any well-reputed server that already exists, and you’ll be on your way. But, if you insist on having complete control over your social network presence, and do not want to leave anything up to somebody else’s opinions, including the length of your posts or display name, whether you federate with some specific server, emoji reactions, etc., only then you might want to host your own instance. Since you already considered the advantages of building your own Mastodon instance, let’s take a look at a few downsides to hosting your own.

Moderation headaches:

Even if you plan on running a single-user instance, which my own old personal instance was, you gotta clean up your public feed. This applies even if you don’t subscribe to any relays. And please don’t take moderation lightly. I found and reported (to the FBI) an instance solely used for sharing CSAM (although I was subscribed to two relays at that time, and this was well before Mastodon put in much stronger protection) when one of their members’ account showed up in my federated feed.

Difficult broader outreach:

Unless you plan on hosting a multi-user server serving hundreds of users, you might find broader outreach to be difficult. It is especially difficult for single-user instances, unless you have web notoriety and a lot of people follow you. Although, recently, Mastodon made a lot of changes to help you discover new people. Relays also exist to give your instance a broader outreach without anyone specifically following you.

Storage concerns:

Running Mastodon takes a significant amount of storage space. You either need to look for a cheap S3 compatible object storage service, or be willing to host it in a server you run at your home with a big RAID array. Just as an example, my one year old single-user Mastodon instance uses about 670 GB of storage in my RAID array as of the time of writing this post (keep in mind that I was subscribed to two relays at the time, one quite big, and the other medium sized). If you want to scale, you definitely need S3 compatible object storage in the cloud.

Understood, but how do you host your own instance?

Okay, so you’re adamant about hosting your own Mastodon instance. In that case, let’s look at Podman containers. Containers make everything easier. Whoever made the comic about “it works on my computer”, clearly underestimated the proliferation of containers in the server space. So, to make things easier, we’ll look at how to use Podman rootless containers to host our new Mastodon instance.

Choosing your “version” of Mastodon:

Since Mastodon is open-source, we can choose from multiple different forks of it. In this guide, I will talk about vanilla Mastodon (what mastodon.social runs), Mastodon glitch-soc version (what infosec.exchange, and a bunch of other big servers run), and TheEssem’s fork of glitch-soc (what wetdry.world, and a few other servers including my own run). Let’s run down the main differences of the three.

Vanilla Mastodon

This is the “official” version of Mastodon, and to no one’s surprise, this is also the most widely used version of Mastodon. Most major instances run vanilla Mastodon, including the original server: mastodon.social. With vanilla Mastodon, you will always have a 500 character limit for your toots, 30 character limit for your display name, be stuck to plain text toots, etc. These are the limitations of any other microblogging services that people are used to and are fine with. This also gets the quickest security patches than any other versions/forks, so it is the safest one to use. If you want to use this version, pull the official container image while running your Podman containers:

ghcr.io/mastodon/mastodon:latest

Mastodon glitch-soc

This is a major fork of vanilla Mastodon maintained by an amazing group of volunteers. A lot of major instances like infosec.exchange run this fork of Mastodon. This comes with a lot more customizability, like changing character limits for your toots, display name, bio, publish toots in markdown format, etc. My old personal instance ran this fork of Mastodon. This gets the second quickest security patches, and so, is relatively safe to use. If you want to use this fork, pull this container image while running your Podman containers:

ghcr.io/glitch-soc/mastodon:latest

Chuckya fork of Mastodon (by The Essem)

This is a fork of the glitch-soc fork of Mastodon. This is maintained by one dedicated volunteer, TheEssem on Github. This retains all the customizability of glitch-soc, and adds the ability to react with emojis. Very few instances run this fork, including TheEssem’s wetdry.world, and my new personal instance (meghadeep.social). As much fun as emoji reactions are, please keep in mind that this fork is maintained by one single person for free, so security patches entirely depend on their availability. And, this patch will be merged with glitch-soc soon, so if you can wait for a little while to get emoji reactions, you should stick to the glitch-soc version. If you want to use this fork, pull this container image while running your Podman containers (please note these images are built by me, and pushed manually, so this will take even more time to get an update):

docker.io/meghadeep/mastodon:chuckya-latest

For the purposes of this guide, I will assume that you are using the Chuckya fork (by The Essem on GitHub), since that’s what I personally use.

Preparing your machine:

First, we have to create a few directories where we want to store our data. If you want to use an S3 compatible object storage, you can create these directories anywhere you want, but if you simply wish to use the Linux filesystem to store your Mastodon data, I suggest you create these directories in a partition with plenty of storage space. My old instance had these directories in my RAID array, while my new instance with an S3 compatible object storage has these right in my home partition.

mkdir /path/to/main_directory

Change the path to your liking.

Now we will create the app directory and db directory inside the main directory.

mkdir /path/to/main_directory/db mkdir /path/to/main_directory/app

Customizing environment:

Now that the directories are set up, we’ll create the environment file that we want our Mastodon containers to use. This would depend on which “version” of Mastodon you want to use. Please read through the whole environment file to change the required few. I have already put in some defaults that I use in my new personal Mastodon instance, so, you should be fine with only adding the few environment variables that I have not added in this example environment file.

# This is a sample configuration file. You can generate your configuration
# with the `rake mastodon:setup` interactive setup wizard, but to customize
# your setup even further, you'll need to edit it manually. This sample does
# not demonstrate all available configuration options. Please look at
# https://docs.joinmastodon.org/admin/config/ for the full documentation.

# Note that this file accepts slightly different syntax depending on whether
# you are using `docker-compose` or not. In particular, if you use
# `docker-compose`, the value of each declared variable will be taken verbatim,
# including surrounding quotes.
# See: https://github.com/mastodon/mastodon/issues/16895

# Federation (REQUIRES CHANGES)
# ----------
# This identifies your server and cannot be changed safely later
# ----------
LOCAL_DOMAIN=

# Use this only if you need to run mastodon on a different domain than the one used for federation.
# You can read more about this option on https://docs.joinmastodon.org/admin/config/#web-domain
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
# WEB_DOMAIN=mastodon.example.com

# Use this if you want to have several aliases handler@example1.com
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
# be added. Comma separated values
# ALTERNATE_DOMAINS=example1.com,example2.com

# Use HTTP proxy for outgoing request (optional)
# http_proxy=http://gateway.local:8118
# Access control for hidden service.
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true

# Authorized fetch mode (optional)
# Require remote servers to authentify when fetching toots, see
# https://docs.joinmastodon.org/admin/config/#authorized_fetch
AUTHORIZED_FETCH=true

# Limited federation mode (optional)
# Only allow federation with specific domains, see
# https://docs.joinmastodon.org/admin/config/#whitelist_mode
# LIMITED_FEDERATION_MODE=true

# Redis (NO CHANGE)
# -----
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_NAMESPACE=mastodon

# PostgreSQL (REQUIRES CHANGES)
# ----------
DB_HOST=localhost
DB_USER=postgres
DB_NAME=mastodon_production
DB_PASS=postgres
DB_PORT=5432
# Generate with `podman run --rm meghadeep/mastodon:chuckya-latest bin/rails db:encryption:init`
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=

# Elasticsearch (optional)
# ------------------------
# ES_ENABLED=true
# ES_HOST=
# ES_PORT=9200
# ES_PRESET=


# Secrets (REQUIRES CHANGES)
# -------
# Generate each with the `RAILS_ENV=production podman run --rm meghadeep/mastodon:chuckya-latest bundle exec rails secret`
# -------
SECRET_KEY_BASE= 
OTP_SECRET=


# Web Push (REQUIRES CHANGES)
# --------
# Generate with `podman run --rm meghadeep/mastodon:chuckya-latest rails mastodon:webpush:generate_vapid_key` (first is the private key, second is the public one)
# You should only generate this once per instance. If you later decide to change it, all push subscription will
# be invalidated, requiring the users to access the website again to resubscribe.
# --------
VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=


# Registrations (OPTIONAL CHANGES)
# -------------

# Single user mode will disable registrations and redirect frontpage to the first profile
# SINGLE_USER_MODE=true

# Prevent registrations with following e-mail domains
# EMAIL_DOMAIN_DENYLIST=example1.com|example2.de|etc

# Only allow registrations with the following e-mail domains
# EMAIL_DOMAIN_ALLOWLIST=

#TODO move this
# Optionally change default language
# DEFAULT_LOCALE=de


# Sending mail (REQUIRES CHANGES)
# ------------
SMTP_SERVER=
SMTP_PORT=
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=


# File storage (OPTIONAL CHANGES)
# -----------------------
# The attachment host must allow cross origin request from WEB_DOMAIN or
# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the
# following header field:
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
# -----------------------
# S3_ENABLED=true
# S3_ENDPOINT=
# S3_HOSTNAME=
# S3_BUCKET=
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# S3_PROTOCOL=https
# S3_SIGNATURE_VERSION=v4


# IP and session retention
# -----------------------
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
# -----------------------
IP_RETENTION_PERIOD=31556952
SESSION_RETENTION_PERIOD=31556952


# ------------------------------------
# --------  GLITCH-SOC ONLY ----------
# ------------------------------------


# Swift (optional)
# The attachment host must allow cross origin request - see the description
# above.
# SWIFT_ENABLED=true
# SWIFT_USERNAME=
# For Keystone V3, the value for SWIFT_TENANT should be the project name
# SWIFT_TENANT=
# SWIFT_PASSWORD=
# Some OpenStack V3 providers require PROJECT_ID (optional)
# SWIFT_PROJECT_ID=
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
# issues with token rate-limiting during high load.
# SWIFT_AUTH_URL=
# SWIFT_CONTAINER=
# SWIFT_OBJECT_URL=
# SWIFT_REGION=
# Defaults to 'default'
# SWIFT_DOMAIN_NAME=
# Defaults to 60 seconds. Set to 0 to disable
# SWIFT_CACHE_TTL=

# Optional asset host for multi-server setups
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
# if WEB_DOMAIN is not set. For example, the server may have the
# following header field:
# Access-Control-Allow-Origin: https://example.com/
# CDN_HOST=https://assets.example.com

# Optional list of hosts that are allowed to serve media for your instance
# This is useful if you include external media in your custom CSS or about page,
# or if your data storage provider makes use of redirects to other domains.
# EXTRA_DATA_HOSTS=https://data.example1.com|https://data.example2.com

# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
# S3_ALIAS_HOST=

# Streaming API integration
# STREAMING_API_BASE_URL=


# External authentication (optional)
# ----------------------------------
# LDAP authentication (optional)
# LDAP_ENABLED=true
# LDAP_HOST=localhost
# LDAP_PORT=389
# LDAP_METHOD=simple_tls
# LDAP_BASE=
# LDAP_BIND_DN=
# LDAP_PASSWORD=
# LDAP_UID=cn
# LDAP_MAIL=mail
# LDAP_SEARCH_FILTER=(|(%{uid}=%{email})(%{mail}=%{email}))
# LDAP_UID_CONVERSION_ENABLED=true
# LDAP_UID_CONVERSION_SEARCH=., -
# LDAP_UID_CONVERSION_REPLACE=_

# PAM authentication (optional)
# PAM authentication uses for the email generation the "email" pam variable
# and optional as fallback PAM_DEFAULT_SUFFIX
# The pam environment variable "email" is provided by:
# https://github.com/devkral/pam_email_extractor
# PAM_ENABLED=true
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
# PAM_EMAIL_DOMAIN=example.com
# Name of the pam service (pam "auth" section is evaluated)
# PAM_DEFAULT_SERVICE=rpam
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
# PAM_CONTROLLED_SERVICE=rpam

# Global OAuth settings (optional) :
# If you have only one strategy, you may want to enable this
# OAUTH_REDIRECT_AT_SIGN_IN=true

# Optional CAS authentication (cf. omniauth-cas) :
# CAS_ENABLED=true
# CAS_URL=https://sso.myserver.com/
# CAS_HOST=sso.myserver.com/
# CAS_PORT=443
# CAS_SSL=true
# CAS_VALIDATE_URL=
# CAS_CALLBACK_URL=
# CAS_LOGOUT_URL=
# CAS_LOGIN_URL=
# CAS_UID_FIELD='user'
# CAS_CA_PATH=
# CAS_DISABLE_SSL_VERIFICATION=false
# CAS_UID_KEY='user'
# CAS_NAME_KEY='name'
# CAS_EMAIL_KEY='email'
# CAS_NICKNAME_KEY='nickname'
# CAS_FIRST_NAME_KEY='firstname'
# CAS_LAST_NAME_KEY='lastname'
# CAS_LOCATION_KEY='location'
# CAS_IMAGE_KEY='image'
# CAS_PHONE_KEY='phone'

# Optional SAML authentication (cf. omniauth-saml)
# SAML_ENABLED=true
# SAML_ACS_URL=http://localhost:3000/auth/auth/saml/callback
# SAML_ISSUER=https://example.com
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
# SAML_IDP_CERT=
# SAML_IDP_CERT_FINGERPRINT=
# SAML_NAME_IDENTIFIER_FORMAT=RAILS_SERVE_STATIC_FILES: true
# SAML_CERT=
# SAML_PRIVATE_KEY=
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241"
# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42"
# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4"
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=


# Custom settings
# ---------------
# Various ways to customize Mastodon's behavior
# ---------------

# Maximum allowed character count (OPTIONAL CHANGES)
MAX_TOOT_CHARS=2500

# Maximum allowed hashtags to follow in a feed column
# Note that setting this value higher may cause significant
# database load (OPTIONAL CHANGES)
MAX_FEED_HASHTAGS=4

# Maximum number of pinned posts (OPTIONAL CHANGES)
MAX_PINNED_TOOTS=5

# Maximum allowed bio characters (OPTIONAL CHANGES)
MAX_BIO_CHARS=2500

# Maximim number of profile fields allowed (OPTIONAL CHANGES)
MAX_PROFILE_FIELDS=10

# Maximum allowed display name characters (OPTIONAL CHANGES)
MAX_DISPLAY_NAME_CHARS=200

# Maximum allowed poll options (OPTIONAL CHANGES)
MAX_POLL_OPTIONS=10

# Maximum allowed poll option characters (OPTIONAL CHANGES)
MAX_POLL_OPTION_CHARS=300

# Rails option
RAILS_SERVE_STATIC_FILES=true
RAILS_ENV=production
NODE_ENV=production

# LibreTranslate
# LIBRE_TRANSLATE_ENDPOINT=

# Maximum image and video/audio upload sizes
# Units are in bytes
# 1048576 bytes equals 1 megabyte
# MAX_IMAGE_SIZE=8388608
# MAX_VIDEO_SIZE=41943040

# Maximum search results to display
# Only relevant when elasticsearch is installed
# MAX_SEARCH_RESULTS=20

# Maximum hashtags to display
# Customize the number of hashtags shown in 'Explore'
# MAX_TRENDING_TAGS=10

# Maximum custom emoji file sizes
# If undefined or smaller than MAX_EMOJI_SIZE, the value
# of MAX_EMOJI_SIZE will be used for MAX_REMOTE_EMOJI_SIZE
# Units are in bytes
# MAX_EMOJI_SIZE=262144
# MAX_REMOTE_EMOJI_SIZE=262144

# Optional hCaptcha support
# HCAPTCHA_SECRET_KEY=
# HCAPTCHA_SITE_KEY=


# ---------------------------------
# ------ ESSEM'S FORK ONLY --------
# ---------------------------------

# Maximum number of emoji reactions per toot and user (minimum 1) (OPTIONAL CHANGES)
MAX_REACTIONS=5

Change the environment variables that need to be changed, and save it as .env.production in the main_directory.

nano /path/to/main_directory/.env.production

And paste the entire environment file with your changes. CTRL+X to save and exit.

After we change all the required fields in the environment file, it is time to start initializing our Mastodon instance.

Initializing your Mastodon instance:

As long as you correctly modify the environment file with your own configurations, the hard part for you is done!

Create a new file in the main_directory to initialize the mastodon instance.

nano /path/to/main_directory/initialize.sh

Copy-paste the following code snippet, make sure to change your directories for the volume mounts and the environment file. If you are using TheEssem’s fork of Mastodon, you won’t need to change the container images, otherwise change it to any version of Mastodon mentioned at the very beginning:

#!/bin/bash

podman pod create \
--replace \
--name mastodon-emoji-pod

podman run \
--replace \
-d \
--name mast-emoji-db \
--pod mastodon-emoji-pod \
--shm-size=256mb \
-e 'POSTGRES_HOST_AUTH_METHOD=trust' \
-v /path/to/main_directory/db:/var/lib/postgresql/data:Z \
--health-cmd='["pg_isready", "-U", "postgres"]' \
--restart=unless-stopped \
postgres:14-alpine

podman run \
--replace \
-d \
--name mast-emoji-valkey \
--pod mastodon-emoji-pod \
valkey/valkey:latest

echo "waiting for postgres to be ready"
sleep 10

podman run \
--replace \
--rm \
--name mast-emoji-web \
--pod mastodon-emoji-pod \
--env-file=/path/to/main_directory/.env.production \
--health-cmd="wget -q --spider --proxy=off localhost:3000/health || exit 1" \
-v /path/to/main_directory/app:/mastodon/public/system:Z \
--requires=mast-emoji-db,mast-emoji-valkey \
docker.io/meghadeep/mastodon:chuckya-latest \
bin/rails db:create

sleep 2

podman run \
--replace \
--rm \
--name mast-emoji-web \
--pod mastodon-emoji-pod \
--env-file=/path/to/main_directory/.env.production \
--health-cmd="wget -q --spider --proxy=off localhost:3000/health || exit 1" \
-v /path/to/main_directory/app:/mastodon/public/system:Z \
--requires=mast-emoji-db,mast-emoji-valkey \
docker.io/meghadeep/mastodon:chuckya-latest \
bin/rails db:migrate

Make sure you change the paths in the env file and volume mapping parts of the script before saving it!

Make the file executable, and run it:

chmod +x /path/to/main_directory/initialize.sh /path/to/main_directory/initialize.sh

At this point, our database will have been initialized for production use. The only thing left is to run all the containers necessary.

Running your Mastodon instance:

We will create our last shell script to pull up all the necessary Podman containers.

Create a new file in the main_directory to help start the mastodon instance.

nano /path/to/main_directory/compose.sh

Copy-paste the following code snippet, make sure to change your directories for the volume mounts and the environment file. If you are using TheEssem’s fork of Mastodon, you won’t need to change the container images, otherwise change it to any version of Mastodon mentioned at the very beginning:

#!/bin/bash

podman pod create \
--replace \
--name mastodon-emoji-pod \
--net=slirp4netns:allow_host_loopback=true,port_handler=slirp4netns \
-p 14000:4000 -p 13000:3000

podman run \
--replace \
-d \
--name mast-emoji-db \
--pod mastodon-emoji-pod \
--shm-size=256mb \
-e 'POSTGRES_HOST_AUTH_METHOD=trust' \
-v /path/to/main_directory/db:/var/lib/postgresql/data:Z \
--health-cmd='["pg_isready", "-U", "postgres"]' \
--restart=unless-stopped \
postgres:14-alpine

podman run \
--replace \
-d \
--name mast-emoji-valkey \
--pod mastodon-emoji-pod \
valkey/valkey:latest

podman run \
--replace \
-d \
--name mast-emoji-streaming \
--pod mastodon-emoji-pod \
--restart=unless-stopped \
--env-file=/path/to/main_directory/.env.production \
--health-cmd="wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1" \
--requires=mast-emoji-db,mast-emoji-valkey \
docker.io/meghadeep/mastodon:chuckya-streaming-latest \
node ./streaming

podman run \
--replace \
-d \
--name mast-emoji-web \
--pod mastodon-emoji-pod \
--restart=unless-stopped \
--env-file=/path/to/main_directory/.env.production \
--health-cmd="wget -q --spider --proxy=off localhost:3000/health || exit 1" \
-v /path/to/main_directory/app:/mastodon/public/system:Z \
--requires=mast-emoji-db,mast-emoji-valkey \
docker.io/meghadeep/mastodon:chuckya-latest \
bundle exec puma -C config/puma.rb

podman run \
--replace \
-d \
--name mast-emoji-sidekiq \
--pod mastodon-emoji-pod \
--restart=unless-stopped \
--env-file=/path/to/main_directory/.env.production \
--health-cmd="ps aux | grep '[s]idekiq\ 6' || false" \
-v /path/to/main_directory/app:/mastodon/public/system:Z \
--requires=mast-emoji-db,mast-emoji-valkey \
docker.io/meghadeep/mastodon:chuckya-latest \
bundle exec sidekiq

Make the file executable, and run it:

chmod +x /path/to/main_directory/compose.sh /path/to/main_directory/compose.sh

And that’s it! You now have all the containers pulled up for running your own Mastodon instance. Keep in mind that you’ll need a reverse proxy with proper FQDN that you provisioned in the environment file.

I currently use NGINX, and the following config works for me (replace back.end.ip.addr with the IP address of your server running the containers, and please note that I'm only including the reverse proxy location configs within the server directive):

		location /api/v1/streaming {
			proxy_pass http://back.end.ip.addr:14000/;
			proxy_http_version 1.1;
			proxy_redirect off;
			proxy_buffering off;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection Upgrade;
			proxy_set_header Host $host;
		}
		location ^~ /.well-known/acme-challenge/ {
			try_files $uri /;
		}
		location / {
			proxy_pass http://back.end.ip.addr:13000/;
			proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection Upgrade;
			proxy_set_header Host $host;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Proto https;
		}

I have used Apache in the past, and the following config worked for me (replace back.end.ip.addr with the IP address of your server running the containers, and please note that I'm only including the reverse proxy location configs within the virtual host directive):

ProxyPass /.well-known/acme-challenge !
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
ProxyPass /api/v1/streaming http://back.end.ip.addr:14000/
ProxyPass / http://back.end.ip.addr:13000/
ProxyPassReverse / http://back.end.ip.addr:13000/

Once you have your reverse proxy set up, you’ll need to create and authorize your new Owner account.

podman exec -i mast-emoji-web tootctl accounts create <your-local-username> --email <your-email> --confirmed --role Owner podman exec -i mast-emoji-web tootctl accounts approve <your-local-username>

You should now be able to login using your newly created account. Make sure to change the password afterwards.

Welcome to the Fediverse!

If you are working in the energy sector in the US, you probably already know that Palisades Nuclear Plant, a nuclear power plant that was decommissioned in 2022, is in line to come online as early as next year. And, it’s not the only shuttered plant that can come online this decade; the infamous Three Mile Island nuclear power plant is slated to come online as early as 2028. Even if you are not intimately familiar with the energy sector, you probably have heard rumors that Amazon is going nuclear to power its ginormous datacenters; and that even Microsoft has promised to look into nuclear power to power its next generation of AIs. With all these rumors and excitements in the nuclear power sector, I thought it was time to revisit nuclear power plants, why it might be making a come-back, and where it might be falling short.

Is there a renewed interest in nuclear power around the world?

US

In the US, there are at least two nuclear power plants that are supposed to start commercially producing power within the next five years.

Palisades already signed a power purchase agreement, and plans to start producing power for the grid in late 2025. Holtec International is responsible for bringing the plant online, and they are starting with the existing retired 800 MW unit, with plans to build their SMRs on site after 2030.

Alt text Photo credit: Holtec International (https://holtecinternational.com/products-and-services/holtec-palisades/)

Three Mile Island, the infamous site of the worst nuclear power disaster in the US, is also slated to be restarted. Constellation Energy, which bought the reactor that was unaffected by the partial meltdown, plans to start producing power by 2028. Microsoft already has a power purchase agreement with TMI to power its next generation of AIs with 800 MW of nuclear power.

Alt text Photo credit: Britannica (https://www.britannica.com/technology/nuclear-reactor/Three-Mile-Island-and-Chernobyl)

India

The Indian Department of Atomic Energy plans to add seven new reactors to its aging fleet by 2029 using public-private joint venture. At present, nuclear power provides a measly 2% of India’s total energy needs; with these seven new reactors, that is supposed to almost double, from 7.48 GWe to 13.08 GWe. DAE is also experimenting with indigenous SMRs, termed Bharat Small Modular Reactor, a 220MW reactor that uses light water instead of the traditional pressurized heavy water, and breeder reactors that can produce more nuclear fuel than it consumes to account for the relatively low nuclear fuel availability in the country. The first indigenous breeder reactor (550 MWe) at Kalpakkam Plant has already loaded the radioactive core, and is working towards achieving criticality. The biggest project is arguably the Kudankulam Nuclear Power Plant, with two already functioning reactors, and four more reactors approved at the site. It is projected to generate a total of 6000 MW of power when completed.

Alt text Kudankulam Nuclear Power Plant, Photo credit: IndiaTV (https://www.indiatvnews.com/news/india/kudankulam-nuclear-power-plant-russia-ships-equipment-627451)

China

China has always been a huge proponent of nuclear power, and already has a massive fleet of over fifty reactors. With twenty more reactors under construction as of 2022, China has the fastest growing nuclear power generation in the world. China is also heavily investing in SMRs, and has quite a few of those operating as a part of an extended test program.

Alt text Photo credit: CGTN News (https://news.cgtn.com/news/3d3d514f3541544e31457a6333566d54/share.html)

Africa

The sole operating nuclear power plant in all of Africa is in South Africa, and so far, other African countries had been quiet on nuclear power. But, that has started to change.

Ghana is looking to build their own nuclear power plant. The Ministry of Energy (of Ghana) issued a Request for Information to gather technical and financial insights from vendors and countries regarding the deployment of SMRs in Ghana.

Nigeria and Kenya are finalizing their nuclear preparedness applications with the International Atomic Energy Agency (IAEA). While Kenya is in Phase 2, where they are looking for a suitable site to build their first nuclear power plant, Nigeria is still in the initial phase, where they are bringing their infrastructure up to code for a nuclear power plant in the country.

Uganda is on the verge of finalizing their partnership to start their nuclear initiative. 2024 will be a pivotal time in Uganda’s economy as they plan to secure financing required for a successful build of their first nuclear power plant.

Rwanda has already entered into a collaboration with Dual Fluid, a Canadian-German nuclear technology company, to build a new generation demonstration reactor along with overseeing the necessary technology transfer. The demonstration reactor is scheduled to be operational by the end of 2026, and comprehensive technical tests and technology transfer are supposed to be completed by 2028.

Egypt is by far in the most advanced stage of building their own nuclear power plant in Africa outside of South Africa. ROSATOM, the Russian state corporation, is overseeing the construction, and will provide the plant with Russian nuclear fuel after successful completion. The plant is being built in El-Dabaa (320 kilometers northwest of Cairo), and comprises of four reactors, each with a nameplate capacity of 1200 MW. GE has won the contract to provide the plant with their turbines once built. Egypt hopes to put the plant on the grid by 2028.

Alt text Photo credit: NPAA (https://web.archive.org/web/20220720201047/https://nppa.gov.eg/en/main-construction-phase-for-the-el-dabaa-nuclear-power-plant-project-and-pouring-of-the-first-concrete-for-unit-1-commenced-in-egypt/)

South America

South America is no stranger to nuclear power. Even though hydroelectric power is readily available in the region, nuclear power has been gaining traction among the South American countries since the 2000s.

Argentina is leading nuclear power adoption in South America, with three already operating nuclear reactors, generating about 5% of the country’s electricity demand, and with a domestically designed SMR under construction. This new SMR, CAREM 25, will be a part of the new generation of reactors in Argentina, since the older three operating plants use traditional heavy water and enriched fuel systems.

Brazil has two nuclear reactors in operation already, with the third reactor having been suspended under construction. Work on the third reactor, Angra 3, was started in 1984, but was suspended in 1986. The project resumed in 2006, but was suspended again in 2015, after the construction was 65% complete. Recently, Eletronuclear, the company in charge of the reactor, got the Brazilian court to give it permission to resume work. The reactor is expected to commercially start producing power in 2031.

Oceania

In Australia, Nuclear power is heavily politicized. The country does not have any energy producing reactors, even though it is rich in Uranium. The sole nuclear reactor in Australia at Lucas Heights is used to produce radioactive materials used in nuclear medicine.

New Zealand goes even farther in banning nuclear reactors by enforcing New Zealand Nuclear-Free Zone. Under the New Zealand Nuclear Free Zone, Disarmament, and Arms Control Act of 1987, territorial sea, land and airspace of New Zealand became nuclear-free zones. This has since remained a part of New Zealand’s foreign policy.

European Union

EU is highly divided on nuclear power. And although about 25% of the bloc’s total energy comes from nuclear power, not all countries in EU are in agreement about the future of nuclear power in the region.

Germany is one of the strongest oppositions of nuclear power in the EU. Once a strong proponent of nuclear power, so much so that in 1990, a quarter of the country’s total energy needs came from nuclear plants, Germany has already phased out nuclear power in April 2023.

Portugal, Denmark, and Austria are also heavily against nuclear power. In 2022, these three countries actually filed a legal challenge against the EU, claiming its categorization of nuclear energy as green investment was ‘greenwashing’.

Opinions on nuclear power in Italy are mixed. Italy had previously shuttered all of its nuclear power plants, but in recent years there have been a number of proposals to revive nuclear power.

Earlier this year, the EU held its first ever Nuclear Energy Summit in Brussels. 32 EU member countries attended the summit (Germany was notably absent). The summit highlighted the importance of nuclear power for meeting the region’s decarbonization goals, and had discussions about creating financing pipelines for future projects in the EU.

Opinion

I am very supportive of nuclear power. I think it is a fantastic way to decarbonize our grid, and provides a very safe form of reliable energy. Don’t get me wrong, renewables are great; but at the end of the day, it is dependent on the weather. And while batteries mitigate a lot of the intermittency issues associated with renewables, at its current state, it’s still not the silver bullet that humankind had been waiting for. And although accurate weather forecasts, and high performance grid scale batteries do help, we’d need to massively scale up our installed renewable capacity, and would need to ramp up battery research even more, if we want to completely decarbonize our grid without nuclear power.

Full decarbonization is possible without nuclear power, but it’s just not practical (I may have to eat my words in the future). Lithium mining is highly problematic, with severe geopolitical implications, and other battery technologies (like sodium batteries) are not as energy dense as lithium batteries. And at its present state, batteries themselves are limited with their cycle/lifetime numbers. It is possible to offset the restrictions due to finite cycle/lifetime numbers using more installed capacity, but then we are making the grid purposefully inefficient. Grid scale batteries also store a finite amount of charge (usually 4 hours at full power), and any weather events lasting more than four hours will need to be met with even more installed capacity of renewables+batteries.

Nuclear power, on the other hand, is a mature technology that produces minimal waste products with the highest capacity factor. Nuclear power is stable, and can provide the baseload power even on calm nights. Even at its present state, we have the technology to make nuclear power as safe as humanly possible that can withstand literal Tsunamis (hint: Fukushima Disaster), with safer smaller versions of big nuclear plants right around the corner (hint: SMRs).

SMRs are poised to revolutionize the electric grid with smaller reactors taking up much less space, and with somewhat flexible power output that can ramp up or down according to demand. While they are not as flexible as gas units or batteries, they are also not limited by their available charge nor do they produce any greenhouse gases.

In developing countries like India that are still heavily dependent on coal power, and where residential solar is still very much limited to a minuscule fraction of the already privileged part of the population, nuclear power can have a drastic effect on per capita carbon footprint. Renewables are obviously necessary, but if we use those as those are designed (intermittent resources), we make a much better use of our installed capacity.

Even from a technical standpoint, nuclear power provides the grid with much needed system inertia (MVAR support) that renewables cannot. If we have too much renewable power in our generation mix, we’d need a new type of grid connected technology called synchronous condensers to supply the grid with system inertia. Nuclear power does not come with such requirement.

Nuclear power does come with other restrictions though, such as radioactive waste disposal, and the need for a foolproof disaster management system. Any oversight in either of these invites disaster that can potentially kill tens of thousands of people. As it stands right now, we have effectively solved the disaster management system of nuclear plants; there are Thorium based reactors being tested that can be operated by high schoolers, and has such strong passive protection systems that it’s almost impossible for it to go into a meltdown. And if we look at older nuclear disaster management technologies, it might be controversial, but in my opinion, the Fukushima Disaster actually proved how safe nuclear power was even two decades ago; since even when everything went wrong in the decades old reactor, even when the plant got hit by a literal tsunami, the plant operators could direct the contaminated materials into safe storage tanks, and there had been no recorded deaths directly resulting from the disaster. However, disposal of radioactive waste is still a hot button issue that does not have an easy solution. Even though the amount of radioactive waste a nuclear plant produces is relatively small, the half-lives of the most common radioactive elements produced as waste products are in thousands of years which forces us to think in geological timescale. We need proper storage of radioactive materials in a place that’s geologically stable, and we need effective deterrents that can stand the test of time, hundreds of generations worth of time. So far, the most common method of storing radioactive waste is inside the plant area itself, in large concrete chambers, which isn’t ideal, and desperately needs to be changed. At the end of the day, we need a good mix of both nuclear power, and renewable power in our grid. It’s not a zero sum game, nuclear power and renewable power both have their own niche that they can target. Right now most of the world’s “peaking plants” are diesel units or gas turbine units. Both very expensive, and diesel units have the added problem of being environmentally disastrous. If we have enough nuclear power that can provide us with the baseload requirement, we can better direct renewables to provide us with power intermittently, without relying too much on batteries. I long to see the day when the whole world is powered by safe and affordable energy without any GHG emissions.