From 7ed7d95974eaf476a5e00cfb6e88cf1f6c5fdb58 Mon Sep 17 00:00:00 2001 From: oddlama Date: Mon, 13 Apr 2026 22:00:52 +0200 Subject: [PATCH 1/4] nixos/tests/ente: migrate test from minio to garage --- nixos/modules/services/web-apps/ente.md | 64 +++++-------------------- nixos/tests/ente/default.nix | 59 +++++++++++++---------- 2 files changed, 45 insertions(+), 78 deletions(-) diff --git a/nixos/modules/services/web-apps/ente.md b/nixos/modules/services/web-apps/ente.md index 95763301fc3d..cb623db653cd 100644 --- a/nixos/modules/services/web-apps/ente.md +++ b/nixos/modules/services/web-apps/ente.md @@ -6,57 +6,20 @@ end-to-end encrypted platform for photos and videos. ## Quickstart {#module-services-ente-quickstart} To host ente, you need the following things: -- S3 storage server (either external or self-hosted like [minio](https://github.com/minio/minio)) +- An S3-compatible object storage server (either an external provider or a + self-hosted one such as [garage](#module-services-garage)). From your + storage provider you will need: + - the S3 endpoint URL + - a bucket name + - an access key ID and secret access key with read/write access to the bucket - Several subdomains pointing to your server: - accounts.example.com - albums.example.com - api.example.com - cast.example.com - photos.example.com - - s3.example.com -The following example shows how to setup ente with a self-hosted S3 storage via minio. -You can host the minio s3 storage on the same server as ente, but as this isn't -a requirement the example shows the minio and ente setup separately. -We assume that the minio server will be reachable at `https://s3.example.com`. - -```nix -{ - services.minio = { - enable = true; - # ente's config must match this region! - region = "us-east-1"; - # Please use a file, agenix or sops-nix to securely store your root user password! - # MINIO_ROOT_USER=your_root_user - # MINIO_ROOT_PASSWORD=a_randomly_generated_long_password - rootCredentialsFile = "/run/secrets/minio-credentials-full"; - }; - - systemd.services.minio.environment.MINIO_SERVER_URL = "https://s3.example.com"; - - # Proxy for minio - networking.firewall.allowedTCPPorts = [ - 80 - 443 - ]; - services.nginx = { - recommendedProxySettings = true; - virtualHosts."s3.example.com" = { - forceSSL = true; - useACME = true; - locations."/".proxyPass = "http://localhost:9000"; - # determine max file upload size - extraConfig = '' - client_max_body_size 16G; - proxy_buffering off; - proxy_request_buffering off; - ''; - }; - }; -} -``` - -And the configuration for ente: +Once you have an S3 endpoint, bucket and credentials, configure ente as follows: ```nix { @@ -84,11 +47,13 @@ And the configuration for ente: s3 = { use_path_style_urls = true; b2-eu-cen = { + # The S3 endpoint, bucket and credentials from your storage provider endpoint = "https://s3.example.com"; + # Must be us-east-1 as it is required internally by ente region = "us-east-1"; bucket = "ente"; - key._secret = pkgs.writeText "minio_user" "minio_user"; - secret._secret = pkgs.writeText "minio_pw" "minio_pw"; + key._secret = "/run/secrets/s3-access-key-id"; + secret._secret = "/run/secrets/s3-secret-access-key"; }; }; key = { @@ -122,13 +87,6 @@ If you have a mail server or smtp relay, you can optionally configure `services.ente.api.settings.smtp` so ente can send you emails (registration code and possibly other events). This is optional. -After starting the minio server, make sure the bucket exists: - -``` -mc alias set minio https://s3.example.com root_user root_password --api s3v4 -mc mb -p minio/ente -``` - Now ente should be ready to go under `https://photos.example.com`. ## Registering users {#module-services-ente-registering-users} diff --git a/nixos/tests/ente/default.nix b/nixos/tests/ente/default.nix index cdc30bda0fa1..fc9ad93eaacf 100644 --- a/nixos/tests/ente/default.nix +++ b/nixos/tests/ente/default.nix @@ -1,11 +1,7 @@ { lib, pkgs, ... }: let - accessKey = "BKIKJAA5BMMU2RHO6IBB"; - secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12"; - rootCredentialsFile = pkgs.writeText "minio-credentials-full" '' - MINIO_ROOT_USER=${accessKey} - MINIO_ROOT_PASSWORD=${secretKey} - ''; + garageAccessKey = "GKaaaaaaaaaaaaaaaaaaaaaaaa"; + garageSecretKey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; certs = import ./snakeoil-certs.nix; domain = certs.domain; @@ -14,22 +10,29 @@ in name = "ente"; meta.maintainers = [ lib.maintainers.oddlama ]; - nodes.minio = + nodes.garage = { ... }: { - environment.systemPackages = [ pkgs.minio-client ]; - services.minio = { + services.garage = { enable = true; - inherit rootCredentialsFile; + package = pkgs.garage_2; + settings = { + replication_factor = 1; + consistency_mode = "consistent"; + rpc_bind_addr = "[::]:3901"; + rpc_public_addr = "[::1]:3901"; + rpc_secret = "5c1915fa04d0b6739675c61bf5907eb0fe3d9c69850c83820f51b4d25d13868c"; + s3_api = { + s3_region = "us-east-1"; + api_bind_addr = "[::]:3900"; + }; + }; }; - networking.firewall.allowedTCPPorts = [ - 9000 - ]; + networking.firewall.allowedTCPPorts = [ 3900 ]; - systemd.services.minio.environment = { - MINIO_SERVER_URL = "https://s3.${domain}"; - }; + # Garage requires at least 1GiB of free disk space to run. + virtualisation.diskSize = 2 * 1024; }; nodes.ente = @@ -71,7 +74,7 @@ in forceSSL = true; sslCertificate = certs.${domain}.cert; sslCertificateKey = certs.${domain}.key; - locations."/".proxyPass = "http://${nodes.minio.networking.primaryIPAddress}:9000"; + locations."/".proxyPass = "http://${nodes.garage.networking.primaryIPAddress}:3900"; extraConfig = '' client_max_body_size 32M; proxy_buffering off; @@ -103,8 +106,8 @@ in endpoint = "https://s3.${domain}"; region = "us-east-1"; bucket = "ente"; - key._secret = pkgs.writeText "accesskey" accessKey; - secret._secret = pkgs.writeText "secretkey" secretKey; + key._secret = pkgs.writeText "accesskey" garageAccessKey; + secret._secret = pkgs.writeText "secretkey" garageSecretKey; }; }; key = { @@ -118,13 +121,19 @@ in }; testScript = '' - minio.start() - minio.wait_for_unit("minio.service") - minio.wait_for_open_port(9000) + garage.start() + garage.wait_for_unit("garage.service") + garage.wait_for_open_port(3900) - # Create a test bucket on the server - minio.succeed("mc alias set minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4") - minio.succeed("mc mb -p minio/ente") + # Configure the cluster layout + garage_node_id = garage.succeed("garage status | tail -n1 | awk '{ print $1 }'") + garage.succeed(f"garage layout assign -c 1G -z garage {garage_node_id}") + garage.succeed("garage layout apply --version 1") + + # Import the predefined API key and create the bucket + garage.succeed("garage key import ${garageAccessKey} ${garageSecretKey} --yes") + garage.succeed("garage bucket create ente") + garage.succeed("garage bucket allow --read --write ente --key ${garageAccessKey}") # Start ente ente.start() From 3402876555f3887859e12c0556d6dec35f187dce Mon Sep 17 00:00:00 2001 From: oddlama Date: Mon, 13 Apr 2026 22:17:33 +0200 Subject: [PATCH 2/4] ente-web: replace ente.io in new files --- pkgs/by-name/en/ente-web/package.nix | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pkgs/by-name/en/ente-web/package.nix b/pkgs/by-name/en/ente-web/package.nix index 4c467a2d7510..4af881060245 100644 --- a/pkgs/by-name/en/ente-web/package.nix +++ b/pkgs/by-name/en/ente-web/package.nix @@ -8,6 +8,7 @@ nodejs, rustPlatform, rustc, + sd, wasm-bindgen-cli_0_2_108, wasm-pack, yarnConfigHook, @@ -81,16 +82,13 @@ stdenv.mkDerivation (finalAttrs: { packages/wasm/package.json \ --replace-fail "wasm-pack " ${lib.escapeShellArg "${wasm-pack}/bin/wasm-pack "} '' - # Replace hardcoded ente.io urls if desired + # Replace hardcoded links pointing to the public ente instance so that + # users of a self-hosted instance are not accidentally redirected there + lib.optionalString (enteMainUrl != null) '' - substituteInPlace \ - apps/payments/src/services/billing.ts \ - apps/photos/src/pages/shared-albums.tsx \ - --replace-fail "https://ente.io" ${lib.escapeShellArg enteMainUrl} - - substituteInPlace \ - apps/accounts/src/pages/index.tsx \ - --replace-fail "https://web.ente.io" ${lib.escapeShellArg enteMainUrl} + for pattern in "https://web.ente.io" "https://ente.com" "https://ente.io"; do + mapfile -d "" -t files < <(grep -rlFZ -- "$pattern" apps/) + ${lib.getExe sd} -F -- "$pattern" ${lib.escapeShellArg enteMainUrl} "''${files[@]}" + done ''; yarnBuildScript = "build:${enteApp}"; From f244290cdf647436eabcbcbb6cc6d4f2bc44911c Mon Sep 17 00:00:00 2001 From: oddlama Date: Tue, 14 Apr 2026 19:51:23 +0200 Subject: [PATCH 3/4] nixos/ente: include documentation in manual --- nixos/doc/manual/redirects.json | 15 +++++++++++++++ nixos/modules/services/web-apps/ente.md | 2 +- nixos/modules/services/web-apps/ente.nix | 5 ++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index feb535c2142c..5b1a52588c16 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -1544,6 +1544,21 @@ "module-services-emacs-man-pages": [ "index.html#module-services-emacs-man-pages" ], + "module-services-ente": [ + "index.html#module-services-ente" + ], + "module-services-ente-quickstart": [ + "index.html#module-services-ente-quickstart" + ], + "module-services-ente-registering-users": [ + "index.html#module-services-ente-registering-users" + ], + "module-services-ente-increasing-storage-limit": [ + "index.html#module-services-ente-increasing-storage-limit" + ], + "module-services-ente-ios-background-sync": [ + "index.html#module-services-ente-ios-background-sync" + ], "module-services-livebook": [ "index.html#module-services-livebook" ], diff --git a/nixos/modules/services/web-apps/ente.md b/nixos/modules/services/web-apps/ente.md index cb623db653cd..a904461870a9 100644 --- a/nixos/modules/services/web-apps/ente.md +++ b/nixos/modules/services/web-apps/ente.md @@ -115,7 +115,7 @@ available. While adding new plans is possible in theory, it requires some manual database operations which isn't worthwhile. Instead, use `ente-cli` with your admin user to modify the storage limit. -## iOS background sync +## iOS background sync {#module-services-ente-ios-background-sync} On iOS, background sync is achived via a silent notification sent by the server every 30 minutes that allows the phone to sync for about 30 seconds, enough for diff --git a/nixos/modules/services/web-apps/ente.nix b/nixos/modules/services/web-apps/ente.nix index a432996d405b..0eda31d834a6 100644 --- a/nixos/modules/services/web-apps/ente.nix +++ b/nixos/modules/services/web-apps/ente.nix @@ -359,5 +359,8 @@ in }) ]; - meta.maintainers = with lib.maintainers; [ oddlama ]; + meta = { + doc = ./ente.md; + maintainers = with lib.maintainers; [ oddlama ]; + }; } From 98d5ac253018a2a89e3def46656520aa22c3f2d1 Mon Sep 17 00:00:00 2001 From: oddlama Date: Thu, 16 Apr 2026 21:08:54 +0200 Subject: [PATCH 4/4] ente-web: add nixosTests.ente as passthru.tests --- pkgs/by-name/en/ente-web/package.nix | 78 +++++++++++++++------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/pkgs/by-name/en/ente-web/package.nix b/pkgs/by-name/en/ente-web/package.nix index 4af881060245..e6b77b8d8679 100644 --- a/pkgs/by-name/en/ente-web/package.nix +++ b/pkgs/by-name/en/ente-web/package.nix @@ -22,6 +22,7 @@ # can set this parameter to override these occurrences with your own url. Must include the schema. # Example: https://my-ente.example.com enteMainUrl ? null, + nixosTests, }: stdenv.mkDerivation (finalAttrs: { @@ -104,57 +105,60 @@ stdenv.mkDerivation (finalAttrs: { runHook postInstall ''; - passthru.updateScript = writeScript "update-ente-web" '' - #!/usr/bin/env nix-shell - #!nix-shell -i bash -p coreutils nix-update gnugrep gnused curl + passthru = { + tests = { inherit (nixosTests) ente; }; + updateScript = writeScript "update-ente-web" '' + #!/usr/bin/env nix-shell + #!nix-shell -i bash -p coreutils nix-update gnugrep gnused curl - set -eu -o pipefail + set -eu -o pipefail - # Assume the current working directory is Nixpkgs - file_path="./pkgs/by-name/en/ente-web/package.nix" + # Assume the current working directory is Nixpkgs + file_path="./pkgs/by-name/en/ente-web/package.nix" - # Extract version, then update - old_version=$(grep -oP 'version = "\K[^"]+' "$file_path" | head -n1) - if [[ -z "$old_version" ]]; then - echo "Failed to extract old version from $file_path" - exit 1 - fi + # Extract version, then update + old_version=$(grep -oP 'version = "\K[^"]+' "$file_path" | head -n1) + if [[ -z "$old_version" ]]; then + echo "Failed to extract old version from $file_path" + exit 1 + fi - nix-update ente-web --version-regex 'photos-v(.*)' + nix-update ente-web --version-regex 'photos-v(.*)' - new_version=$(grep -oP 'version = "\K[^"]+' "$file_path" | head -n1) - if [[ -z "$new_version" ]]; then - echo "Failed to extract new version from $file_path" - exit 1 - fi + new_version=$(grep -oP 'version = "\K[^"]+' "$file_path" | head -n1) + if [[ -z "$new_version" ]]; then + echo "Failed to extract new version from $file_path" + exit 1 + fi - if [[ "$old_version" == "$new_version" ]]; then - echo "No update" - exit 0 - fi + if [[ "$old_version" == "$new_version" ]]; then + echo "No update" + exit 0 + fi - echo "Updated to version $new_version, checking wasm-bindgen..." + echo "Updated to version $new_version, checking wasm-bindgen..." - # Fetch Cargo.lock from GitHub instead of cloning repository - cargo_lock_url="https://raw.githubusercontent.com/ente-io/ente/photos-v$new_version/web/packages/wasm/Cargo.lock" + # Fetch Cargo.lock from GitHub instead of cloning repository + cargo_lock_url="https://raw.githubusercontent.com/ente-io/ente/photos-v$new_version/web/packages/wasm/Cargo.lock" - wasm_bindgen_version=$(curl -s "$cargo_lock_url" | tr -d '\r' | grep -A1 '^name = "wasm-bindgen"$' | grep -oP 'version = "\K[^"]+' | head -n1) + wasm_bindgen_version=$(curl -s "$cargo_lock_url" | tr -d '\r' | grep -A1 '^name = "wasm-bindgen"$' | grep -oP 'version = "\K[^"]+' | head -n1) - if [[ -z "$wasm_bindgen_version" ]]; then - echo "Failed to find wasm-bindgen version in Cargo.lock from $cargo_lock_url" - exit 1 - fi + if [[ -z "$wasm_bindgen_version" ]]; then + echo "Failed to find wasm-bindgen version in Cargo.lock from $cargo_lock_url" + exit 1 + fi - echo "Found wasm-bindgen version: $wasm_bindgen_version" + echo "Found wasm-bindgen version: $wasm_bindgen_version" - # Construct new attribute name - wasm_bindgen_attr="wasm-bindgen-cli_''${wasm_bindgen_version//./_}" + # Construct new attribute name + wasm_bindgen_attr="wasm-bindgen-cli_''${wasm_bindgen_version//./_}" - # Replace old attribute name in file - sed -i "s/wasm-bindgen-cli_[0-9_]\+/$wasm_bindgen_attr/g" "$file_path" + # Replace old attribute name in file + sed -i "s/wasm-bindgen-cli_[0-9_]\+/$wasm_bindgen_attr/g" "$file_path" - echo "Successfully updated wasm-bindgen-cli to $wasm_bindgen_attr" - ''; + echo "Successfully updated wasm-bindgen-cli to $wasm_bindgen_attr" + ''; + }; meta = { description = "Ente application web frontends";