[Focalboard] Sets up Focalboard with storage, database and secrets. [OpenProject] Removes the container

This commit is contained in:
2025-04-21 16:42:56 -07:00
parent 089541916a
commit c88845ea2a
5 changed files with 168 additions and 63 deletions

View File

@@ -4,6 +4,8 @@ bookstack_db: ENC[AES256_GCM,data:m8fGgAfmJu1rEaxmTVH4FfBhyiU=,iv:OnBT/6sp9zmcJ1
mariadb_root_password: ENC[AES256_GCM,data:p965ZhFQqqX+Ub1yhgklVYlBH6A=,iv:qC5WwTvZGvlbAkYiv35xHizMYAnP0V0Vw79EkvL32wQ=,tag:gOJQvHeOC9turFKOMQ9DNg==,type:str] mariadb_root_password: ENC[AES256_GCM,data:p965ZhFQqqX+Ub1yhgklVYlBH6A=,iv:qC5WwTvZGvlbAkYiv35xHizMYAnP0V0Vw79EkvL32wQ=,tag:gOJQvHeOC9turFKOMQ9DNg==,type:str]
openproject: openproject:
secret-key-base: ENC[AES256_GCM,data:luTuUtxL/SGx6O10y9cRiAzJHw==,iv:8qVJm+obsHr9eV0h+jdpsreeFGxEM+UFZHHiIUUPs6w=,tag:+zpjhKoIiNNSSYxe1QkQ7Q==,type:str] secret-key-base: ENC[AES256_GCM,data:luTuUtxL/SGx6O10y9cRiAzJHw==,iv:8qVJm+obsHr9eV0h+jdpsreeFGxEM+UFZHHiIUUPs6w=,tag:+zpjhKoIiNNSSYxe1QkQ7Q==,type:str]
focalboard:
database: ENC[AES256_GCM,data:GDxYdkVV+tl3qHxWMMoetmMnLnY=,iv:JujgNPyUEHCmD/yW3UKCTj9GTk9a7EkvUiyFLF4sF8A=,tag:46YZ7AthpiiaX69aN9a3Bg==,type:str]
sops: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
@@ -28,8 +30,8 @@ sops:
by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw
vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q== vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-03T19:04:17Z" lastmodified: "2025-04-21T19:34:54Z"
mac: ENC[AES256_GCM,data:DEUuXrCl3OXJ9NbfLoxHIND5+m7enHNDbuLE2jS8nvZCpKm83YoXwp0RhIFA725wJnBej26HLkovCi7V/4s5NrrfT9sPHGNBMSHB0AAcwu3Dmo6G2PBKvAWZTxXmiIXGx8vSvWNbLrp3vTV8jjTpfbuMvOiuxayKfn6esKI9T2o=,iv:zUfbL753Uvzg6WW4kwI8swmpWHIQ/IpCyYSsLptVDG4=,tag:XZy8jNZYYcqspd6zptH3pQ==,type:str] mac: ENC[AES256_GCM,data:D44YsnrRpYQmJxAjXdap+Ya6iqPrhdEmiaTgUOM53JYmkihIvmMxm3b09xMxucv3B7tvi5vCfcllgij+RZ2RPnQDFg8ZzYQu7AQSG8rgwoh3E9Zijx2gQm59hhvJlca2cB710hUL87Tkdbvz26LZevIf5gP83u2JCkXLzr9O/Ew=,iv:lMthECFXzbao4bVVK9eJgK5ubu1NUg97BI2T9OqlICw=,tag:8t+2kPoqKeSKF8e+x5dtmg==,type:str]
pgp: [] pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.4 version: 3.9.4

View File

@@ -4,12 +4,12 @@
# Additional configuration # Additional configuration
imports = [ imports = [
./containers/bookstack.nix ./containers/bookstack.nix
./containers/focalboard.nix
./containers/gitea.nix ./containers/gitea.nix
./containers/grafana.nix ./containers/grafana.nix
./containers/jobhunt.nix ./containers/jobhunt.nix
./containers/mariadb.nix ./containers/mariadb.nix
./containers/nextcloud.nix ./containers/nextcloud.nix
./containers/openproject.nix
./containers/prometheus.nix ./containers/prometheus.nix
./containers/pocket-id.nix ./containers/pocket-id.nix
./containers/public-homepage.nix ./containers/public-homepage.nix

View File

@@ -0,0 +1,74 @@
{ config, ... }:
let
inherit (import ./lib.nix config) mkContainer mkPostgresContainer terakoda;
userIds = import ./user-ids.nix;
in {
imports = [
(mkPostgresContainer {
name = "focalboard";
directory = "/tank/focalboard/db";
uid = userIds.focalboard.uid;
gid = userIds.focalboard.gid;
passwordSecret = "focalboard/database";
})
];
users.groups.focalboard = {
gid = userIds.focalboard.gid;
};
users.users.focalboard = {
uid = userIds.focalboard.uid;
isSystemUser = true;
description = "System User for Focalboard";
group = "focalboard";
};
sops.secrets = {
"focalboard/database" = {
restartUnits = [ "podman-focalboard.service" "podman-focalboard-postgres.service" ];
mode = "0400";
owner = config.users.users.focalboard.name;
};
};
sops.templates."focalboard-config.json" = {
restartUnits = [ "podman-focalboard.service" ];
owner = config.users.users.focalboard.name;
content = builtins.toJSON {
# Defaults from https://github.com/mattermost-community/focalboard/blob/main/config.json
"serverRoot" = "https://focalboard.terakoda.com";
"port" = 8000;
"dbtype" = "postgres";
"dbconfig" = "postgres://focalboard:${config.sops.placeholder."focalboard/database"}@focalboard-postgres/focalboard?sslmode=disable&connect_timeout=10";
"useSSL" = true;
"prometheus_address" = ":9092";
"session_expire_time" = 2592000;
"session_refresh_time" = 18000;
"postgres_dbconfig" = "dbname=focalboard sslmode=disable";
"webpath" = "./pack";
"filespath" = "./data/files";
"telemetry" = true;
"prometheusaddress" = ":9092";
"enableLocalMode" = true;
"localModeSocketLocation" = "/var/tmp/focalboard_local.socket";
};
};
virtualisation.oci-containers.containers = {
focalboard = mkContainer {
image = "mattermost/focalboard";
hostName = "focalboard";
domain = terakoda;
dependsOn = [ "focalboard-postgres" ];
port = 8000;
user = "${toString userIds.focalboard.uid}:${toString userIds.focalboard.gid}";
volumes = [
"/tank/focalboard/data/files:/opt/focalboard/data/files"
"${config.sops.templates."focalboard-config.json".path}:/opt/focalboard/config.json:ro"
];
};
};
}

View File

@@ -2,6 +2,8 @@ config:
let let
havenisms = "havenisms.com"; havenisms = "havenisms.com";
blazestar = "blazestar.net"; blazestar = "blazestar.net";
terakoda = "terakoda.com";
terakoda_net = "terakoda.net";
hostRule = host: domain: "Host(`${host}.${domain}`)"; hostRule = host: domain: "Host(`${host}.${domain}`)";
hostRuleHavenisms = host: hostRule host havenisms; hostRuleHavenisms = host: hostRule host havenisms;
localNet = "192.168.0.0/16"; localNet = "192.168.0.0/16";
@@ -18,46 +20,42 @@ in
localHostRuleHavenisms localHostRuleHavenisms
havenisms havenisms
blazestar blazestar
; terakoda
terakoda_net;
mkContainer = mkContainer = {
{ image,
image, hostName,
hostName, port,
port, homepageOpts ? {},
homepageOpts, dependsOn ? [],
dependsOn ? [ ], domain ? havenisms,
domain ? havenisms, ports ? [],
ports ? [ ], volumes ? [],
volumes ? [ ], environment ? {},
environment ? { }, environmentFiles ? [],
environmentFiles ? [ ], public ? false,
public ? false, user ? null,
}: }:
let let
routerRule = if public then hostRule hostName domain else localHostRule hostName domain; routerRule = if public then hostRule hostName domain else localHostRule hostName domain;
homepageLabels = if homepageOpts == {} then {} else {
"homepage.group" = "${homepageOpts.group}";
"homepage.name" = "${homepageOpts.name}";
"homepage.icon" = "${homepageOpts.icon}";
"homepage.href" = "https://${hostName}.${domain}";
"homepage.description" = "${homepageOpts.description}";
};
in in
{ {
inherit inherit image dependsOn volumes environment environmentFiles ports user;
image
dependsOn
volumes
environment
environmentFiles
ports
;
hostname = "${hostName}.${domain}"; hostname = "${hostName}.${domain}";
autoStart = true; autoStart = true;
extraOptions = [ labels = {
"-l=traefik.enable=true" "traefik.enable" = "true";
"-l=traefik.http.routers.${hostName}.rule=${routerRule}" "traefik.http.routers.${hostName}.rule" = "${routerRule}";
"-l=traefik.http.services.${hostName}.loadbalancer.server.port=${toString port}" "traefik.http.services.${hostName}.loadbalancer.server.port" = "${toString port}";
"-l=homepage.group=${homepageOpts.group}" } // homepageLabels;
"-l=homepage.name=${homepageOpts.name}"
"-l=homepage.icon=${homepageOpts.icon}"
"-l=homepage.href=https://${hostName}.${domain}"
"-l=homepage.description=${homepageOpts.description}"
];
}; };
# Creates a MariaDB container for a specific app. It should be safe to give # Creates a MariaDB container for a specific app. It should be safe to give
@@ -66,33 +64,60 @@ in
# user. # user.
# #
# Note that this returns a _module_ so that it can be imported and provide many different config values. # Note that this returns a _module_ so that it can be imported and provide many different config values.
mkMariaDbContainer = mkMariaDbContainer = {
{ name,
name, uid,
uid, gid,
gid, passwordSecret,
passwordSecret, directory,
directory, }:
}: { config, ... }:
{ config, ... }: {
{ virtualisation.oci-containers.containers."${name}-mariadb" = {
virtualisation.oci-containers.containers."${name}-mariadb" = { image = "lscr.io/linuxserver/mariadb:latest";
image = "lscr.io/linuxserver/mariadb:latest"; autoStart = true;
autoStart = true; ports = [ "3306:3306" ];
ports = [ "3306:3306" ]; volumes = [
volumes = [ "${directory}:/config"
"${directory}:/config" "${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password"
"${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password" "${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password"
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password" ];
]; environment = {
environment = { PUID = "${toString uid}";
PUID = "${toString uid}"; PGID = "${toString gid}";
PGID = "${toString gid}"; MYSQL_USER = name;
MYSQL_USER = name; MYSQL_DATABASE = name;
MYSQL_DATABASE = name; FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password";
FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password"; FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password";
FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password";
};
}; };
}; };
};
mkPostgresContainer = {
name,
uid,
gid,
passwordSecret,
directory,
containerName ? "${name}-postgres",
databaseName ? name,
username ? name,
}: { config, ... }: {
virtualisation.oci-containers.containers."${containerName}" = {
image = "postgres";
autoStart = true;
volumes = [
# Note that data must be mounted at this location to persist.
# See https://github.com/docker-library/docs/blob/master/postgres/README.md#pgdata
"${directory}:/var/lib/postgresql/data"
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/postgres_password"
];
user = "${toString uid}:${toString gid}";
environment = {
POSTGRES_USER = username;
POSTGRES_DB = databaseName;
POSTGRES_PASSWORD_FILE = "/run/secrets/postgres_password";
};
};
};
} }

View File

@@ -13,4 +13,8 @@
uid = 2005; uid = 2005;
gid = 2005; gid = 2005;
}; };
focalboard = {
uid = 2006;
gid = 2006;
};
} }