Problems setting up Nextcloud

I’m trying to set up Nextcloud. As far as I understand the wiki and manual, I only need a pretty minimal config.

{ config, ... }:
let
  nextcloud-pass = config.age.secrets.nextcloud.path;
in
{
  age.secrets.nextcloud.file = ../secrets/nextcloud.age;

  containers.nextcloud = {
    autoStart = true;
    bindMounts = {
      "${nextcloud-pass}".hostPath = "${nextcloud-pass}";
    };

    config =
      { pkgs, ... }:
      {
        services = {
          nextcloud = {
            enable = true;
            package = pkgs.nextcloud29;
            hostName = "localhost";
            config = {
              adminpassFile = "${nextcloud-pass}";
            };

            database.createLocally = true;
          };

          tailscale = {
            enable = true;
            useRoutingFeatures = "server";
            interfaceName = "userspace-networking";
          };
        };

        system.activationScripts.serveNextcloud.text = "${pkgs.tailscale}/bin/tailscale serve --bg 80";

        system.stateVersion = "24.05";
      };
  };
}

Unfortunately when I go to the address, all I get is the error:
Configuration was not read or initialized correctly, not overwriting /var/lib/nextcloud/config/config.php

What am I missing?

Did you manage to solve this? The config looks fine to me but I have never tried it in a container myself.

woks fine without a container, but can’t get it running in a container either. no idea where the problem could be tbh.

I too tried it without the container and it worked after I changed the permissions for the agenix secret. Unfortunately, when Nextcloud runs in a container, I can’t set the permissions with agenix as the nextcloud user and group don’t exist.

How can I change this declaratively? sudo chown inside the container didn’t work…

chown: changing ownership of '/run/agenix/nextcloud': Read-only file system

so this means running nextcloud in the container should work but this is a agenix specific problem?

Accessing secrets inside a container is discussed here. Nvm, that doesn’t actually fix the issue Nvm, that was actually the solution

I will try it out and report back when I have results

It works now! I had to import agenix inside of the container. This is the configuration:

{ inputs, ... }:
{
  containers.nextcloud = {
    autoStart = true;
    bindMounts = {
      "/etc/ssh/ssh_host_ed25519_key".isReadOnly = true;
    };

    config =
      { pkgs, config, ... }:
      {
        imports = [ inputs.agenix.nixosModules.default ];

        age = {
          identityPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
          secrets."nextcloud" = {
            file = ../secrets/nextcloud.age;
            mode = "770";
            owner = "nextcloud";
            group = "nextcloud";
          };
        };

        services = {
          nextcloud = {
            enable = true;
            package = pkgs.nextcloud29;
            hostName = "nextcloud";
            https = true;
            config = {
              adminpassFile = config.age.secrets.nextcloud.path;
            };
            settings = {
              trusted_domains = [ "nextcloud.***.ts.net" ];
            };

            database.createLocally = true;
          };

          tailscale = {
            enable = true;
            useRoutingFeatures = "server";
            interfaceName = "userspace-networking";
          };
        };

        system.activationScripts.serveNextcloud.text = "${pkgs.tailscale}/bin/tailscale serve --bg 80";

        system.stateVersion = "24.05";
      };
  };
}

I am trying to replicate your config but it’s not working for me. I think the problem is the tailscale serve. Did you do anything extra for it to work?

The solution was to first do tailscale cert but the activation script was still not working so I turned it into a systemd service. I also followed the other approach of getting secrets into the container mentioned in that discussion by also having the nextcloud user outside the container. I’m not sure yet which approach I like better.

Here is my (abridged) confic:

users.users.nextcloud = {
  uid = 777;
  group = "nextcloud";
  isSystemUser = true;
};
users.groups.nextcloud.gid = 777;

sops.secrets = {
  "nextcloud/admin-pass" = {
    owner = "nextcloud";
    group = "nextcloud";
  };
  tailscale-auth-key.restartUnits = [ "tailscaled-autoconnect.service" ];
};

containers.nextcloud = {
  autoStart = true;
  bindMounts = {
    "/run/secrets/nextcloud/admin-pass" = { };
    "/run/secrets/tailscale-auth-key" = { };
    "/data/nextcloud".isReadOnly = false;
  };

  config =
    { pkgs, ... }:
    {
      system.stateVersion = "24.05";

      users.users.nextcloud.uid = 777;
      users.groups.nextcloud.gid = 777;

      services = {
        nextcloud = {
          enable = true;
          package = pkgs.nextcloud29;
          home = "/data/nextcloud";
          hostName = "nextcloud";

          database.createLocally = true;
          config = {
            dbtype = "pgsql";
            adminuser = "admin";
            adminpassFile = "/run/secrets/nextcloud/admin-pass";
          };

          https = true;
          settings = {
            overwriteProtocol = "https";
            trusted_domains = [ "nextcloud.stork-atlas.ts.net" ];
          };
        };

        tailscale = {
          enable = true;
          authKeyFile = "/run/secrets/tailscale-auth-key";
          useRoutingFeatures = "server";
          interfaceName = "userspace-networking";
        };
      };

      systemd.services.tailscaled-serve = {
        after = [
          "tailscaled.service"
          "tailscaled-autoconnect.service"
        ];
        wants = [ "tailscaled.service" ];
        wantedBy = [ "multi-user.target" ];
        serviceConfig.Type = "oneshot";
        script = "${lib.getExe pkgs.tailscale} serve --bg 80";
      };
    };
};

The automatic tailscale cert renewal is still a todo

I went into the container once and did tailscale up and logged in. Maybe I will set this up declaratively in the future but for now it’s fine.

I also ran tailscale serve --bg 80 once, the activation script doesn’t do anything.

I also have another problem: I can only tailscale serve --bg 80 own single service even though they are in separate containers.

I think I need to setup networking bridge interfaces for this to work?

Do I need to change the port like this?

services.nginx.virtualHosts."localhost".listen = [ { addr = "127.0.0.1"; port = 8080; } ];

No, you shouldn’t.

I am now finished with my transition to the container setup. It took me very long but I am very happy with it.

I also found a better method of managing the secrets. I pass the individual root owned secret files into the container and then use systemd tmpfile rules to change the permissions there.

Here’s my full config: nixos-config/hosts/stratus/containers/nextcloud at 8afe40c87d94dfbedd8bfedaecd513323d6a318a · SebastianStork/nixos-config · GitHub

Edit: I also turned the container ephemeral and mount all state.

What shouldn’t I do?

Why is this better? I actually like the other approach, seems easy to read at least.

What shouldn’t I do?

You shouldn’t need to configure anything extra for nginx. At least I didn’t need to, just tailscale serve.

Why is this better? I actually like the other approach, seems easy to read at least.

I didn’t mean your approach, but my old one. I didn’t like the user hackery, tmpfile rules are much more elegant approach.
Passing the ssh key into the container is a fine method but I decided for myself, that I want the container as minimal as possible, so without any sops/age stuff. All that secret decryption stuff should get handled by the host.

The problem is I can’t serve anything else running on port 80 even in another container, for example FreshRSS.

I managed to isolate the container network completely from the host by following this guide. My container now shows up as a completely different host on my router interface. This should fix that issue completely but I am still in the process of setting up a second container to test it.

A short update: The problem with the macvlan method I used is that it only allows for tailscale userspace-networking which prevents reaching other tailscale devices from inside the container. This may not be a deal breaker for everyone but I decided to switch to the bridge networking method described in the wiki which doesn’t have this issue.