commit d3118da20781a2118bcdf273486b8909ec94515e Author: niten Date: Tue Jul 25 18:37:43 2023 -0700 Initial checkin diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..83881d4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,17 @@ +{ + description = "Mastodon running in a container"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-23.05"; + arion.url = "github:hercules-ci/arion"; + }; + + outputs = { self, nixpkgs, arion, ... }: { + nixosModules = rec { + default = mastodonContainer; + mastodonContainer = { ... }: { + imports = [ arion.nixosModules.arion ./mastodon-container.nix ]; + }; + }; + }; +} diff --git a/mastodon-container.nix b/mastodon-container.nix new file mode 100644 index 0000000..ed25851 --- /dev/null +++ b/mastodon-container.nix @@ -0,0 +1,164 @@ +{ config, lib, pkgs, ... }@toplevel: + +with lib; +let cfg = config.services.mastodonContainer; + +in { + options.services.mastodonContainer = { + enable = mkEnableOption "Enable Mastodon running in an Arion container."; + + version = mkOption { + type = str; + description = "Version of Mastodon to launch."; + }; + + images = { + mastodon = mkOption { + type = str; + description = "Docker image to use for Mastodon."; + default = + "ghcr.io/mastodon/mastodon:${toplevel.config.services.mastodonContainer.version}"; + }; + + postgres = mkOption { + type = str; + description = "Docker image to use for PostgreSQL server."; + default = "postgres:15-alpine"; + }; + + redis = mkOption { + type = str; + description = "Docker image to use for Redis server."; + default = "redis:7-alpine"; + }; + }; + + state-directory = mkOption { + type = str; + description = "Port at which to store server data."; + }; + + ports = { + web = mkOption { + type = port; + description = "Port at which to serve Mastodon web requests."; + default = 3000; + }; + streaming = mkOption { + type = port; + description = "Port at which to serve Mastodon streaming requests."; + default = 4000; + }; + }; + + uids = { + mastodon = mkOption { + type = int; + description = "UID as which to run Mastodon."; + default = 730; + }; + postgres = mkOption { + type = int; + description = "UID as which to run PostgreSQL."; + default = 731; + }; + redis = mkOption { + type = int; + description = "UID as which to run Redis."; + default = 732; + }; + }; + }; + + config = mkIf cfg.enable { + users.users = { + mastodon = { + isSystemUser = true; + group = "mastodon"; + uid = cfg.uids.mastodon; + }; + mastodon-postgres = { + isSystemUser = true; + group = "mastodon"; + uid = cfg.uids.postgres; + }; + mastodon-redis = { + isSystemUser = true; + group = "mastodon"; + uid = cfg.uids.redis; + }; + }; + + virtualisation.arion.projects.mastodon.settings = let + mkUserMap = uid: "${toString uid}:${toString uid}"; + image = { pkgs, ... }: { + project.name = "mastodon"; + networks = { + internal_network.internal = true; + external_network.internal = false; + }; + services = { + postgres.service = { + image = cfg.images.postgres; + restart = "always"; + volumes = + [ "${cfg.state-directory}/postgres:/var/lib/postgresql/data" ]; + healthcheck.test = [ "CMD" "pg_isready" "-U" "postgres" ]; + environment.POSTGRES_HOST_AUTH_METHOD = "trust"; + networks = [ "internal_network" ]; + user = mkUserMap cfg.uids.postgres; + }; + redis.service = { + image = cfg.images.redis; + restart = "always"; + volumes = [ "${cfg.state-directory}/redis:/data" ]; + healthcheck.test = [ "CMD" "redis-cli" "ping" ]; + networks = [ "internal_network" ]; + user = mkUserMap cfg.uids.redis; + }; + web.service = { + # TODO: bulid image? + image = cfg.images.mastodon; + restart = "always"; + volumes = + [ "${cfg.state-directory}/mastodon:/mastodon/public/system" ]; + command = '' + bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"''; + healthcheck.test = [ + "CMD-SHELL" + "wget -q --spider --proxy=off localhost:3000/health || exit 1" + ]; + ports = [ "${toString cfg.ports.web}:3000" ]; + depends_on = [ "postgres" "redis" ]; + networks = [ "internal_network" "external_network" ]; + user = mkUserMap cfg.uids.mastodon; + }; + streaming.service = { + image = cfg.images.mastodon; + restart = "always"; + command = "node ./streaming"; + healthcheck.test = [ + "CMD-SHELL" + "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1" + ]; + ports = [ "${toString cfg.ports.streaming}:4000" ]; + depends_on = [ "postgres" "redis" ]; + networks = [ "internal_network" "external_network" ]; + }; + sidekiq.service = { + image = cfg.images.mastodon; + restart = "always"; + volumes = + [ "${cfg.state-directory}/mastodon:/mastodon/public/system" ]; + command = "bundle exec sidekiq"; + healthcheck.test = + [ "CMD-SHELL" "ps aux | grep '[s]idekiq 6' || false" ]; + depends_on = [ "postgres" "redis" ]; + networks = [ "internal_network" "external_network" ]; + user = mkUserMap cfg.uids.mastodon; + }; + }; + }; + in { imports = [ image ]; }; + }; +}