Created
October 5, 2025 07:45
-
-
Save SamirTalwar/39588ad591f52cd5abfc130bd2aead05 to your computer and use it in GitHub Desktop.
How I deploy Mastodon on NixOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| version, | |
| environmentFiles, | |
| redisPort, | |
| redisPassword, | |
| }: | |
| { config, pkgs, ... }: | |
| let | |
| image = pkgs.dockerTools.pullImage { | |
| imageName = "ghcr.io/mastodon/mastodon"; | |
| imageDigest = "sha256:e8ad3b126c71ed5d7df24d1257754a45338d5158e9dd88255accdf958a676ca6"; | |
| hash = "sha256-5BTT1KTve/jQfGCqVjlwQnLt2otOYL944aGn5HRGjmg="; | |
| finalImageName = "ghcr.io/mastodon/mastodon"; | |
| finalImageTag = "v4.4.5"; | |
| }; | |
| streamingImage = pkgs.dockerTools.pullImage { | |
| imageName = "ghcr.io/mastodon/mastodon-streaming"; | |
| imageDigest = "sha256:b2d6aefcc160dd4e60b196a814e6a803e6a5e073b17a1f9453b1e42afafad2f8"; | |
| hash = "sha256-h4f0UHFNRJjUq4cRyyagpzyn5CewZbgiZk1YWa/zcsQ="; | |
| finalImageName = "ghcr.io/mastodon/mastodon-streaming"; | |
| finalImageTag = "v4.4.5"; | |
| }; | |
| in | |
| { | |
| services.redis.servers.mastodon = { | |
| enable = true; | |
| port = redisPort; | |
| requirePass = redisPassword; | |
| bind = null; | |
| }; | |
| # Allow containers to access the databases. | |
| networking.firewall.interfaces."podman1".allowedTCPPorts = [ | |
| redisPort | |
| ]; | |
| networking.firewall.interfaces."podman2".allowedTCPPorts = [ | |
| redisPort | |
| ]; | |
| systemd.services."podman-network-mastodon" = { | |
| description = "Create Podman mastodon network"; | |
| wantedBy = [ "multi-user.target" ]; | |
| before = [ | |
| "podman-mastodon-web.service" | |
| "podman-mastodon-streaming.service" | |
| "podman-mastodon-sidekiq.service" | |
| ]; | |
| serviceConfig = { | |
| Type = "oneshot"; | |
| RemainAfterExit = true; | |
| Environment = [ | |
| "PATH=/run/wrappers/bin:${pkgs.lib.makeBinPath [ pkgs.podman ]}" | |
| ]; | |
| }; | |
| script = '' | |
| if ! podman network exists mastodon; then | |
| podman network create --subnet=10.89.1.0/24 mastodon | |
| fi | |
| ''; | |
| }; | |
| virtualisation.oci-containers.containers.mastodon-web = { | |
| inherit environmentFiles; | |
| autoStart = true; | |
| image = "${image.imageName}:${image.imageTag}"; | |
| imageFile = image; | |
| cmd = [ | |
| "bundle" | |
| "exec" | |
| "rails" | |
| "server" | |
| "--port=3000" | |
| ]; | |
| networks = [ | |
| "gateway" | |
| "mastodon" | |
| ]; | |
| }; | |
| virtualisation.oci-containers.containers.mastodon-streaming = { | |
| inherit environmentFiles; | |
| autoStart = true; | |
| image = "${streamingImage.imageName}:${streamingImage.imageTag}"; | |
| imageFile = streamingImage; | |
| cmd = [ | |
| "node" | |
| "./streaming" | |
| ]; | |
| networks = [ | |
| "gateway" | |
| "mastodon" | |
| ]; | |
| }; | |
| virtualisation.oci-containers.containers.mastodon-sidekiq = { | |
| inherit environmentFiles; | |
| autoStart = true; | |
| image = "${image.imageName}:${image.imageTag}"; | |
| imageFile = image; | |
| cmd = [ | |
| "bundle" | |
| "exec" | |
| "sidekiq" | |
| ]; | |
| networks = [ "mastodon" ]; | |
| }; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| RAILS_ENV=production | |
| NODE_ENV=production | |
| SINGLE_USER_MODE=true | |
| # Federation | |
| # ---------- | |
| # This identifies your server and cannot be changed safely later | |
| # ---------- | |
| LOCAL_DOMAIN=test.example | |
| # Redis | |
| # ----- | |
| REDIS_HOST=host.containers.internal | |
| REDIS_PORT=6999 | |
| REDIS_PASSWORD=B9Fx1oP1c9YXI1SgtZxKCwH3mCjwrJHT | |
| # PostgreSQL | |
| # ---------- | |
| DATABASE_URL=postgresql://[email protected]:5432/mastodon | |
| # Elasticsearch (optional) | |
| # ------------------------ | |
| # ES_ENABLED=true | |
| # ES_HOST=localhost | |
| # ES_PORT=9200 | |
| # Authentication for ES (optional) | |
| # ES_USER=elastic | |
| # ES_PASS=password | |
| # Secrets | |
| # ------- | |
| # Make sure to use `bundle exec rails secret` to generate secrets | |
| # ------- | |
| SECRET_KEY_BASE=8ce80daefd414e9fc3d939947cc52daa188709b52f595f0d638632bd082a455f029092594fe643b886b89106e52fd9a5a57f6514e6b298a63dd977fb43fffc1d | |
| OTP_SECRET=d4a7f1b9b3195a15ee691c28173445f5193052614fedae56c419cb2f7c633e14185c71e9fab483b803c3afe04e42e5fe04b526573de5da38cb583cf0e524a5b1 | |
| # Active Record Encryption | |
| # ------- | |
| # Generate with `bundle exec rails db:encryption:init` | |
| # ------- | |
| ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=yj81XmDT05N1U78pxKjRIXGwlhMbWXno | |
| ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=fvMus8CpfcA1tFd37pRJCH94psEuj8yj | |
| ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=M4yB0UthqVaBashk5tc0Tzsbr8AL5bTl | |
| # Web Push | |
| # -------- | |
| # Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` | |
| # -------- | |
| VAPID_PRIVATE_KEY=2QDAtJhwNn7qkfOq3w9ij1sGigqwkNLvNgG3qiLsquw= | |
| VAPID_PUBLIC_KEY=BDvDgI_vBrqPRDJD4O5Mmj3LbO-Ujsw-OQSTWh-rl2BfSx2W2SfyQQtqtGoIQ6GJV7cerK0QIMCv3Z5v4iJDoS8= | |
| # Sending mail | |
| # ------------ | |
| # SMTP_SERVER= | |
| # SMTP_PORT= | |
| # SMTP_LOGIN= | |
| # SMTP_PASSWORD= | |
| # SMTP_FROM_ADDRESS= | |
| # File storage (optional) | |
| # ----------------------- | |
| S3_ENABLED=false | |
| # S3_ENDPOINT= | |
| # S3_HOSTNAME= | |
| # S3_ALIAS_HOST= | |
| # S3_BUCKET= | |
| # AWS_ACCESS_KEY_ID= | |
| # AWS_SECRET_ACCESS_KEY= | |
| # 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| let | |
| pkgs = import ../../_pkgs; | |
| in | |
| pkgs.testers.runNixOSTest { | |
| name = "mastodon"; | |
| nodes = { | |
| machine = | |
| { ... }: | |
| { | |
| # The default disk image doesn't have enough space to unpack the images. | |
| # We therefore switch to a tmpfs-backed image (using `null`), and | |
| # allocate 4 GB of RAM to ensure it has enough space. | |
| virtualisation.diskImage = null; | |
| virtualisation.memorySize = 4096; | |
| imports = [ | |
| ../common.nix | |
| ../gateway-network.nix | |
| (import ./. { | |
| version = "4.4.5"; | |
| environmentFiles = [ | |
| (pkgs.writeText "env" (builtins.readFile ./test.env)) | |
| ]; | |
| redisPort = 6999; | |
| redisPassword = "B9Fx1oP1c9YXI1SgtZxKCwH3mCjwrJHT"; | |
| }) | |
| ]; | |
| # Make sure the gateway network is created before the app. | |
| systemd.services."podman-network-gateway".before = [ | |
| "podman-mastodon-web.service" | |
| ]; | |
| # Make sure the gateway network is created before the app. | |
| systemd.services."podman-mastodon-web".before = [ | |
| "podman-mastodon-streaming.service" | |
| "podman-mastodon-sidekiq.service" | |
| ]; | |
| virtualisation.oci-containers.containers.mastodon-web = { | |
| # Run migrations first. | |
| cmd = pkgs.lib.mkForce [ | |
| "bash" | |
| "-ex" | |
| "-c" | |
| '' | |
| bundle exec rake db:migrate | |
| bundle exec rails server --port=3000 | |
| '' | |
| ]; | |
| # Open the port directly. | |
| ports = [ | |
| "3000:3000/tcp" | |
| ]; | |
| }; | |
| virtualisation.oci-containers.containers.mastodon-streaming = { | |
| # Open the port directly. | |
| ports = [ | |
| "4000:4000/tcp" | |
| ]; | |
| }; | |
| # Set up the database, allowing any container to log in. | |
| services.postgresql = { | |
| enable = true; | |
| enableTCPIP = true; | |
| ensureDatabases = [ "mastodon" ]; | |
| ensureUsers = [ | |
| { | |
| name = "mastodon"; | |
| ensureDBOwnership = true; | |
| ensureClauses = { | |
| login = true; | |
| }; | |
| } | |
| ]; | |
| authentication = '' | |
| #type database user origin-address auth-method | |
| host sameuser all 10.88.0.0/14 trust # services running via Podman, over TCP | |
| ''; | |
| }; | |
| # Allow containers to access the databases. | |
| networking.firewall.interfaces."podman1".allowedTCPPorts = [ | |
| 5432 # postgresql | |
| ]; | |
| networking.firewall.interfaces."podman2".allowedTCPPorts = [ | |
| 5432 # postgresql | |
| ]; | |
| }; | |
| }; | |
| testScript = '' | |
| start_all() | |
| machine.wait_for_unit("multi-user.target") | |
| machine.wait_for_open_port(3000) | |
| machine.wait_for_open_port(4000) | |
| ''; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment