feat(ente): move server state to persistent data directory

This commit is contained in:
Philipp 2026-09-08 01:15:15 +02:00
commit 1925039252
Signed by: Philipp
GPG key ID: B27C3DE2FD94AFC3
5 changed files with 296 additions and 1 deletions

View file

@ -38,6 +38,7 @@ let
packageSet = pkgs: {
codeberg-themes = pkgs.callPackage ../packages/codeberg-themes { };
ente-server = pkgs.callPackage ../packages/ente-server { };
ente-web-auth = pkgs.callPackage ../packages/ente-web-auth { };
linkwarden = pkgs.callPackage ../packages/linkwarden { };
redlib = pkgs.callPackage ../packages/redlib { };

View file

@ -12,7 +12,7 @@ let
enteApp = cfg.package.override {
extraBuildEnv = {
NEXT_PUBLIC_ENTE_ENDPOINT = "https://ente.monapona.de";
NEXT_PUBLIC_ENTE_ENDPOINT = cfg.apiEndpoint;
NEXT_TELEMETRY_DISABLED = "1";
};
};
@ -33,6 +33,12 @@ in
default = "ente.stahl.sh";
};
apiEndpoint = mkOption {
description = "The public Ente API endpoint used by the Auth frontend.";
type = types.str;
default = "https://ente.stahl.sh/api";
};
nginx = {
enable = mkEnabledOption "Enable nginx for this service.";
};

View file

@ -0,0 +1,229 @@
{
config,
lib,
pkgs,
namespace,
...
}:
with lib;
with lib.${namespace};
let
cfg = config.${namespace}.services.ente-server;
sopsCfg = config.${namespace}.system.sops;
in
{
options.${namespace}.services.ente-server = {
enable = mkEnableOption "native Ente Auth backend server";
package = mkOption {
description = "The Ente server package to run.";
type = types.package;
default = pkgs.${namespace}.ente-server;
};
domain = mkOption {
description = "Public domain of the Ente Auth frontend and backend.";
type = types.str;
default = "ente.stahl.sh";
};
port = mkOption {
description = "Local TCP port the Ente server listens on.";
type = types.port;
default = 6789;
};
secretsFile = mkOption {
description = "SOPS file containing the Ente server secrets.";
type = types.path;
default = sopsCfg.secretsDir + "/blarm-ente-server.yaml";
};
credentialsFile = mkOption {
description = "Rendered Ente server credentials YAML file.";
type = types.str;
readOnly = true;
default = config.sops.templates."ente-server-credentials.yaml".path;
};
dataDir = mkOption {
description = "Persistent state directory for the Ente server.";
type = types.path;
default = "/data/ente";
};
database = {
name = mkOption {
description = "PostgreSQL database name for Ente.";
type = types.str;
default = "ente";
};
user = mkOption {
description = "PostgreSQL user for Ente. This must match the systemd service user for peer auth.";
type = types.str;
default = "ente";
};
};
nginx = {
enable = mkEnabledOption "Enable nginx reverse proxy for Ente server.";
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.database.name == cfg.database.user;
message = "awesome-flake.services.ente-server.database.name must match database.user because PostgreSQL ensureDBOwnership requires a database with the same name as the owner.";
}
];
${namespace} = {
system.sops = enabled;
services.acme.enable = mkIf cfg.nginx.enable true;
};
sops.secrets = {
"ente-server/key/encryption" = {
format = "yaml";
key = "ente/key/encryption";
sopsFile = cfg.secretsFile;
};
"ente-server/key/hash" = {
format = "yaml";
key = "ente/key/hash";
sopsFile = cfg.secretsFile;
};
"ente-server/jwt/secret" = {
format = "yaml";
key = "ente/jwt/secret";
sopsFile = cfg.secretsFile;
};
};
sops.templates."ente-server-credentials.yaml" = {
owner = cfg.database.user;
group = "ente";
mode = "0400";
restartUnits = [ "ente-server.service" ];
content = ''
# Generated by sops-nix. Do not edit manually.
# For Ente Auth only, no S3 section is required.
http:
use-tls: false
port: ${toString cfg.port}
db:
# lib/pq treats an absolute host path as a PostgreSQL Unix socket directory.
host: /run/postgresql
port: 5432
name: ${cfg.database.name}
user: ${cfg.database.user}
password: ""
sslmode: disable
apps:
accounts: https://${cfg.domain}
key:
encryption: ${config.sops.placeholder."ente-server/key/encryption"}
hash: ${config.sops.placeholder."ente-server/key/hash"}
jwt:
secret: ${config.sops.placeholder."ente-server/jwt/secret"}
'';
};
networking.firewall.allowedTCPPorts = mkIf cfg.nginx.enable [
80
443
];
users.groups.ente = { };
users.users.${cfg.database.user} = {
isSystemUser = true;
group = "ente";
};
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0700 ${cfg.database.user} ente -"
];
services.postgresql = {
enable = true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensureDBOwnership = true;
}
];
};
systemd.services.ente-server = {
description = "Ente Auth backend server";
after = [
"postgresql.service"
"postgresql-setup.service"
];
requires = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
environment = {
ENVIRONMENT = "local";
ENTE_CREDENTIALS_FILE = cfg.credentialsFile;
GIN_MODE = "release";
};
serviceConfig = {
ExecStart = "${cfg.package}/bin/museum";
ExecStartPre = "${pkgs.coreutils}/bin/test -r ${lib.escapeShellArg cfg.credentialsFile}";
User = cfg.database.user;
Group = "ente";
WorkingDirectory = "${cfg.package}/share/ente-server";
Restart = "on-failure";
RestartSec = "10s";
CapabilityBoundingSet = "";
DevicePolicy = "closed";
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
ReadWritePaths = [ cfg.dataDir ];
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
};
services.nginx = mkIf cfg.nginx.enable {
enable = true;
virtualHosts."${cfg.domain}" = {
locations = {
"/api".return = "301 /api/";
"/api/" = {
proxyPass = "http://127.0.0.1:${toString cfg.port}/";
recommendedProxySettings = true;
};
};
};
};
};
}

View file

@ -0,0 +1,38 @@
{
lib,
buildGoModule,
fetchFromGitHub,
}:
buildGoModule (finalAttrs: {
pname = "ente-server";
version = "4.4.25";
src = fetchFromGitHub {
owner = "ente-io";
repo = "ente";
tag = "auth-v${finalAttrs.version}";
hash = "sha256-LmgfqLibMnL9N+5bv2MoTeR0xojqSgtIf9Bv/arjenk=";
};
sourceRoot = "${finalAttrs.src.name}/server";
subPackages = [ "cmd/museum" ];
vendorHash = "sha256-Nbh9fs+43e1iE1ujr9T5vu7K1QscG+jdq5+iad4HkDc=";
env.CGO_ENABLED = 0;
postInstall = ''
install -d "$out/share/ente-server"
cp -r configurations migrations mail-templates web-templates "$out/share/ente-server/"
'';
meta = {
description = "Ente server backend for Ente Auth";
homepage = "https://ente.io/";
changelog = "https://github.com/ente-io/ente/releases";
license = lib.licenses.agpl3Only;
mainProgram = "museum";
platforms = lib.platforms.linux;
};
})

View file

@ -0,0 +1,21 @@
ente:
key:
encryption: ENC[AES256_GCM,data:8Fj9t5pAx+r8kggRvdCCp3IGTvO+0GGu7dNlOyKyRiXQfpuqtJDcl47Tc68=,iv:2Xo2ZHsnxoXJPaaQG8hzvieMSakELwyKjDBUvFcL5mI=,tag:qTNpHIQvvjaKrq7EBXUivw==,type:str]
hash: ENC[AES256_GCM,data:dKYUSzqY9Sn27ZbSLbbYmV4ccMt8mGMuTC+QI85qGRnhV3ZQ3LDRrJdLSr9zBM9xTHOA7QzpHsCKuPY1fLnzyEdScTQnuw+m3YEMOLfDFm5XcrfnrGA0/w==,iv:FDcF1NzzRrmlsIPXvDu0BEEvO76aZ++qczVWzhhtVsM=,tag:RMDdmfHwY9ReKiE50ls9tQ==,type:str]
jwt:
secret: ENC[AES256_GCM,data:qT1fMW/q6XD5tCOM938TkkBMQsx42FEfA9UDP0ahKhRCc2A1E768+rg98Gw=,iv:8dYDVNmOibzDBVaAkaTqTcQfJvBxWO3KqdkISULU7AY=,tag:u+04smg8lzA2MhIV425eZw==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4ZkMvK2hJNVVxOHVhTmlD
aXZSd3dQeklSMXFJWW5BVzlhOXZNa3pZdkFFCnA3WXlXL0RCM0R3aTl6RGM0RURR
M3N5Z1dweWlZT09zMTZOMXZwWHF1ZzgKLS0tIFZVS3BmakNGWThLRXZwQWRBbk05
YUlsR1paWnF3cGVON25raVpMVHFqdjQKOJsEsUDK3bNKviXfRZd0+6c24/HKy4Jp
6qY6LiB8dvQXI96jU7qF2tkc2vvlUC7yoRzRTSjztdTplLv6XBvQFg==
-----END AGE ENCRYPTED FILE-----
recipient: age132m0pg4utk3cjve2lgcjffvz7cevl0fq5krufu9sgud7wu2wgurqk49kgl
lastmodified: "2026-09-03T12:13:44Z"
mac: ENC[AES256_GCM,data:wzmZnYF2f00qvjIk1UzauuX8wRQPxtvR3NJvjx9zcAHb1J05nUvPGH0iDnBOWjR9NGyMyxdpCBq/VaYyOYGi0GuUHs4oFYVuvkRxIceP71cEGtYbK1ghACL+hMMf+QtLZy6KZDVm3LOb8hqy4x+X4BDJqRZUxOWy7pFaV9pg1Ao=,iv:wkNYb01vGDtVCdUjfJ3mXW982eVfmy/VhmknbX+37SI=,tag:UQfqqYHyNyAjrVhXKR+J3g==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3