Switch to one nixos-using image
This commit is contained in:
parent
874a48bbfa
commit
97d4dafd9b
|
@ -1,154 +1,21 @@
|
||||||
{ config, lib, pkgs, ... }@toplevel:
|
{ config, lib, pkgs, ... }@toplevel:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
let
|
let cfg = config.services.mastodonContainer;
|
||||||
cfg = config.services.mastodonContainer;
|
|
||||||
|
|
||||||
hostSecrets = config.fudo.secrets.host-secrets."${config.instance.hostname}";
|
|
||||||
|
|
||||||
makeEnvFile = envVars:
|
|
||||||
let
|
|
||||||
envLines =
|
|
||||||
mapAttrsToList (var: val: ''${var}="${toString val}"'') envVars;
|
|
||||||
in pkgs.writeText "envFile" (concatStringsSep "\n" envLines);
|
|
||||||
|
|
||||||
databasePasswd = readFile
|
|
||||||
(pkgs.lib.passwd.stablerandom-passwd-file "mastodon-db-passwd"
|
|
||||||
config.instance.build-seed);
|
|
||||||
|
|
||||||
secretKeyBase = readFile
|
|
||||||
(pkgs.lib.passwd.stablerandom-passwd-file "mastodon-secret-key-base"
|
|
||||||
config.instance.build-seed);
|
|
||||||
|
|
||||||
otpSecret = readFile (pkgs.lib.passwd.stablerandom-passwd-file "mastodon-otp"
|
|
||||||
config.instance.build-seed);
|
|
||||||
|
|
||||||
proxyConf = pkgs.writeText "mastodon-nginx.conf" ''
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
upstream backend {
|
|
||||||
server web:3000;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream streaming {
|
|
||||||
server streaming:4000;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 3000;
|
|
||||||
server_name localhost;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_disable "msie6";
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_comp_level 6;
|
|
||||||
gzip_buffers 16 8k;
|
|
||||||
gzip_http_version 1.1;
|
|
||||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri @proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ /(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
|
|
||||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
||||||
try_files $uri @proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /sw.js {
|
|
||||||
add_header Cache-Control "public, max-age=0";
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
||||||
try_files $uri @proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
location @proxy {
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header Proxy "";
|
|
||||||
proxy_pass_header Server;
|
|
||||||
proxy_pass http://backend;
|
|
||||||
proxy_buffering on;
|
|
||||||
proxy_redirect off;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
proxy_cache CACHE;
|
|
||||||
proxy_cache_valid 200 7d;
|
|
||||||
proxy_cache_valid 410 24h;
|
|
||||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
|
||||||
add_header X-Cached $upstream_cache_status;
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
||||||
tcp_nodelay on;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api/v1/streaming {
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header Proxy "";
|
|
||||||
proxy_pass http://streaming;
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_redirect off;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
tcp_nodelay on;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
in {
|
in {
|
||||||
options.services.mastodonContainer = with types; {
|
|
||||||
|
options.services.mastodonContainer = {
|
||||||
enable = mkEnableOption "Enable Mastodon running in an Arion container.";
|
enable = mkEnableOption "Enable Mastodon running in an Arion container.";
|
||||||
|
|
||||||
|
domain = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Domain name of this Mastodon instance.";
|
||||||
|
};
|
||||||
|
|
||||||
hostname = mkOption {
|
hostname = mkOption {
|
||||||
type = str;
|
type = str;
|
||||||
description = "Hostname of this Mastodon instance.";
|
description = "Hostname of the Mastodon server.";
|
||||||
};
|
default = toplevel.config.services.mastodonContainer.domain;
|
||||||
|
|
||||||
web-domain = mkOption {
|
|
||||||
type = str;
|
|
||||||
description = "Domain name to attach to users, eg @user@web.domain";
|
|
||||||
default = toplevel.config.services.mastodonContainer.hostname;
|
|
||||||
};
|
|
||||||
|
|
||||||
version = mkOption {
|
|
||||||
type = str;
|
|
||||||
description = "Version of Mastodon to launch.";
|
|
||||||
};
|
|
||||||
|
|
||||||
images = {
|
|
||||||
mastodon = mkOption {
|
|
||||||
type = str;
|
|
||||||
description = "Docker image to use for Mastodon.";
|
|
||||||
default =
|
|
||||||
"tootsuite/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";
|
|
||||||
};
|
|
||||||
|
|
||||||
nginx = mkOption {
|
|
||||||
type = str;
|
|
||||||
description = "Docker image to use for Proxy server.";
|
|
||||||
default = "nginx:alpine-slim";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
state-directory = mkOption {
|
state-directory = mkOption {
|
||||||
|
@ -162,14 +29,15 @@ in {
|
||||||
default = 3000;
|
default = 3000;
|
||||||
};
|
};
|
||||||
|
|
||||||
environment = mkOption {
|
environment-files = mkOption {
|
||||||
type = attrsOf str;
|
type = listOf str;
|
||||||
description = "Environment variables to set for the Mastodon job.";
|
description =
|
||||||
default = { };
|
"List of files with env variables to set for the Mastodon job.";
|
||||||
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
smtp = {
|
smtp = {
|
||||||
server = mkOption {
|
host = mkOption {
|
||||||
type = str;
|
type = str;
|
||||||
description = "Outgoing SMTP server.";
|
description = "Outgoing SMTP server.";
|
||||||
};
|
};
|
||||||
|
@ -179,194 +47,78 @@ in {
|
||||||
description = "Outgoing SMTP server port.";
|
description = "Outgoing SMTP server port.";
|
||||||
default = 25;
|
default = 25;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "User as which to authenticate to the SMTP server.";
|
||||||
};
|
};
|
||||||
|
|
||||||
uids = {
|
password-file = mkOption {
|
||||||
mastodon = mkOption {
|
type = str;
|
||||||
type = int;
|
description = "Path to file containing SMTP password";
|
||||||
description = "UID as which to run Mastodon.";
|
|
||||||
default = 991;
|
|
||||||
};
|
};
|
||||||
postgres = mkOption {
|
|
||||||
type = int;
|
from-address = mkOption {
|
||||||
description = "UID as which to run PostgreSQL.";
|
type = str;
|
||||||
default = 992;
|
description = "Address from which to send outgoing mail.";
|
||||||
};
|
default =
|
||||||
redis = mkOption {
|
"${toplevel.config.services.mastodonContainer.smtp.user}@${toplevel.config.services.mastodonContainer.domain}";
|
||||||
type = int;
|
|
||||||
description = "UID as which to run Redis.";
|
|
||||||
default = 993;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
groups.mastodon = {
|
|
||||||
members = [ "mastodon" "mastodon-postgres" "mastodon-redis" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
fudo.secrets.host-secrets."${config.instance.hostname}" = let
|
|
||||||
in {
|
|
||||||
mastodonCommonEnv = {
|
|
||||||
source-file = makeEnvFile {
|
|
||||||
LOCAL_DOMAIN = cfg.hostname;
|
|
||||||
WEB_DOMAIN = cfg.web-domain;
|
|
||||||
REDIS_HOST = "redis";
|
|
||||||
REDIS_PORT = 6379;
|
|
||||||
SMTP_SERVER = cfg.smtp.server;
|
|
||||||
SMTP_PORT = toString cfg.smtp.port;
|
|
||||||
SMTP_FROM_ADDRESS = "noreply@${cfg.web-domain}";
|
|
||||||
SECRET_KEY_BASE = secretKeyBase;
|
|
||||||
OTP_SECRET = otpSecret;
|
|
||||||
};
|
|
||||||
target-file = "/run/mastodon/common.env";
|
|
||||||
};
|
|
||||||
mastodonPostgresEnv = {
|
|
||||||
source-file = makeEnvFile {
|
|
||||||
POSTGRES_USER = "mastodon";
|
|
||||||
POSTGRES_PASSWORD = databasePasswd;
|
|
||||||
POSTGRES_DB = "mastodon";
|
|
||||||
};
|
|
||||||
target-file = "/run/mastodon/postgres.env";
|
|
||||||
};
|
|
||||||
mastodonEnv = {
|
|
||||||
source-file = makeEnvFile ({
|
|
||||||
DB_HOST = "postgres";
|
|
||||||
DB_USER = "mastodon";
|
|
||||||
DB_NAME = "mastodon";
|
|
||||||
DB_PASS = databasePasswd;
|
|
||||||
} // cfg.environment);
|
|
||||||
target-file = "/run/mastodon/mastodon.env";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd = {
|
|
||||||
tmpfiles.rules = [
|
|
||||||
"d ${cfg.state-directory}/mastodon-opt 0700 mastodon root - -"
|
|
||||||
"d ${cfg.state-directory}/mastodon 0700 mastodon root - -"
|
|
||||||
"d ${cfg.state-directory}/postgres 0700 mastodon-postgres root - -"
|
|
||||||
"d ${cfg.state-directory}/redis 0700 mastodon-redis root - -"
|
|
||||||
];
|
|
||||||
services.arion-mastodon = {
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
requires = [ "network-online.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.arion.projects.mastodon.settings = let
|
virtualisation.arion.projects.mastodon.settings = let
|
||||||
mkUserMap = uid: "${toString uid}:${toString uid}";
|
|
||||||
image = { pkgs, ... }: {
|
image = { pkgs, ... }: {
|
||||||
project.name = "mastodon";
|
project.name = "mastodon";
|
||||||
networks = {
|
|
||||||
internal_network = { internal = true; };
|
|
||||||
external_network = { internal = false; };
|
|
||||||
};
|
|
||||||
services = {
|
services = {
|
||||||
proxy.service = {
|
mastodon = { pkgs, ... }: {
|
||||||
image = cfg.images.nginx;
|
useSystemd = true;
|
||||||
|
service = {
|
||||||
restart = "always";
|
restart = "always";
|
||||||
ports = [ "${toString cfg.port}:3000" ];
|
volumes = [
|
||||||
volumes = [ "${proxyConf}:/etc/nginx/nginx.conf:ro,Z" ];
|
"postgres-data:/var/lib/postgres/data"
|
||||||
depends_on = [ "web" "streaming" ];
|
"redis-data:/var/lib/redis"
|
||||||
networks = [ "external_network" "internal_network" ];
|
"mastodon-data:/var/lib/mastodon"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
postgres.service = {
|
configuration = {
|
||||||
image = cfg.images.postgres;
|
boot.tmp.useTmpfs = true;
|
||||||
restart = "always";
|
system.nssModules = mkForce [ ];
|
||||||
volumes =
|
services = {
|
||||||
[ "${cfg.state-directory}/postgres:/var/lib/postgresql/data" ];
|
nscd.enable = false;
|
||||||
healthcheck.test = [ "CMD" "pg_isready" "-U" "mastodon" ];
|
postgresql.enable = true;
|
||||||
user = mkUserMap cfg.uids.postgres;
|
mastodon = {
|
||||||
env_file = [
|
enable = true;
|
||||||
hostSecrets.mastodonCommonEnv.target-file
|
localDomain = cfg.domain;
|
||||||
hostSecrets.mastodonPostgresEnv.target-file
|
extraEnvFiles = cfg.environment-files;
|
||||||
];
|
smtp = {
|
||||||
networks = [ "internal_network" ];
|
inherit (cfg.smtp) host port user;
|
||||||
|
fromAddress = cfg.smtp.from-address;
|
||||||
|
authenticate = true;
|
||||||
|
passwordFile = cfg.smtp.password-file;
|
||||||
};
|
};
|
||||||
redis.service = {
|
redis.createLocally = true;
|
||||||
image = cfg.images.redis;
|
database.createLocally = true;
|
||||||
hostname = "redis";
|
configureNginx = true;
|
||||||
restart = "always";
|
automaticMigrations = true;
|
||||||
volumes = [ "${cfg.state-directory}/redis:/data" ];
|
|
||||||
healthcheck.test = [ "CMD" "redis-cli" "ping" ];
|
|
||||||
user = mkUserMap cfg.uids.redis;
|
|
||||||
env_file = [ hostSecrets.mastodonCommonEnv.target-file ];
|
|
||||||
networks = [ "internal_network" ];
|
|
||||||
};
|
};
|
||||||
web.service = {
|
|
||||||
image = cfg.images.mastodon;
|
|
||||||
hostname = "web";
|
|
||||||
restart = "always";
|
|
||||||
volumes =
|
|
||||||
[ "${cfg.state-directory}/mastodon:/mastodon/public/system" ];
|
|
||||||
# command = ''bash -c "while :; do echo 'kill me'; sleep 1; done"'';
|
|
||||||
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"
|
|
||||||
];
|
|
||||||
depends_on = [ "postgres" "redis" ];
|
|
||||||
user = mkUserMap cfg.uids.mastodon;
|
|
||||||
env_file = [
|
|
||||||
hostSecrets.mastodonCommonEnv.target-file
|
|
||||||
hostSecrets.mastodonEnv.target-file
|
|
||||||
];
|
|
||||||
networks = [ "internal_network" "external_network" ];
|
|
||||||
};
|
};
|
||||||
streaming.service = {
|
|
||||||
image = cfg.images.mastodon;
|
|
||||||
hostname = "streaming";
|
|
||||||
restart = "always";
|
|
||||||
command = "node ./streaming";
|
|
||||||
healthcheck.test = [
|
|
||||||
"CMD-SHELL"
|
|
||||||
"wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"
|
|
||||||
];
|
|
||||||
depends_on = [ "postgres" "redis" ];
|
|
||||||
env_file = [
|
|
||||||
hostSecrets.mastodonCommonEnv.target-file
|
|
||||||
hostSecrets.mastodonEnv.target-file
|
|
||||||
];
|
|
||||||
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" ];
|
|
||||||
user = mkUserMap cfg.uids.mastodon;
|
|
||||||
env_file = [
|
|
||||||
hostSecrets.mastodonCommonEnv.target-file
|
|
||||||
hostSecrets.mastodonEnv.target-file
|
|
||||||
];
|
|
||||||
networks = [ "internal_network" "external_network" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in { imports = [ image ]; };
|
in { imports = [ image ]; };
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
virtualHosts."${cfg.hostname}" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://localhost:${cfg.port}";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue