{ config, pkgs, lib, ... }: let secrets = import ./secrets.nix; mastodon-source = pkgs.callPackage "${toString pkgs.path}/pkgs/servers/mastodon/source.nix" {}; mastodon-lor-sh = pkgs.mastodon.override { srcOverride = pkgs.applyPatches { version = mastodon-source.version; yarnHash = mastodon-source.yarnHash; src = pkgs.fetchFromGitHub { owner = mastodon-source.owner; repo = mastodon-source.repo; rev = mastodon-source.rev; hash = mastodon-source.outputHash; }; patches = [ ./patches/logo.patch ./patches/app-icon.patch ./patches/logo-symbol-wordmark.patch ./patches/mascot.patch ./patches/add-tango-theme.patch ./patches/add-merveilles-theme.patch ./patches/add-black-theme.patch ./patches/themes-config.patch ./patches/fix-mastodon-light-highlight-color.patch ./patches/fix-character-limit.patch ./patches/max-toot-chars-api.patch ./patches/simple-form.patch ./patches/not-so-scary-500.patch ] ++ mastodon-source.patches; postPatch = (import ./branding.nix { pkgs = pkgs; }).branding; }; }; sidekiq-manager = pkgs.writers.writePython3 "sidekiq-manager" {} '' from itertools import permutations from subprocess import Popen def sidekiq(queues, connections=16): mastodon = "${mastodon-lor-sh}" cmd = [f"{mastodon}/bin/sidekiq", "-r", mastodon] cmd += ["-c", f"{connections}"] for q in queues: cmd += ['-q', q] return Popen(cmd) procs = [sidekiq(['mailers', 'pull'])] queues = ['default', 'push', 'ingress'] procs += [sidekiq(qs) for qs in permutations(queues)] p = sidekiq([]) p.wait() ''; s3cmd = pkgs.writeShellScript "s3cmd" '' ${pkgs.s3cmd}/bin/s3cmd \ --access_key='${secrets.backup.accessKey}' \ --secret_key='${secrets.backup.secretKey}' \ --host='${secrets.backup.host}' \ --host-bucket='${secrets.backup.hostBucket}' \ $@ ''; auth = pkgs.buildGoModule rec { name = "auth"; src = ./auth; vendorHash = "sha256-cLn1tZL+LVMmSpLZYA7uRkEW7eFWGf+NFdvBEvQtjH4="; }; bucket = secrets.backup.bucket; domainName = "lor.sh"; in { systemd.services."mastodon-auth" = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; environment = { SOCKET = "/var/run/mastodon-auth/auth.socket"; DATABASE = "mastodon"; AUTH_SECRET = secrets.authSecret; }; serviceConfig = { Restart = "always"; RestartSec = 30; ExecStart = "${auth}/bin/auth"; User = "mastodon"; RuntimeDirectory = "mastodon-auth"; RuntimeDirectoryMode = "0750"; }; }; services.postgresqlBackup = { enable = true; databases = [ "mastodon" ]; compression = "gzip"; }; systemd.services.postgresqlBackup-mastodon.serviceConfig.ExecStartPost = pkgs.writeShellScript "backup-to-s3" '' cd /var/backup/postgresql ${pkgs.gnupg}/bin/gpg --batch --passphrase '${secrets.backup.password}' \ --symmetric mastodon.sql.gz ${s3cmd} rm ${bucket}/mastodon.prev.sql.gz.gpg ${s3cmd} mv ${bucket}/mastodon.sql.gz.gpg ${bucket}/mastodon.prev.sql.gz.gpg ${s3cmd} put mastodon.sql.gz.gpg ${bucket}/ rm mastodon.sql.gz.gpg ''; systemd.services.mastodon-sidekiq-all.serviceConfig.ExecStart = lib.mkForce "${sidekiq-manager}"; # https://github.com/mperham/sidekiq/wiki/Memory#bloat systemd.services.mastodon-sidekiq-all.environment.MALLOC_ARENA_MAX = "2"; services.caddy = { enable = true; virtualHosts."${domainName}".extraConfig = '' root * ${mastodon-lor-sh}/public encode gzip @static file handle @static { file_server } handle /api/v1/streaming* { reverse_proxy unix//run/mastodon-streaming/streaming-1.socket } handle /api/v0/auth* { reverse_proxy unix//run/mastodon-auth/auth.socket } handle { reverse_proxy unix//run/mastodon-web/web.socket } header { Strict-Transport-Security "max-age=31536000;" } @5xx expression `{err.status_code} >= 500 && {err.status_code} < 600` handle_errors { rewrite @5xx /500.html file_server } ''; }; users.users.caddy.extraGroups = [ "mastodon" ]; services.mastodon = { enable = true; package = mastodon-lor-sh; localDomain = "${domainName}"; configureNginx = false; streamingProcesses = 8; smtp = { createLocally = false; authenticate = true; host = "smtp.eu.mailgun.org"; port = 587; fromAddress = "Mastodon "; user = "mastodon@m.${domainName}"; passwordFile = builtins.toFile "smtp-password" secrets.smtpPassword; }; vapidPublicKeyFile = builtins.toFile "vapidPublicKey" secrets.vapidPublicKey; secretKeyBaseFile = builtins.toFile "secretKeyBase" secrets.secretKeyBase; otpSecretFile = builtins.toFile "otpSecret" secrets.otpSecret; vapidPrivateKeyFile = builtins.toFile "vapidPrivateKey" secrets.vapidPrivateKey; extraConfig = { S3_ENABLED = "true"; S3_PROTOCOL = "https"; S3_BUCKET = "lor-sh"; S3_REGION = "eu-central-1"; S3_HOSTNAME = "s3.eu-central-1.wasabisys.com"; S3_ENDPOINT = "https://s3.eu-central-1.wasabisys.com/lor-sh"; S3_ALIAS_HOST = "s3.eu-central-1.wasabisys.com/lor-sh/lor-sh"; AWS_ACCESS_KEY_ID = secrets.AWS_ACCESS_KEY_ID; AWS_SECRET_ACCESS_KEY = secrets.AWS_SECRET_ACCESS_KEY; DEEPL_API_KEY = secrets.DEEPL_API_KEY; DEEPL_PLAN = "pro"; }; }; }