How to install opencloud web apps?

One can install web apps for Nextcloud with the extraApps option like:

  extraApps = {
    inherit (config.services.nextcloud.package.packages.apps) news contacts calendar tasks;
  };
  extraAppsEnable = true;

There doesn’t seem to be an equivalent option for Opencloud, so I assume that one must manually install them from a zip file, as documented here.

However, I am having trouble understanding the instructions. Specifically, the $OC_DATA_DIR environment variable isn’t defined, and I can’t seem to find an existing web/assets/apps path on my system.

I would appreciate any help!

For Collabora online you could have a look at: OpenCloud - Official NixOS Wiki. Haven’t tried it yet though but it is on my todo list.

1 Like

Thanks, but Collabora looks like a different category of thing than the web apps I’m talking about. (Correct me if I’m wrong.)

I’m trying to install the “arcade” app.

I added $OC_DATA_DIR to my configuration:

  services.opencloud = {
    enable = true;
    url = "https://opencloud.example.com";
    environment = {
      PROXY_TLS = "false"; # disable https when behind reverse-proxy
      OC_DATA_DIR = toString /var/lib/opencloud; # <- new
    };
    # ...
  };

I copied the unzipped folder to the directory in the doc:

$ ls -la /var/lib/opencloud/web/assets/apps/arcade
total 20
drwxr-xr-x 4 opencloud opencloud 4096 Apr 21 00:52 .
drwxr-xr-x 3 opencloud opencloud 4096 Apr 21 00:52 ..
drwxr-xr-x 2 opencloud opencloud 4096 Apr 21 00:52 assets
drwxr-xr-x 2 opencloud opencloud 4096 Apr 21 00:52 js
-rw-r--r-- 1 opencloud opencloud   49 Apr 21 00:52 manifest.json

Then I did a nix-rebuild switch, which said starting the following units: opencloud.service, so according to my understanding, I should have seen an “open with arcade” item when right-clicking a rom. Obviously, my understanding is wrong…

The default for that variable appears to be based on the stateDir. I wouldn’t mess with setting it, that could break the service.

The WEB_ASSET_CORE_PATH might also affect things. I think you’ll have to dig a bit into the opencloud docs to see what all the variables the default NixOS service sets do, and reconstruct the intended paths from there (and/or read opencloud source code). Or wait for someone more experienced to figure this out.

Hmm, I was afraid of that. This raises the question: what is the relationship between OC_BASE_DATA_PATH and OC_DATA_DIR?

I get that impression that Opencloud has less mindshare than Nextcloud at the moment, so I might be waiting a while…

Usually *_DIR is the canonical location with read and write access, while *_PATH is a colon separated list of read-only locations, auxiliary to the corresponding *_DIR.

1 Like

As far as I understood OC_BASE_DATA_PATH is the actual environment variable used by the service itself. OC_DATA_DIR refers to the environment variable used by the docker-compose file for mapping any local dir into the container. So if you are not using the compose setup the first should be the correct one. I agree that the docs are quite confusing.

You are absolutely right. Sorry for sending you on the wrong track. As it is still on my todo I did not look into it in detail.

1 Like

Another data point: According to cat /proc/$(pgrep opencloud)/environ, WEB_ASSET_CORE_PATH=/nix/store/7q0rj8rx2hh3a4x0s44znwyrzk3knrmr-opencloud-web-4.2.0and OC_BASE_DATA_PATH=/var/lib/opencloud.

This is very embarrassing, but it turns out that the problem was using a version of the arcade app that was too new for my version of Opencloud. Once I switched from version 2 (for Opencloud 6.0) to version 1 (for Opencloud 3.5, Nixos 25.11 is on 3.7.0), it worked, even without setting any env vars.

Thanks for the help, everyone!

1 Like

Would you mind sharing your final configuration for others to learn from? I definitely would be interested.

1 Like

