blob: e94f5f8af8cf7b2304b17e18267271b682828d49 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
{ lib, pkgs, ... }:
# Self-hosted TypeType instance: https://github.com/TypeType-Video/TypeType
#
# Upstream only ships container images, so this is a translation of their
# docker-compose.yml rather than a native service. Omitted from the upstream
# stack: typetype-downloader, garage, garage-config (the download/S3
# subsystem) and typetype-secrets (replaced by /var/secrets, below).
# TODO downloads: needs typetype-downloader + a Garage bucket bootstrapped by
# hand (scripts/bootstrap-garage.sh does layout assign / bucket create / key
# create), plus the typetype_downloader database.
# TODO SSO
let
network = "typetype";
# The frontend image's nginx resolves these names over Docker's embedded DNS
# (resolver 127.0.0.11), so retain the original container names.
containers = [
"typetype"
"typetype-server"
"typetype-token"
"typetype-postgres"
"typetype-dragonfly"
];
# Pin by version tag and digest.
images = {
web = "ghcr.io/typetype-video/typetype:1.3.1@sha256:4da200fb96d858cfa3bc2a8cbb98a9682a560f40a055b9c407f3e173a28dcf82";
server = "ghcr.io/typetype-video/typetype-server:1.3.1@sha256:f1ad7fd31e5c1cb994601f714df82e8207c3769d759df232e21a3876751a8faf";
token = "ghcr.io/typetype-video/typetype-token:1.3.1@sha256:8dfcc6d84cc09c33d18add0ec807093c2182be10857a021a4c61ace9a3f561d5";
};
secrets = "/var/secrets/typetype";
in
{
systemd.tmpfiles.rules = [
"d /var/lib/typetype 0750 root root -"
# Bind mounted rather than a Docker volume so backup.nix picks it up; 999
# is the postgres uid inside the image.
"d /var/lib/typetype/postgres 0700 999 999 -"
"d ${secrets} 0750 root root -"
];
systemd.services =
lib.genAttrs (map (c: "docker-${c}") containers) (_: {
after = [ "docker-network-typetype.service" ];
requires = [ "docker-network-typetype.service" ];
unitConfig.AssertPathExists = "${secrets}/env";
})
// {
# Initially create network.
docker-network-typetype = {
wantedBy = [ "multi-user.target" ];
after = [ "docker.service" ];
requires = [ "docker.service" ];
path = [ pkgs.docker ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
docker network inspect ${network} >/dev/null 2>&1 ||
docker network create ${network}
'';
};
};
virtualisation.oci-containers.containers = {
typetype = {
image = images.web;
networks = [ network ];
dependsOn = [
"typetype-server"
"typetype-token"
];
ports = [ "127.0.0.1:8082:80" ];
};
typetype-server = {
image = images.server;
networks = [ network ];
dependsOn = [
"typetype-postgres"
"typetype-dragonfly"
"typetype-token"
];
# Sets DATABASE_PASSWORD.
environmentFiles = [ "${secrets}/env" ];
environment = {
ALLOWED_ORIGINS = "https://tt.elmo.mou.fo";
DATABASE_URL = "jdbc:postgresql://typetype-postgres:5432/typetype";
DATABASE_USER = "typetype";
DRAGONFLY_URL = "redis://typetype-dragonfly:6379";
YOUTUBE_REMOTE_LOGIN_ENABLED = "false";
YOUTUBE_REMOTE_LOGIN_SERVICE_URL = "http://typetype-token:8081";
YOUTUBE_REMOTE_LOGIN_CALLBACK_BASE_URL = "http://typetype-server:8080";
YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE = "/run/typetype-secrets/youtube_remote_login_internal_token";
YOUTUBE_SESSION_ENCRYPTION_KEY_FILE = "/run/typetype-secrets/youtube_session_encryption_key";
};
volumes = [ "${secrets}:/run/typetype-secrets:ro" ];
};
typetype-token = {
image = images.token;
networks = [ network ];
environment = {
NODE_ENV = "production";
YOUTUBE_REMOTE_LOGIN_ENABLED = "false";
YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE = "/run/typetype-secrets/youtube_remote_login_internal_token";
};
volumes = [ "${secrets}:/run/typetype-secrets:ro" ];
# --ipc=host is upstream's; it only matters once remote login is enabled
# and the service starts driving a headless browser.
extraOptions = [
"--init"
"--ipc=host"
];
};
typetype-postgres = {
image = "postgres:17";
networks = [ network ];
# Sets POSTGRES_PASSWORD.
environmentFiles = [ "${secrets}/env" ];
environment = {
POSTGRES_DB = "typetype";
POSTGRES_USER = "typetype";
};
volumes = [ "/var/lib/typetype/postgres:/var/lib/postgresql/data" ];
};
typetype-dragonfly = {
image = "docker.dragonflydb.io/dragonflydb/dragonfly:v1.39.0";
networks = [ network ];
extraOptions = [
"--ulimit"
"memlock=-1"
];
};
};
# TODO allocate domain
services.nginx.virtualHosts."tt.elmo.mou.fo" = {
useACMEHost = "elmo.mou.fo";
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8082";
proxyWebsockets = true;
};
# Matches client_max_body_size in the frontend image's nginx.conf.
extraConfig = ''
client_max_body_size 2g;
'';
};
}
|