Good idea. There really isn’t much more to it though.

  services.opencloud = {
    enable = true;
    url = "https://opencloud.example.com";
    environment = {
      PROXY_TLS = "false"; # disable https when behind reverse-proxy
    };
    environmentFile = config.sops.secrets."opencloud-admin-password".path;
  };

  services.nginx = {
    enable = true;
    # ...
    virtualHosts."opencloud.exammple.com" = {
      acmeRoot = null;
      useACMEHost = "example.com";
      forceSSL = true;
      locations."/" = {
        proxyPass = "http://127.0.0.1:${toString config.services.opencloud.port}";
        proxyWebsockets = true;
        extraConfig = ''
          proxy_set_header Host $host;
        '';
      };
    };
    # ...
  };
  sops = {
    defaultSopsFile = ./secrets/default.yaml;
    age.keyFile = "/root/.config/sops/age/keys.txt";
    secrets = {
      "opencloud-admin-password" = {
        owner = "opencloud";
      };
    };
  };

The unzipped app was placed in /var/lib/opencloud/web/assets/apps/ as stated above.

Maybe I’ll ask an LLM to help me make it declarative instead of manually downloading and unzipping and copying and chowning.

Thanks.

I specifically was interested in this part :wink:

I had to shave like a bajillion yaks, but I was able to get a local LLM to make this for me:

opencloud-webapps.nix

{
  lib,
  pkgs,
  config,
  ...
}:

let
  inherit (lib) types;

  webapps = config.services.opencloud.webapps;

  webappSchema = types.submodule {
    options = {
      enable = lib.mkOption {
        type = types.bool;
        default = false;
        description = "Whether to install this web app.";
      };
      name = lib.mkOption {
        type = types.str;
        description = "Name of the web app (used for directory name).";
      };
      url = lib.mkOption {
        type = types.str;
        description = "URL to the web app ZIP release asset.";
      };
      sha256 = lib.mkOption {
        type = types.str;
        description = "SHA256 hash of the web app ZIP file.";
      };
    };
  };

  unpackWebApp =
    webapp:
    let
      zipFile = pkgs.fetchurl {
        inherit (webapp) url sha256;
      };
    in
    pkgs.runCommandLocal "webapp-${webapp.name}-unpacked"
      {
        nativeBuildInputs = [ pkgs.unzip ];
      }
        ''
          unzip -o ${zipFile}
          mkdir -p "$out"
          for dir in */; do
            if [ -d "$dir" ]; then
              cp -r "$dir"/* "$out"/
            fi
          done
        '';

  targetPath = "${config.services.opencloud.stateDir}/web/assets/apps";

  makeInstallScript =
    webapp:
    let
      unpacked = unpackWebApp webapp;
    in
    {
      text = ''
        set -euo pipefail
        SRC="${unpacked}"
        DST="${targetPath}/${webapp.name}"
        TMP_DST=$(mktemp -d -t ${webapp.name}.XXX)

        mkdir -p "$TMP_DST"
        mkdir -p "$(dirname "$DST")"
        cp -a "$SRC/." "$TMP_DST/"
        chown -R ${config.services.opencloud.user}:${config.services.opencloud.group} "$TMP_DST"
        rm -rf "$DST"
        mv "$TMP_DST" "$DST"
      '';
    };
in
{
  options = {
    services.opencloud.webapps = lib.mkOption {
      type = types.listOf webappSchema;
      default = [ ];
      description = "List of web apps to install for OpenCloud.";
    };
  };

  config = lib.mkIf (webapps != [ ]) {
    systemd.tmpfiles.rules = [
      "d ${targetPath} 0755 ${config.services.opencloud.user} ${config.services.opencloud.group} -"
    ];

    system.activationScripts = lib.listToAttrs (
      lib.map (
        webapp:
        if webapp.enable then
          {
            name = "${webapp.name}-install";
            value = makeInstallScript webapp;
          }
        else
          {
            name = "${webapp.name}-install";
            value = null;
          }
      ) webapps
    );
  };
}

configuration.nix

  imports = [
    # ...
    ./opencloud-webapps.nix
  ];
  # ...
  services.opencloud = {
    enable = true;
    url = "https://cloud.example.com";
    environment = {
      PROXY_TLS = "false"; # disable https when behind reverse-proxy
    };
    environmentFile = config.sops.secrets."opencloud-admin-password".path;

    webapps = [{
      enable = true;
      name = "arcade";
      url = "https://github.com/opencloud-eu/web-extensions/releases/download/arcade-v1.0.0/arcade-1.0.0.zip";
       sha256 = "20d522de8260ab0162cfe2354bbef402a2ff8bf77f026454dae4759ae8129c8a";
    }];
  };

I haven’t tested it with any webapps besides arcade yet. It definitely doesn’t support apps that require configuration, like external-sites.

Comments welcome!